diff --git a/.github/workflows/badges.yaml b/.github/workflows/badges.yaml index 223c7348631f8f..16edb45c21cf6b 100644 --- a/.github/workflows/badges.yaml +++ b/.github/workflows/badges.yaml @@ -7,12 +7,7 @@ on: env: BASE_IMAGE: openpilot-base DOCKER_REGISTRY: ghcr.io/commaai - - BUILD: | - docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true - docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true - docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base . - RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c + RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/sh -c jobs: badges: @@ -23,23 +18,7 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - - name: Build Docker image - run: eval "$BUILD" - + - uses: ./.github/workflows/setup - name: Push badges run: | ${{ env.RUN }} "scons -j$(nproc) && python selfdrive/ui/translations/create_badges.py" @@ -49,6 +28,6 @@ jobs: git config user.email "badge-researcher@comma.ai" git config user.name "Badge Researcher" - git add translation_badge_*.svg + git add translation_badge.svg git commit -m "Add/Update badges" git push -f origin HEAD diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 7397e30f832971..8529da8e89fb1f 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -17,7 +17,7 @@ env: docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base . - RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c + RUN: docker run --shm-size 1G -v $PWD:/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 /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c BUILD_CL: | docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true @@ -27,11 +27,10 @@ env: UNIT_TEST: coverage run --append -m unittest discover jobs: - # TODO: once actions/cache supports read only mode, use the cache for all jobs build_release: name: build release runs-on: ubuntu-20.04 - timeout-minutes: 50 + timeout-minutes: 30 env: STRIPPED_DIR: /tmp/releasepilot steps: @@ -39,44 +38,29 @@ jobs: with: fetch-depth: 0 submodules: true - - name: Pull LFS - run: git lfs pull + - name: Build devel + run: TARGET_DIR=$STRIPPED_DIR release/build_devel.sh + - uses: ./.github/workflows/setup - name: Check submodules if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot' run: release/check-submodules.sh - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build devel + - name: Build openpilot and run checks + run: | + cd $STRIPPED_DIR + ${{ env.RUN }} "CI=1 python selfdrive/manager/build.py" + - name: Run tests run: | - TARGET_DIR=$STRIPPED_DIR release/build_devel.sh - cp Dockerfile.openpilot_base $STRIPPED_DIR + cd $STRIPPED_DIR + ${{ env.RUN }} "release/check-dirty.sh && \ + python -m unittest discover selfdrive/car" + - name: pre-commit + run: | + cd $GITHUB_WORKSPACE cp .pre-commit-config.yaml $STRIPPED_DIR cp .pylintrc $STRIPPED_DIR cp mypy.ini $STRIPPED_DIR - - name: Build Docker image - run: | - eval "$BUILD" - rm $STRIPPED_DIR/Dockerfile.openpilot_base - - name: Build openpilot and run checks - run: | - cd $STRIPPED_DIR - ${{ env.RUN }} "CI=1 python selfdrive/manager/build.py && \ - pre-commit run --all && \ - rm .pre-commit-config.yaml && \ - rm .pylintrc && \ - rm mypy.ini && \ - release/check-dirty.sh && \ - python -m unittest discover selfdrive/car" + cd $STRIPPED_DIR + ${{ env.RUN }} "pre-commit run --all" build_all: name: build all @@ -86,26 +70,14 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }} + - uses: ./.github/workflows/setup with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }} - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" + save-cache: true - name: Build openpilot with all flags run: ${{ env.RUN }} "scons -j$(nproc) --extras --test && release/check-dirty.sh" - name: Cleanup scons cache run: | - ${{ env.RUN }} "scons -j$(nproc) --extras --test && \ - rm -rf /tmp/scons_cache/* && \ + ${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \ scons -j$(nproc) --extras --test --cache-populate" #build_mac: @@ -175,21 +147,9 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- + - uses: ./.github/workflows/setup - name: Build Docker image run: | - eval "$BUILD" docker pull $DOCKER_REGISTRY/$IMAGE_NAME:latest || true docker build --cache-from $DOCKER_REGISTRY/$IMAGE_NAME:latest -t $DOCKER_REGISTRY/$IMAGE_NAME:latest -f tools/webcam/Dockerfile . - name: Build openpilot @@ -244,30 +204,11 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache dependencies - id: dependency-cache - uses: actions/cache@v2 - with: - path: /tmp/comma_download_cache - key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/test_valgrind_replay.py') }} - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" + - uses: ./.github/workflows/setup - name: Run valgrind run: | ${{ env.RUN }} "scons -j$(nproc) && \ - FILEREADER_CACHE=1 python selfdrive/test/test_valgrind_replay.py" + python selfdrive/test/test_valgrind_replay.py" - name: Print logs if: always() run: cat selfdrive/test/valgrind_logs.txt @@ -277,28 +218,10 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 50 steps: - - name: Get current date - id: date - run: echo "::set-output name=time::$(date +'%s')" - - name: Output timestamp - run: echo $TIMESTAMP - env: - TIMESTAMP: ${{ steps.date.outputs.time }} - uses: actions/checkout@v3 with: submodules: true - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: ${{ github.ref != 'refs/heads/master' || github.repository != 'commaai/openpilot' }} - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}-${{ steps.date.outputs.time }} - restore-keys: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - - name: Build Docker image - run: eval "$BUILD" + - uses: ./.github/workflows/setup - name: Run unit tests run: | ${{ env.RUN }} "export SKIP_LONG_TESTS=1 && \ @@ -339,30 +262,17 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache dependencies + - uses: ./.github/workflows/setup + - name: Cache test routes id: dependency-cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: /tmp/comma_download_cache - key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/test_processes.py') }} - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" + key: proc-replay-${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/ref_commit') }} - name: Run replay run: | ${{ env.RUN }} "scons -j$(nproc) && \ - FILEREADER_CACHE=1 CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ + CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ coverage xml" - name: Print diff if: always() @@ -389,35 +299,16 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Pull LFS - run: git lfs pull - - name: Cache dependencies - id: dependency-cache - uses: actions/cache@v2 - with: - path: /tmp/comma_download_cache - key: ${{ hashFiles('.github/workflows/selfdrive_tests.yaml', 'selfdrive/test/process_replay/model_replay.py') }} - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- + - uses: ./.github/workflows/setup - name: Build Docker image - # Sim docker is needed to get the intel OPENCL drivers + # Sim docker is needed to get the OpenCL drivers run: eval "$BUILD_CL" - name: Run replay run: | ${{ env.RUN_CL }} "scons -j$(nproc) && \ - ONNXCPU=1 FILEREADER_CACHE=1 CI=1 coverage run \ - selfdrive/test/process_replay/model_replay.py -j$(nproc) && \ - coverage xml" + ONNXCPU=1 CI=1 coverage run \ + selfdrive/test/process_replay/model_replay.py -j$(nproc) && \ + coverage xml" test_longitudinal: name: longitudinal @@ -427,20 +318,7 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" + - uses: ./.github/workflows/setup - name: Test longitudinal run: | ${{ env.RUN }} "mkdir -p selfdrive/test/out && \ @@ -469,30 +347,17 @@ jobs: - uses: actions/checkout@v3 with: submodules: true - - name: Cache dependencies + - uses: ./.github/workflows/setup + - name: Cache test routes id: dependency-cache - uses: actions/cache@v2 + uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b with: path: /tmp/comma_download_cache key: car_models-${{ hashFiles('selfdrive/car/tests/test_models.py', 'selfdrive/car/tests/routes.py') }}-${{ matrix.job }} - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" - name: Test car models run: | ${{ env.RUN }} "scons -j$(nproc) --test && \ - FILEREADER_CACHE=1 coverage run -m pytest selfdrive/car/tests/test_models.py && \ + coverage run -m pytest selfdrive/car/tests/test_models.py && \ coverage xml && \ chmod -R 777 /tmp/comma_download_cache" env: @@ -530,20 +395,7 @@ jobs: with: submodules: true ref: ${{ github.event.pull_request.base.ref }} - - name: Cache scons - id: scons-cache - # TODO: Change the version to the released version when https://github.com/actions/cache/pull/489 (or 571) is merged. - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b - env: - CACHE_SKIP_SAVE: true - with: - path: /tmp/scons_cache - key: scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - restore-keys: | - scons-${{ hashFiles('.github/workflows/selfdrive_tests.yaml') }}- - scons- - - name: Build Docker image - run: eval "$BUILD" + - uses: ./.github/workflows/setup - name: Get base car info run: | ${{ env.RUN }} "scons -j$(nproc) && python selfdrive/debug/dump_car_info.py --path /tmp/openpilot_cache/base_car_info" diff --git a/.github/workflows/setup/action.yaml b/.github/workflows/setup/action.yaml new file mode 100644 index 00000000000000..79ec9212354c26 --- /dev/null +++ b/.github/workflows/setup/action.yaml @@ -0,0 +1,45 @@ +name: 'openpilot env setup' + +env: + BASE_IMAGE: openpilot-base + DOCKER_REGISTRY: ghcr.io/commaai + BUILD: | + docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base) || true + docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true + docker build --cache-from $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $DOCKER_REGISTRY/$BASE_IMAGE:latest -t $BASE_IMAGE:latest -f Dockerfile.openpilot_base . + +inputs: + save-cache: + default: false + required: false + +runs: + using: "composite" + steps: + # do this after checkout to ensure our custom LFS config is used to pull from GitLab + - shell: bash + run: git lfs pull + + # build cache + - id: date + shell: bash + run: echo "::set-output name=date::$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d')" + - shell: bash + run: echo "${{ steps.date.outputs.date }}" + - shell: bash + run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV + if: github.ref != 'refs/heads/master' || inputs.save-cache == 'false' + - id: scons-cache + # TODO: change the version to the released version + # when https://github.com/actions/cache/pull/489 (or 571) is merged. + uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b + with: + path: /tmp/scons_cache + key: scons-${{ steps.date.outputs.date }}-${{ github.sha }} + restore-keys: | + scons-${{ steps.date.outputs.date }}- + scons- + + # build our docker image + - shell: bash + run: eval "$BUILD" diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index e93ce2bb39a146..173e2083847264 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -21,46 +21,6 @@ env: GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c jobs: - build_latest_ubuntu: - name: build latest ubuntu - runs-on: ubuntu-20.04 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v3 - with: - submodules: true - - name: Cache pyenv - id: ubuntu-latest-pyenv - uses: actions/cache@v3 - with: - path: | - ~/.pyenv - ~/.local/share/virtualenvs/ - key: ubuntu-latest-python-${{ hashFiles('tools/ubuntu_setup.sh') }}- - - name: Cache scons - id: ubuntu-latest-scons - uses: actions/cache@v3 - with: - path: /tmp/scons_cache - key: ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}- - restore-keys: | - ubuntu-latest-scons-${{ hashFiles('.github/workflows/tools_test.yaml') }}- - ubuntu-latest-scons- - - - name: tools/ubuntu_setup.sh - run: | - source tools/openpilot_env.sh - tools/ubuntu_setup.sh - - name: Build openpilot - run: | - source tools/openpilot_env.sh - pipenv run scons -j$(nproc) --extras --test - - name: Cleanup scons cache - run: | - source tools/openpilot_env.sh - rm -rf /tmp/scons_cache/* - pipenv run scons -j$(nproc) --extras --test --cache-populate - plotjuggler: name: plotjuggler runs-on: ubuntu-20.04 diff --git a/.gitignore b/.gitignore index 07626f4e285ab2..6aee0ed8e0b077 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ selfdrive/modeld/_dmonitoringmodeld /src/ one -/body/ openpilot notebooks xx diff --git a/.gitmodules b/.gitmodules index 1e6110b7a6941e..bc439b451c4583 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,18 @@ [submodule "panda"] - path = panda - url = ../../commaai/panda.git + path = panda + url = ../../commaai/panda.git [submodule "opendbc"] - path = opendbc - url = ../../commaai/opendbc.git + path = opendbc + url = ../../commaai/opendbc.git [submodule "laika_repo"] - path = laika_repo - url = ../../commaai/laika.git + path = laika_repo + url = ../../commaai/laika.git [submodule "cereal"] - path = cereal - url = ../../commaai/cereal.git + path = cereal + url = ../../commaai/cereal.git [submodule "rednose_repo"] - path = rednose_repo - url = ../../commaai/rednose.git + path = rednose_repo + url = ../../commaai/rednose.git +[submodule "body"] + path = body + url = ../../commaai/body.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b8bc1f1b9ce67..e273cd9aeeab91 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: entry: cppcheck language: system types: [c++] - exclude: '^(third_party/)|(pyextra/)|(cereal/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)/|(installer/)' + exclude: '^(third_party/)|(pyextra/)|(cereal/)|(body/)|(rednose/)|(rednose_repo/)|(opendbc/)|(panda/)|(tools/)|(selfdrive/modeld/thneed/debug/)|(selfdrive/modeld/test/)|(selfdrive/camerad/test/)|(installer/)' args: - --error-exitcode=1 - --language=c++ diff --git a/README.md b/README.md index 94837575b456e9..0b6fc20105ba26 100755 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Table of Contents What is openpilot? ------ -[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW) and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md). +[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW), and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models, and model years](docs/CARS.md). In addition, while openpilot is engaged, a camera-based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](docs/INTEGRATION.md) and [limitations](docs/LIMITATIONS.md). @@ -40,9 +40,9 @@ Running on a dedicated device in a car To use openpilot in a car, you need four things * A supported device to run this software: a [comma three](https://comma.ai/shop/products/three). -* This software. The setup procedure of the comma three allows the user to enter a url for custom software. -The url, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another github username can install a fork. -* One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot. +* This software. The setup procedure of the comma three allows the user to enter a URL for custom software. +The URL, openpilot.comma.ai will install the release version of openpilot. To install openpilot master, you can use installer.comma.ai/commaai/master, and replacing commaai with another GitHub username can install a fork. +* One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported but has adaptive cruise control and lane-keeping assist, it's likely able to run openpilot. * A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car. We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup). @@ -52,11 +52,11 @@ Running on PC All of openpilot's services can run as normal on a PC, even without special hardware or a car. To develop or experiment with openpilot you can run openpilot on recorded or simulated data. -With openpilot's tools you can plot logs, replay drives and watch the full-res camera streams. See [the tools README](tools/README.md) for more information. +With openpilot's tools, you can plot logs, replay drives, and watch the full-res camera streams. See [the tools README](tools/README.md) for more information. -You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes, but does require a decent GPU. +You can also run openpilot in simulation [with the CARLA simulator](tools/sim/README.md). This allows openpilot to drive around a virtual car on your Ubuntu machine. The whole setup should only take a few minutes but does require a decent GPU. -A PC running openpilot can also control your vehicle if it is connected to a [a webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and [a harness](https://comma.ai/shop/products/car-harness). +A PC running openpilot can also control your vehicle if it is connected to a [webcam](https://github.com/commaai/openpilot/tree/master/tools/webcam), a [black panda](https://comma.ai/shop/products/panda), and a [harness](https://comma.ai/shop/products/car-harness). Community and Contributing ------ @@ -78,8 +78,8 @@ By default, openpilot uploads the driving data to our servers. You can also acce openpilot is open source software: the user is free to disable data collection if they wish to do so. -openpilot logs the road facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs. -The driver facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded. +openpilot logs the road-facing cameras, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs. +The driver-facing camera is only logged if you explicitly opt-in in settings. The microphone is not recorded. By using openpilot, you agree to [our Privacy Policy](https://comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma for the use of this data. @@ -87,11 +87,11 @@ Safety and Testing ---- * openpilot observes ISO26262 guidelines, see [SAFETY.md](docs/SAFETY.md) for more details. -* openpilot has software in the loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit. +* openpilot has software-in-the-loop [tests](.github/workflows/selfdrive_tests.yaml) that run on every commit. * The code enforcing the safety model lives in panda and is written in C, see [code rigor](https://github.com/commaai/panda#code-rigor) for more details. -* panda has software in the loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety). -* Internally, we have a hardware in the loop Jenkins test suite that builds and unit tests the various processes. -* panda has additional hardware in the loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile). +* panda has software-in-the-loop [safety tests](https://github.com/commaai/panda/tree/master/tests/safety). +* Internally, we have a hardware-in-the-loop Jenkins test suite that builds and unit tests the various processes. +* panda has additional hardware-in-the-loop [tests](https://github.com/commaai/panda/blob/master/Jenkinsfile). * We run the latest openpilot in a testing closet containing 10 comma devices continuously replaying routes. Directory Structure diff --git a/RELEASES.md b/RELEASES.md index e69d7461b7bb14..3ac6c8e41dfe69 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,22 @@ -Version 0.8.16 (2022-XX-XX) +Version 0.8.17 (2022-XX-XX) ======================== + +Version 0.8.16 (2022-08-26) +======================== +* New driving model + * Reduced turn cutting +* Auto-detect right hand drive setting with driver monitoring model +* Improved fan controller for comma three +* New translations + * Japanese thanks to cydia2020! + * Brazilian Portuguese thanks to AlexandreSato! +* Chevrolet Bolt EUV 2022-23 support thanks to JasonJShuler! +* Chevrolet Silverado 1500 2020-21 support thanks to JasonJShuler! +* GMC Sierra 1500 2020-21 support thanks to JasonJShuler! +* Hyundai Ioniq 5 2022 support thanks to sunnyhaibin! * Hyundai Kona Electric 2022 support thanks to sunnyhaibin! +* Hyundai Tucson Hybrid 2022 support thanks to sunnyhaibin! +* Subaru Legacy 2020-22 support thanks to martinl! * Subaru Outback 2020-22 support Version 0.8.15 (2022-07-20) diff --git a/SConstruct b/SConstruct index 940b2367fa4b35..49685b19b20e29 100644 --- a/SConstruct +++ b/SConstruct @@ -393,9 +393,13 @@ if arch != "Darwin": # Build openpilot -SConscript(['cereal/SConscript']) -SConscript(['panda/board/SConscript']) -SConscript(['opendbc/can/SConscript']) +# build submodules +SConscript([ + 'cereal/SConscript', + 'body/board/SConscript', + 'panda/board/SConscript', + 'opendbc/can/SConscript', +]) SConscript(['third_party/SConscript']) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000000..8b66082bf241e6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Suspected vulnerabilities can be reported to both `adeeb@comma.ai` and `security@comma.ai`. diff --git a/body b/body new file mode 160000 index 00000000000000..04aeb30ce0bb14 --- /dev/null +++ b/body @@ -0,0 +1 @@ +Subproject commit 04aeb30ce0bb14759989cd374158233877e1e151 diff --git a/cereal b/cereal index fbd45de6e6bc71..589ef049a7b0ba 160000 --- a/cereal +++ b/cereal @@ -1 +1 @@ -Subproject commit fbd45de6e6bc71b4561eaef65dd86fce952c5d55 +Subproject commit 589ef049a7b0bac31f4c8987c0fc539839fae489 diff --git a/common/params.cc b/common/params.cc index 11b24fe9c120df..f89e7ff002b572 100644 --- a/common/params.cc +++ b/common/params.cc @@ -151,6 +151,7 @@ std::unordered_map keys = { {"RecordFront", PERSISTENT}, {"RecordFrontLock", PERSISTENT}, // for the internal fleet {"ReleaseNotes", PERSISTENT}, + {"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ShouldDoUpdate", CLEAR_ON_MANAGER_START}, {"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"SshEnabled", PERSISTENT}, diff --git a/common/version.h b/common/version.h index bf1c58df1e2920..0a109c1faac060 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "0.8.16" +#define COMMA_VERSION "0.8.17" diff --git a/docs/CARS.md b/docs/CARS.md index 7fe9441a822ac5..78b4d3b168305e 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -2,226 +2,217 @@ # Supported Cars -A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system. +A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. -## How We Rate The Cars +# 205 Supported Cars -### Stop and Go -- [![star](assets/icon-star-full.svg)](##) - openpilot operates down to 0 mph. -- [![star](assets/icon-star-empty.svg)](##) - openpilot operates only above a minimum speed. See your car's manual for the minimum speed. - -### Steer to 0 -- [![star](assets/icon-star-full.svg)](##) - openpilot can control the steering wheel down to 0 mph. -- [![star](assets/icon-star-empty.svg)](##) - No steering control below certain speeds. See your car's manual for the minimum speed. - -### Steering Torque -- [![star](assets/icon-star-full.svg)](##) - Car has enough steering torque to comfortably take most highway turns. -- [![star](assets/icon-star-empty.svg)](##) - Limited ability to make tighter turns. - - -# 199 Supported Cars - -|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque| -|---|---|---|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-19|AcuraWatch Plus|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Acura|RDX 2016-18|AcuraWatch Plus|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Acura|RDX 2019-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Audi|A3 2014-19|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Audi|Q2 2018|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Audi|Q3 2020-21|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Audi|RS3 2018|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Audi|S3 2015-17|ACC + Lane Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Cadillac|Escalade ESV 2016[1](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise Control|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chrysler|Pacifica 2021|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|comma|body|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Genesis|G70 2018-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Genesis|G70 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Genesis|G80 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Genesis|G90 2017-18|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise Control|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Honda|Accord 2018-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Accord Hybrid 2018-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Civic 2016-18|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Civic 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)[2](#footnotes)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Civic 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Honda|Civic Hatchback 2017-21|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Civic Hatchback 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Honda|CR-V 2015-16|Touring Trim|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|CR-V 2017-22|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|CR-V Hybrid 2017-19|Honda Sensing|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|e 2020|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Fit 2018-20|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Freed 2020|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|HR-V 2019-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Insight 2019-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Inspire 2018|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Odyssey 2018-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Passport 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Pilot 2016-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Honda|Ridgeline 2017-22|Honda Sensing|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Elantra Hybrid 2021-22|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Electric 2020|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC) & LFA|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Ioniq Plug-in Hybrid 2020-21|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Kona 2020|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Palisade 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Santa Fe 2019-20|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Santa Fe 2021-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Santa Fe Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Santa Fe Plug-in Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Sonata 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Sonata Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|EV6 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Forte 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|K5 2021-22|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Electric 2019|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Electric 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Electric 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Electric 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Niro Plug-in Hybrid 2018-19|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Optima 2017|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Optima 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Seltos 2021|Smart Cruise Control (SCC)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Sorento 2018|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Sorento 2019|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC) & LKAS|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Kia|Telluride 2020|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|ES 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|ES Hybrid 2017-18|Lexus Safety System+|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|ES Hybrid 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|IS 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|NX 2018-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|NX 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|NX Hybrid 2018-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|NX Hybrid 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|RC 2017-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|RX 2016-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|RX 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|RX Hybrid 2016-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|RX Hybrid 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Lexus|UX Hybrid 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Mazda|CX-5 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Mazda|CX-9 2021-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Nissan|Altima 2019-20|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Nissan|Leaf 2018-22|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Nissan|Rogue 2018-20|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Nissan|X-Trail 2017|ProPILOT Assist|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Ram|1500 2019-22|Adaptive Cruise Control|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|SEAT|Ateca 2018|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|SEAT|Leon 2014-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|Ascent 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|Forester 2019-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|Outback 2020-22|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Subaru|XV 2018-19|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)| -|Subaru|XV 2020-21|EyeSight Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Kamiq 2021[5](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Karoq 2019-21|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Kodiaq 2018-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Octavia 2015, 2018-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Octavia RS 2016|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Scala 2020|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Škoda|Superb 2015-18|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Alphard 2019-20|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Alphard Hybrid 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon 2019-21|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon Hybrid 2019-21|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Avalon Hybrid 2022|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|C-HR 2017-21|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|C-HR Hybrid 2017-19|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Camry 2018-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Camry 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Camry Hybrid 2018-20|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)[4](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Camry Hybrid 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla Cross (Non-US only) 2020-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla Hatchback 2019-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Corolla Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Highlander 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Highlander 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Highlander Hybrid 2017-19|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Highlander Hybrid 2020-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Mirai 2021|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius 2017-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius Prime 2017-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius Prime 2021-22|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Prius v 2017|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 Hybrid 2017-18|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 Hybrid 2019-21|All|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|RAV4 Hybrid 2022|All|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Toyota|Sienna 2018-20|All|[![star](assets/icon-star-half.svg)](##)[3](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Arteon 2018-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Arteon eHybrid 2020-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Arteon R 2020-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Atlas 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Atlas Cross Sport 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|California 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Caravelle 2020[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|CC 2018-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|e-Golf 2014-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf 2015-20[8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf Alltrack 2015-19|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf GTD 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf GTE 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf GTI 2015-21|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf R 2015-19[8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Golf SportsVan 2015-20|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Jetta 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Jetta GLI 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Passat 2015-22[6,7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Passat Alltrack 2015-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Passat GTE 2015-22[7,8](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Polo 2020-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Polo GTI 2020-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|T-Cross 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|T-Roc 2021[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Taos 2022[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Teramont 2018-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Teramont Cross Sport 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Teramont X 2021-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Tiguan 2019-22[7](#footnotes)|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| -|Volkswagen|Touran 2017|Driver Assistance|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)| +|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Harness| +|---|---|---|:---:|:---:|:---:|:---:|:---:| +|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Acura|RDX 2019-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Audi|A3 2014-19|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Audi|Q2 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Audi|Q3 2020-21|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Audi|RS3 2018|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Audi|S3 2015-17|ACC + Lane Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Cadillac|Escalade ESV 2016[1](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| +|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM| +|Chevrolet|Silverado 1500 2020-21|Safety Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM| +|Chevrolet|Volt 2017-18[1](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| +|Chrysler|Pacifica 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Chrysler|Pacifica 2019-20|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|comma|body|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|None| +|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F| +|Genesis|G70 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F| +|Genesis|G80 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Genesis|G90 2017-18|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|GMC|Acadia 2018[1](#footnotes)|Adaptive Cruise Control|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|OBD-II| +|GMC|Sierra 1500 2020-21|Driver Alert Package II|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|GM| +|Honda|Accord 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Accord Hybrid 2018-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Civic 2019-21|All|Stock|0 mph|2 mph[2](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Civic 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B| +|Honda|Civic Hatchback 2017-21|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Civic Hatchback 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Honda Bosch B| +|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|CR-V 2017-22|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|CR-V Hybrid 2017-19|Honda Sensing|Stock|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|e 2020|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Insight 2019-22|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Inspire 2018|All|Stock|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Bosch A| +|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Passport 2019-21|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Honda|Ridgeline 2017-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|Honda Nidec| +|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC) & LKAS|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B| +|Hyundai|Elantra 2021-22|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai K| +|Hyundai|Elantra Hybrid 2021-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai K| +|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC) & LKAS|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai J| +|Hyundai|Ioniq 5 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai Q| +|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Hyundai|Ioniq Electric 2020|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC) & LFA|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Hyundai|Ioniq Plug-in Hybrid 2020-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Hyundai|Kona 2020|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B| +|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G| +|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai O| +|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai I| +|Hyundai|Palisade 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Hyundai|Santa Fe 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai D| +|Hyundai|Santa Fe 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| +|Hyundai|Santa Fe Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| +|Hyundai|Santa Fe Plug-in Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| +|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E| +|Hyundai|Sonata 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A| +|Hyundai|Sonata Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A| +|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| +|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai L| +|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai N| +|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|FCA| +|Kia|Ceed 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E| +|Kia|EV6 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai P| +|Kia|Forte 2018|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B| +|Kia|Forte 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G| +|Kia|K5 2021-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A| +|Kia|Niro Electric 2019|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Kia|Niro Electric 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F| +|Kia|Niro Electric 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Kia|Niro Electric 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai F| +|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Kia|Niro Plug-in Hybrid 2018-19|Smart Cruise Control (SCC) & LKAS|openpilot|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Kia|Optima 2017|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai B| +|Kia|Optima 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai G| +|Kia|Seltos 2021|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai A| +|Kia|Sorento 2018|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Kia|Sorento 2019|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai E| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC) & LKAS|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai C| +|Kia|Telluride 2020|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Hyundai H| +|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|ES Hybrid 2017-18|Lexus Safety System+|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|ES Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|NX 2018-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|NX Hybrid 2018-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|RC 2017-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|RX 2016-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|RX Hybrid 2016-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Mazda|CX-5 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Mazda| +|Mazda|CX-9 2021-22|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|Mazda| +|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan B| +|Nissan|Leaf 2018-22|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A| +|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A| +|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Nissan A| +|Ram|1500 2019-22|Adaptive Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|Ram| +|SEAT|Ateca 2018|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|SEAT|Leon 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A| +|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A| +|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A| +|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru B| +|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru B| +|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|Subaru A| +|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Subaru A| +|Škoda|Kamiq 2021[5](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Karoq 2019-21[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Kodiaq 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Octavia 2015, 2018-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Octavia RS 2016|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Scala 2020|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Škoda|Superb 2015-18|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon 2016|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon 2017-18|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon 2019-21|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon Hybrid 2019-21|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|C-HR 2017-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|C-HR Hybrid 2017-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Camry 2018-20|All|Stock|0 mph[4](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Camry 2021-22|All|openpilot|0 mph[4](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Camry Hybrid 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla 2017-19|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla Cross (Non-US only) 2020-21|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander 2017-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander Hybrid 2017-19|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Highlander Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius 2016|Toyota Safety Sense P|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius 2017-20|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius Prime 2017-20|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Prius v 2017|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 2016|Toyota Safety Sense P|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 2017-18|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 Hybrid 2017-18|All|Stock[3](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Toyota|Sienna 2018-20|All|Stock[3](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|Toyota| +|Volkswagen|Arteon 2018-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Arteon eHybrid 2020-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Arteon R 2020-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Atlas 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Atlas Cross Sport 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|California 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Caravelle 2020[7](#footnotes)|Driver Assistance|Stock|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|CC 2018-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|e-Golf 2014-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf 2015-20[8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf Alltrack 2015-19|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf GTD 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf GTE 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf GTI 2015-21|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf R 2015-19[8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Golf SportsVan 2015-20|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| +|Volkswagen|Jetta 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Jetta GLI 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Passat 2015-22[6,7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Passat Alltrack 2015-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Passat GTE 2015-22[7,8](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Polo 2020-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Polo GTI 2020-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|T-Cross 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|T-Roc 2021[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Taos 2022[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Teramont 2018-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Teramont Cross Sport 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Teramont X 2021-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Tiguan 2019-22[7](#footnotes)|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|J533| +|Volkswagen|Touran 2017|Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|VW| 1Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
@@ -229,8 +220,8 @@ A supported vehicle is one that just works when you install a comma device. Ever 3When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
4openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
5Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
-6Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.
-7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). For the newer design, in the interim, choose "VW J533 Development" from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.
+6Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
+7Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black (older design) or light brown (newer design). In the interim, if your car has a J533 connector CAN gateway inside the dashboard, choose "VW J533 Development" from the vehicle drop-down for a suitable harness. (Some newer models are also observed to not have a J533 connector.)
8Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.)
## Community Maintained Cars @@ -239,13 +230,13 @@ Although they're not upstream, the community has openpilot running on other make # Don't see your car here? **openpilot can support many more cars than it currently does.** There are a few reasons your car may not be supported. -If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. We don't have a roadmap for car support, and in fact, most car support comes from users like you! +If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car support comes from users like you! ### Which cars are able to be supported? -openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering) and [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control), then it almost certainly has these interfaces. These interfaces generally started shipping on cars around 2016. +openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control. -If your car has the following packages or features, then it's a good candidate for support. If it does not, then it's unlikely able to be supported. +If your car has the following packages or features, then it's a good candidate for support. | Make | Required Package/Features | | ---- | ------------------------- | @@ -263,8 +254,9 @@ All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wi ### Toyota Security -Specific new Toyota models are shipping with a new message authentication method that openpilot does not yet support. -So far, this list includes: +openpilot does not yet support these Toyota models due to a new message authentication method. +[Vote](https://comma.ai/shop/products/vote) if you'd like to see openpilot support on these models. + * Toyota RAV4 Prime 2021+ * Toyota Sienna 2021+ * Toyota Venza 2021+ @@ -273,4 +265,4 @@ So far, this list includes: * Toyota Corolla Cross 2022+ (only US model) * Lexus NX 2022+ * Toyota bZ4x 2023+ -* Subaru Solterra 2023+ \ No newline at end of file +* Subaru Solterra 2023+ diff --git a/opendbc b/opendbc index 48856851716219..b913296c912344 160000 --- a/opendbc +++ b/opendbc @@ -1 +1 @@ -Subproject commit 488568517162194fbb2aa45d3dba4c6af663a883 +Subproject commit b913296c9123441b2b271c00239929ed388169b5 diff --git a/panda b/panda index 89989abca5def3..9d6496ece8465d 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 89989abca5def32c73b8c9e66da804acb12be357 +Subproject commit 9d6496ece8465dfe30997b31dfb352e1e51dde6c diff --git a/release/files_common b/release/files_common index 0980f7929dfda9..663d6d35529551 100644 --- a/release/files_common +++ b/release/files_common @@ -434,6 +434,19 @@ rednose/.gitignore rednose/** laika/** +body/.gitignore +body/board/SConscript +body/board/*.h +body/board/*.c +body/board/*.s +body/board/*.ld +body/board/inc/** +body/board/obj/ +body/board/bldc/** +body/board/drivers/** +body/certs/** +body/crypto/** + cereal/.gitignore cereal/__init__.py cereal/car.capnp @@ -491,6 +504,7 @@ opendbc/can/parser_pyx.pyx opendbc/comma_body.dbc +opendbc/chrysler_ram_hd_generated.dbc opendbc/chrysler_ram_dt_generated.dbc opendbc/chrysler_pacifica_2017_hybrid_generated.dbc opendbc/chrysler_pacifica_2017_hybrid_private_fusion.dbc @@ -499,6 +513,8 @@ opendbc/gm_global_a_powertrain_generated.dbc opendbc/gm_global_a_object.dbc opendbc/gm_global_a_chassis.dbc +opendbc/FORD_CADS.dbc +opendbc/ford_fusion_2018_adas.dbc opendbc/ford_lincoln_base_pt.dbc opendbc/honda_accord_2018_can_generated.dbc @@ -518,7 +534,7 @@ opendbc/honda_insight_ex_2019_can_generated.dbc opendbc/acura_ilx_2016_nidec.dbc opendbc/honda_civic_ex_2022_can_generated.dbc -opendbc/kia_ev6.dbc +opendbc/hyundai_canfd.dbc opendbc/hyundai_kia_generic.dbc opendbc/hyundai_kia_mando_front_radar.dbc @@ -538,6 +554,7 @@ opendbc/toyota_nodsu_pt_generated.dbc opendbc/toyota_adas.dbc opendbc/toyota_tss2_adas.dbc +opendbc/vw_golf_mk4.dbc opendbc/vw_mqb_2010.dbc opendbc/tesla_can.dbc diff --git a/release/files_tici b/release/files_tici index f8c0a279593806..8e3249cdfd55fb 100644 --- a/release/files_tici +++ b/release/files_tici @@ -11,5 +11,7 @@ system/camerad/cameras/camera_qcom2.cc system/camerad/cameras/camera_qcom2.h system/camerad/cameras/real_debayer.cl +selfdrive/sensord/rawgps/* + selfdrive/ui/qt/spinner_larch64 selfdrive/ui/qt/text_larch64 diff --git a/selfdrive/boardd/boardd.cc b/selfdrive/boardd/boardd.cc index 240935175bf4bd..23bf8f292989e8 100644 --- a/selfdrive/boardd/boardd.cc +++ b/selfdrive/boardd/boardd.cc @@ -197,7 +197,7 @@ Panda *usb_connect(std::string serial="", uint32_t index=0) { if (getenv("BOARDD_LOOPBACK")) { panda->set_loopback(true); } - panda->enable_deepsleep(); + //panda->enable_deepsleep(); // power on charging, only the first time. Panda can also change mode and it causes a brief disconneciton #ifndef __x86_64__ @@ -372,6 +372,7 @@ std::optional send_panda_states(PubMaster *pm, const std::vector ps.setAlternativeExperience(health.alternative_experience_pkt); ps.setHarnessStatus(cereal::PandaState::HarnessStatus(health.car_harness_status_pkt)); ps.setInterruptLoad(health.interrupt_load); + ps.setFanPower(health.fan_power); // Convert faults bitset to capnp list std::bitset fault_bits(health.faults_pkt); diff --git a/selfdrive/boardd/tests/boardd_old.py b/selfdrive/boardd/tests/boardd_old.py deleted file mode 100755 index fad29f6f34ae37..00000000000000 --- a/selfdrive/boardd/tests/boardd_old.py +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python3 -# pylint: skip-file - -# This file is not used by openpilot. Only boardd.cc is used. -# The python version is slower, but has more options for development. - -# TODO: merge the extra functionalities of this file (like MOCK) in boardd.c and -# delete this python version of boardd - -import os -import struct -import time - -import cereal.messaging as messaging -from common.realtime import Ratekeeper -from system.swaglog import cloudlog -from selfdrive.boardd.boardd import can_capnp_to_can_list -from cereal import car - -SafetyModel = car.CarParams.SafetyModel - -# USB is optional -try: - import usb1 - from usb1 import USBErrorIO, USBErrorOverflow # pylint: disable=no-name-in-module -except Exception: - pass - -# *** serialization functions *** -def can_list_to_can_capnp(can_msgs, msgtype='can'): - dat = messaging.new_message(msgtype, len(can_msgs)) - for i, can_msg in enumerate(can_msgs): - if msgtype == 'sendcan': - cc = dat.sendcan[i] - else: - cc = dat.can[i] - cc.address = can_msg[0] - cc.busTime = can_msg[1] - cc.dat = bytes(can_msg[2]) - cc.src = can_msg[3] - return dat - - -# *** can driver *** -def can_health(): - while 1: - try: - dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x16) - break - except (USBErrorIO, USBErrorOverflow): - cloudlog.exception("CAN: BAD HEALTH, RETRYING") - v, i = struct.unpack("II", dat[0:8]) - ign_line, ign_can = struct.unpack("BB", dat[20:22]) - return {"voltage": v, "current": i, "ignition_line": bool(ign_line), "ignition_can": bool(ign_can)} - -def __parse_can_buffer(dat): - ret = [] - for j in range(0, len(dat), 0x10): - ddat = dat[j:j+0x10] - f1, f2 = struct.unpack("II", ddat[0:8]) - ret.append((f1 >> 21, f2 >> 16, ddat[8:8 + (f2 & 0xF)], (f2 >> 4) & 0xFF)) - return ret - -def can_send_many(arr): - snds = [] - for addr, _, dat, alt in arr: - if addr < 0x800: # only support 11 bit addr - snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat - snd = snd.ljust(0x10, b'\x00') - snds.append(snd) - while 1: - try: - handle.bulkWrite(3, b''.join(snds)) - break - except (USBErrorIO, USBErrorOverflow): - cloudlog.exception("CAN: BAD SEND MANY, RETRYING") - -def can_recv(): - dat = b"" - while 1: - try: - dat = handle.bulkRead(1, 0x10*256) - break - except (USBErrorIO, USBErrorOverflow): - cloudlog.exception("CAN: BAD RECV, RETRYING") - return __parse_can_buffer(dat) - -def can_init(): - global handle, context - handle = None - cloudlog.info("attempting can init") - - context = usb1.USBContext() - #context.setDebug(9) - - for device in context.getDeviceList(skip_on_error=True): - if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc: - handle = device.open() - handle.claimInterface(0) - handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'') - - if handle is None: - cloudlog.warning("CAN NOT FOUND") - exit(-1) - - cloudlog.info("got handle") - cloudlog.info("can init done") - -def boardd_mock_loop(): - can_init() - handle.controlWrite(0x40, 0xdc, SafetyModel.allOutput, 0, b'') - - logcan = messaging.sub_sock('can') - sendcan = messaging.pub_sock('sendcan') - - while 1: - tsc = messaging.drain_sock(logcan, wait_for_one=True) - snds = map(lambda x: can_capnp_to_can_list(x.can), tsc) - snd = [] - for s in snds: - snd += s - snd = list(filter(lambda x: x[-1] <= 2, snd)) - snd_0 = len(list(filter(lambda x: x[-1] == 0, snd))) - snd_1 = len(list(filter(lambda x: x[-1] == 1, snd))) - snd_2 = len(list(filter(lambda x: x[-1] == 2, snd))) - can_send_many(snd) - - # recv @ 100hz - can_msgs = can_recv() - got_0 = len(list(filter(lambda x: x[-1] == 0+0x80, can_msgs))) - got_1 = len(list(filter(lambda x: x[-1] == 1+0x80, can_msgs))) - got_2 = len(list(filter(lambda x: x[-1] == 2+0x80, can_msgs))) - print("sent %3d (%3d/%3d/%3d) got %3d (%3d/%3d/%3d)" % - (len(snd), snd_0, snd_1, snd_2, len(can_msgs), got_0, got_1, got_2)) - m = can_list_to_can_capnp(can_msgs, msgtype='sendcan') - sendcan.send(m.to_bytes()) - -def boardd_test_loop(): - can_init() - cnt = 0 - while 1: - can_send_many([[0xbb, 0, "\xaa\xaa\xaa\xaa", 0], [0xaa, 0, "\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt), 1]]) - #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]]) - #can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]]) - # recv @ 100hz - can_msgs = can_recv() - print("got %d" % (len(can_msgs))) - time.sleep(0.01) - cnt += 1 - -# *** main loop *** -def boardd_loop(rate=100): - rk = Ratekeeper(rate) - - can_init() - - # *** publishes can and health - logcan = messaging.pub_sock('can') - health_sock = messaging.pub_sock('pandaStates') - - # *** subscribes to can send - sendcan = messaging.sub_sock('sendcan') - - # drain sendcan to delete any stale messages from previous runs - messaging.drain_sock(sendcan) - - while 1: - # health packet @ 2hz - if (rk.frame % (rate // 2)) == 0: - health = can_health() - msg = messaging.new_message('pandaStates', 1) - - # store the health to be logged - msg.pandaState[0].voltage = health['voltage'] - msg.pandaState[0].current = health['current'] - msg.pandaState[0].ignitionLine = health['ignition_line'] - msg.pandaState[0].ignitionCan = health['ignition_can'] - msg.pandaState[0].controlsAllowed = True - - health_sock.send(msg.to_bytes()) - - # recv @ 100hz - can_msgs = can_recv() - - # publish to logger - # TODO: refactor for speed - if len(can_msgs) > 0: - dat = can_list_to_can_capnp(can_msgs).to_bytes() - logcan.send(dat) - - # send can if we have a packet - tsc = messaging.recv_sock(sendcan) - if tsc is not None: - can_send_many(can_capnp_to_can_list(tsc.sendcan)) - - rk.keep_time() - -# *** main loop *** -def boardd_proxy_loop(rate=100, address="192.168.2.251"): - rk = Ratekeeper(rate) - - can_init() - - # *** subscribes can - logcan = messaging.sub_sock('can', addr=address) - # *** publishes to can send - sendcan = messaging.pub_sock('sendcan') - - # drain sendcan to delete any stale messages from previous runs - messaging.drain_sock(sendcan) - - while 1: - # recv @ 100hz - can_msgs = can_recv() - #for m in can_msgs: - # print("R: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex"))) - - # publish to logger - # TODO: refactor for speed - if len(can_msgs) > 0: - dat = can_list_to_can_capnp(can_msgs, "sendcan") - sendcan.send(dat) - - # send can if we have a packet - tsc = messaging.recv_sock(logcan) - if tsc is not None: - cl = can_capnp_to_can_list(tsc.can) - #for m in cl: - # print("S: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex"))) - can_send_many(cl) - - rk.keep_time() - -def main(): - if os.getenv("MOCK") is not None: - boardd_mock_loop() - elif os.getenv("PROXY") is not None: - boardd_proxy_loop() - elif os.getenv("BOARDTEST") is not None: - boardd_test_loop() - else: - boardd_loop() - -if __name__ == "__main__": - main() diff --git a/selfdrive/boardd/tests/replay_many.py b/selfdrive/boardd/tests/replay_many.py deleted file mode 100755 index 78aef07497ad14..00000000000000 --- a/selfdrive/boardd/tests/replay_many.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys -import time -import signal -import traceback -import usb1 -from panda import Panda, PandaDFU -from multiprocessing import Pool - -jungle = "JUNGLE" in os.environ -if jungle: - from panda_jungle import PandaJungle # pylint: disable=import-error - -import cereal.messaging as messaging -from selfdrive.boardd.boardd import can_capnp_to_can_list - -def initializer(): - """Ignore CTRL+C in the worker process. - source: https://stackoverflow.com/a/44869451 """ - signal.signal(signal.SIGINT, signal.SIG_IGN) - -def send_thread(sender_serial): - while True: - try: - if jungle: - sender = PandaJungle(sender_serial) - else: - sender = Panda(sender_serial) - sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT) - - sender.set_can_loopback(False) - can_sock = messaging.sub_sock('can') - - while True: - tsc = messaging.recv_one(can_sock) - snd = can_capnp_to_can_list(tsc.can) - snd = list(filter(lambda x: x[-1] <= 2, snd)) - - try: - sender.can_send_many(snd) - except usb1.USBErrorTimeout: - pass - - # Drain panda message buffer - sender.can_recv() - except Exception: - traceback.print_exc() - time.sleep(1) - -if __name__ == "__main__": - if jungle: - serials = PandaJungle.list() - else: - serials = Panda.list() - num_senders = len(serials) - - if num_senders == 0: - print("No senders found. Exiting") - sys.exit(1) - else: - print("%d senders found. Starting broadcast" % num_senders) - - if "FLASH" in os.environ: - for s in PandaDFU.list(): - PandaDFU(s).recover() - - time.sleep(1) - for s in serials: - Panda(s).recover() - Panda(s).flash() - - pool = Pool(num_senders, initializer=initializer) - pool.map_async(send_thread, serials) - - while True: - try: - time.sleep(10) - except KeyboardInterrupt: - pool.terminate() - pool.join() - raise - diff --git a/selfdrive/boardd/tests/test_boardd_api.py b/selfdrive/boardd/tests/test_boardd_api.py deleted file mode 100644 index f2bcd57a6bf27d..00000000000000 --- a/selfdrive/boardd/tests/test_boardd_api.py +++ /dev/null @@ -1,77 +0,0 @@ -import random -import numpy as np - -import selfdrive.boardd.tests.boardd_old as boardd_old -import selfdrive.boardd.boardd as boardd - -from common.realtime import sec_since_boot -from cereal import log -import unittest - - -def generate_random_can_data_list(): - can_list = [] - cnt = random.randint(1, 64) - for _ in range(cnt): - can_data = np.random.bytes(random.randint(1, 8)) - can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)]) - return can_list, cnt - - -class TestBoarddApiMethods(unittest.TestCase): - def test_correctness(self): - for i in range(1000): - can_list, _ = generate_random_can_data_list() - - # Sendcan - # Old API - m_old = boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes() - # new API - m = boardd.can_list_to_can_capnp(can_list, 'sendcan') - - ev_old = log.Event.from_bytes(m_old) - ev = log.Event.from_bytes(m) - - self.assertEqual(ev_old.which(), ev.which()) - self.assertEqual(len(ev.sendcan), len(ev_old.sendcan)) - for i in range(len(ev.sendcan)): - attrs = ['address', 'busTime', 'dat', 'src'] - for attr in attrs: - self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old')) - - # Can - m_old = boardd_old.can_list_to_can_capnp(can_list, 'can').to_bytes() - # new API - m = boardd.can_list_to_can_capnp(can_list, 'can') - - ev_old = log.Event.from_bytes(m_old) - ev = log.Event.from_bytes(m) - self.assertEqual(ev_old.which(), ev.which()) - self.assertEqual(len(ev.can), len(ev_old.can)) - for i in range(len(ev.can)): - attrs = ['address', 'busTime', 'dat', 'src'] - for attr in attrs: - self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old')) - - def test_performance(self): - can_list, _ = generate_random_can_data_list() - recursions = 1000 - - n1 = sec_since_boot() - for _ in range(recursions): - boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes() - n2 = sec_since_boot() - elapsed_old = n2 - n1 - - # print('Old API, elapsed time: {} secs'.format(elapsed_old)) - n1 = sec_since_boot() - for _ in range(recursions): - boardd.can_list_to_can_capnp(can_list) - n2 = sec_since_boot() - elapsed_new = n2 - n1 - # print('New API, elapsed time: {} secs'.format(elapsed_new)) - self.assertTrue(elapsed_new < elapsed_old / 2) - - -if __name__ == '__main__': - unittest.main() diff --git a/selfdrive/car/CARS_template.md b/selfdrive/car/CARS_template.md index e448c19d353f81..2fce0f9036176e 100644 --- a/selfdrive/car/CARS_template.md +++ b/selfdrive/car/CARS_template.md @@ -5,24 +5,12 @@ # Supported Cars -A supported vehicle is one that just works when you install a comma device. Every car performs differently with openpilot, but all supported cars should provide a better experience than any stock system. - -## How We Rate The Cars - -{% for star_row in STAR_DESCRIPTIONS.values() %} -{% for name, stars in star_row.items() %} -### {{name}} -{% for star, description in stars %} -- {{star_icon.format(star)}} - {{description}} -{% endfor %} - -{% endfor %} -{% endfor %} +A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. # {{all_car_info | length}} Supported Cars |{{Column | map(attribute='value') | join('|')}}| -|---|---|---|:---:|:---:|:---:|:---:| +|---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%} {% for car_info in all_car_info %} |{% for column in Column %}{{car_info.get_column(column, star_icon, footnote_tag)}}|{% endfor %} @@ -39,13 +27,13 @@ Although they're not upstream, the community has openpilot running on other make # Don't see your car here? **openpilot can support many more cars than it currently does.** There are a few reasons your car may not be supported. -If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. We don't have a roadmap for car support, and in fact, most car support comes from users like you! +If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car support comes from users like you! ### Which cars are able to be supported? -openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering) and [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control), then it almost certainly has these interfaces. These interfaces generally started shipping on cars around 2016. +openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control. -If your car has the following packages or features, then it's a good candidate for support. If it does not, then it's unlikely able to be supported. +If your car has the following packages or features, then it's a good candidate for support. | Make | Required Package/Features | | ---- | ------------------------- | @@ -63,8 +51,9 @@ All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wi ### Toyota Security -Specific new Toyota models are shipping with a new message authentication method that openpilot does not yet support. -So far, this list includes: +openpilot does not yet support these Toyota models due to a new message authentication method. +[Vote](https://comma.ai/shop/products/vote) if you'd like to see openpilot support on these models. + * Toyota RAV4 Prime 2021+ * Toyota Sienna 2021+ * Toyota Venza 2021+ @@ -74,3 +63,4 @@ So far, this list includes: * Lexus NX 2022+ * Toyota bZ4x 2023+ * Subaru Solterra 2023+ + diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index c77e40daaf28bb..f2d198338d81f4 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -38,7 +38,7 @@ def create_button_enable_events(buttonEvents: capnp.lib.capnp._DynamicListBuilde def gen_empty_fingerprint(): - return {i: {} for i in range(0, 4)} + return {i: {} for i in range(0, 8)} # FIXME: hardcoding honda civic 2016 touring params so they can be used to diff --git a/selfdrive/car/body/values.py b/selfdrive/car/body/values.py index 0caf93b619952a..61b72d5ade21b6 100644 --- a/selfdrive/car/body/values.py +++ b/selfdrive/car/body/values.py @@ -2,7 +2,7 @@ from cereal import car from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness +from selfdrive.car.docs_definitions import CarInfo Ecu = car.CarParams.Ecu SPEED_FROM_RPM = 0.008587 @@ -18,7 +18,7 @@ class CAR: BODY = "COMMA BODY" CAR_INFO: Dict[str, CarInfo] = { - CAR.BODY: CarInfo("comma body", package="All", harness=Harness.none), + CAR.BODY: CarInfo("comma body", package="All"), } FW_VERSIONS = { diff --git a/selfdrive/car/chrysler/carcontroller.py b/selfdrive/car/chrysler/carcontroller.py index 22abf1b677d5fa..5a2d90c64c2410 100644 --- a/selfdrive/car/chrysler/carcontroller.py +++ b/selfdrive/car/chrysler/carcontroller.py @@ -22,23 +22,8 @@ def __init__(self, dbc_name, CP, VM): def update(self, CC, CS): can_sends = [] - # TODO: can we make this more sane? why is it different for all the cars? - lkas_control_bit = self.lkas_control_bit_prev - if CS.out.vEgo > self.CP.minSteerSpeed: - lkas_control_bit = True - elif self.CP.carFingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): - if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0): - lkas_control_bit = False - elif self.CP.carFingerprint in RAM_CARS: - if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5): - lkas_control_bit = False - - # EPS faults if LKAS re-enables too quickly - lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200) lkas_active = CC.latActive and self.lkas_control_bit_prev - # *** control msgs *** - # cruise buttons if (self.frame - self.last_button_frame)*DT_CTRL > 0.05: das_bus = 2 if self.CP.carFingerprint in RAM_CARS else 0 @@ -61,19 +46,35 @@ def update(self, CC, CS): # steering if self.frame % 2 == 0: + + # TODO: can we make this more sane? why is it different for all the cars? + lkas_control_bit = self.lkas_control_bit_prev + if CS.out.vEgo > self.CP.minSteerSpeed: + lkas_control_bit = True + elif self.CP.carFingerprint in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): + if CS.out.vEgo < (self.CP.minSteerSpeed - 3.0): + lkas_control_bit = False + elif self.CP.carFingerprint in RAM_CARS: + if CS.out.vEgo < (self.CP.minSteerSpeed - 0.5): + lkas_control_bit = False + + # EPS faults if LKAS re-enables too quickly + lkas_control_bit = lkas_control_bit and (self.frame - self.last_lkas_falling_edge > 200) + + if not lkas_control_bit and self.lkas_control_bit_prev: + self.last_lkas_falling_edge = self.frame + self.lkas_control_bit_prev = lkas_control_bit + # steer torque new_steer = int(round(CC.actuators.steer * self.params.STEER_MAX)) apply_steer = apply_toyota_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorqueEps, self.params) - if not lkas_active: + if not lkas_active or not lkas_control_bit: apply_steer = 0 self.apply_steer_last = apply_steer can_sends.append(create_lkas_command(self.packer, self.CP, int(apply_steer), lkas_control_bit)) self.frame += 1 - if not lkas_control_bit and self.lkas_control_bit_prev: - self.last_lkas_falling_edge = self.frame - self.lkas_control_bit_prev = lkas_control_bit new_actuators = CC.actuators.copy() new_actuators.steer = self.apply_steer_last / self.params.STEER_MAX diff --git a/selfdrive/car/chrysler/interface.py b/selfdrive/car/chrysler/interface.py index 3c2bc3131a4b3c..9bb22b607ce87a 100755 --- a/selfdrive/car/chrysler/interface.py +++ b/selfdrive/car/chrysler/interface.py @@ -2,7 +2,7 @@ from cereal import car from panda import Panda from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config -from selfdrive.car.chrysler.values import CAR, DBC, RAM_CARS +from selfdrive.car.chrysler.values import CAR, DBC, RAM_HD, RAM_DT from selfdrive.car.interfaces import CarInterfaceBase @@ -12,14 +12,20 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret.carName = "chrysler" - ret.radarOffCan = DBC[candidate]['radar'] is None + ret.dashcamOnly = candidate in RAM_HD - param = Panda.FLAG_CHRYSLER_RAM_DT if candidate in RAM_CARS else None - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler, param)] + ret.radarOffCan = DBC[candidate]['radar'] is None ret.steerActuatorDelay = 0.1 ret.steerLimitTimer = 0.4 + # safety config + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)] + if candidate in RAM_HD: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_HD + elif candidate in RAM_DT: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_CHRYSLER_RAM_DT + ret.minSteerSpeed = 3.8 # m/s if candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): # TODO: allow 2019 cars to steer down to 13 m/s if already engaged. @@ -47,18 +53,24 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa # Ram elif candidate == CAR.RAM_1500: ret.steerActuatorDelay = 0.2 - ret.wheelbase = 3.88 ret.steerRatio = 16.3 ret.mass = 2493. + STD_CARGO_KG CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) - ret.minSteerSpeed = 14.5 if car_fw is not None: for fw in car_fw: if fw.ecu == 'eps' and fw.fwVersion in (b"68312176AE", b"68312176AG", b"68273275AG"): ret.minSteerSpeed = 0. + elif candidate == CAR.RAM_HD: + ret.steerActuatorDelay = 0.2 + ret.wheelbase = 3.785 + ret.steerRatio = 15.61 + ret.mass = 3405. + STD_CARGO_KG + ret.minSteerSpeed = 16 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, 1.0, False) + else: raise ValueError(f"Unsupported car: {candidate}") diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index c45a81a0ace509..10478efa89ed49 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -22,23 +22,30 @@ class CAR: # Ram RAM_1500 = "RAM 1500 5TH GEN" + RAM_HD = "RAM HD 5TH GEN" class CarControllerParams: def __init__(self, CP): - self.STEER_MAX = 261 # higher than this faults the EPS on Chrysler/Jeep. Ram DT allows more self.STEER_ERROR_MAX = 80 - - if CP.carFingerprint in RAM_CARS: + if CP.carFingerprint in RAM_HD: + self.STEER_DELTA_UP = 14 + self.STEER_DELTA_DOWN = 14 + self.STEER_MAX = 361 # higher than this faults the EPS + elif CP.carFingerprint in RAM_DT: self.STEER_DELTA_UP = 6 self.STEER_DELTA_DOWN = 6 + self.STEER_MAX = 261 # EPS allows more, up to 350? else: self.STEER_DELTA_UP = 3 self.STEER_DELTA_DOWN = 3 + self.STEER_MAX = 261 # higher than this faults the EPS STEER_THRESHOLD = 120 -RAM_CARS = {CAR.RAM_1500, } +RAM_DT = {CAR.RAM_1500, } +RAM_HD = {CAR.RAM_HD, } +RAM_CARS = RAM_DT | RAM_HD @dataclass class ChryslerCarInfo(CarInfo): @@ -57,6 +64,10 @@ class ChryslerCarInfo(CarInfo): CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-22", harness=Harness.ram), + CAR.RAM_HD: [ + ChryslerCarInfo("Ram 2500 2020-22", harness=Harness.ram), + ChryslerCarInfo("Ram 3500 2020-22", harness=Harness.ram), + ], } # Unique CAN messages: @@ -177,6 +188,41 @@ class ChryslerCarInfo(CarInfo): b'68500483AB', ], }, + + CAR.RAM_HD: { + (Ecu.combinationMeter, 0x742, None): [ + b'68361606AH', + b'68492693AD', + ], + (Ecu.srs, 0x744, None): [ + b'68399794AC', + b'68428503AA', + b'68428505AA', + ], + (Ecu.esp, 0x747, None): [ + b'68334977AH', + b'68504022AB', + b'68530686AB', + ], + (Ecu.fwdRadar, 0x753, None): [ + b'04672895AB', + b'56029827AG', + b'68484694AE', + ], + (Ecu.eps, 0x761, None): [ + b'68421036AC', + b'68507906AB', + ], + (Ecu.engine, 0x7e0, None): [ + b'52421132AF', + b'M2370131MB', + b'M2421132MB', + ], + (Ecu.gateway, 0x18DACBF1, None): [ + b'68488419AB', + b'68535476AB', + ], + }, } DBC = { @@ -188,4 +234,5 @@ class ChryslerCarInfo(CarInfo): CAR.JEEP_CHEROKEE: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), CAR.JEEP_CHEROKEE_2019: dbc_dict('chrysler_pacifica_2017_hybrid_generated', 'chrysler_pacifica_2017_hybrid_private_fusion'), CAR.RAM_1500: dbc_dict('chrysler_ram_dt_generated', None), + CAR.RAM_HD: dbc_dict('chrysler_ram_hd_generated', None), } diff --git a/selfdrive/car/docs.py b/selfdrive/car/docs.py index 5793596a437009..4d79cbfc765b42 100755 --- a/selfdrive/car/docs.py +++ b/selfdrive/car/docs.py @@ -8,16 +8,16 @@ from typing import Dict, List from common.basedir import BASEDIR -from selfdrive.car.docs_definitions import STAR_DESCRIPTIONS, StarColumns, TierColumns, CarInfo, Column, Star +from selfdrive.car import gen_empty_fingerprint +from selfdrive.car.docs_definitions import CarInfo, Column from selfdrive.car.car_helpers import interfaces, get_interface_attr from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR as HKG_RADAR_START_ADDR -def get_all_footnotes(only_tier_cols: bool = False) -> Dict[Enum, int]: +def get_all_footnotes() -> Dict[Enum, int]: all_footnotes = [] - hide_cols = set(StarColumns) - set(TierColumns) if only_tier_cols else [] for footnotes in get_interface_attr("Footnote", ignore_none=True).values(): - all_footnotes.extend([fn for fn in footnotes if fn.value.column not in hide_cols]) + all_footnotes.extend(footnotes) return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)} @@ -25,12 +25,13 @@ def get_all_footnotes(only_tier_cols: bool = False) -> Dict[Enum, int]: CARS_MD_TEMPLATE = os.path.join(BASEDIR, "selfdrive", "car", "CARS_template.md") -def get_all_car_info(only_tier_cols: bool = False) -> List[CarInfo]: +def get_all_car_info() -> List[CarInfo]: all_car_info: List[CarInfo] = [] - footnotes = get_all_footnotes(only_tier_cols) + footnotes = get_all_footnotes() for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items(): # Hyundai exception: those with radar have openpilot longitudinal - fingerprint = {0: {}, 1: {HKG_RADAR_START_ADDR: 8}, 2: {}, 3: {}} + fingerprint = gen_empty_fingerprint() + fingerprint[1] = {HKG_RADAR_START_ADDR: 8} CP = interfaces[model][0].get_params(model, fingerprint=fingerprint, disable_radar=True) if CP.dashcamOnly or car_info is None: @@ -57,21 +58,13 @@ def group_by_make(all_car_info: List[CarInfo]) -> Dict[str, List[CarInfo]]: return dict(sorted_car_info) -def generate_cars_md(all_car_info: List[CarInfo], template_fn: str, only_tier_cols: bool) -> str: +def generate_cars_md(all_car_info: List[CarInfo], template_fn: str) -> str: with open(template_fn, "r") as f: template = jinja2.Template(f.read(), trim_blocks=True, lstrip_blocks=True) - cols = list(Column) - if only_tier_cols: - hide_cols = set(StarColumns) - set(TierColumns) - cols = [c for c in cols if c not in hide_cols] - for car in all_car_info: - for c in hide_cols: - del car.row[c] - - footnotes = [fn.value.text for fn in get_all_footnotes(only_tier_cols)] + footnotes = [fn.value.text for fn in get_all_footnotes()] cars_md: str = template.render(all_car_info=all_car_info, group_by_make=group_by_make, - footnotes=footnotes, Star=Star, Column=cols, STAR_DESCRIPTIONS=STAR_DESCRIPTIONS) + footnotes=footnotes, Column=Column) return cars_md @@ -79,11 +72,10 @@ def generate_cars_md(all_car_info: List[CarInfo], template_fn: str, only_tier_co parser = argparse.ArgumentParser(description="Auto generates supported cars documentation", formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument("--tier-columns", action="store_true", help="Include only columns that count in the tier") parser.add_argument("--template", default=CARS_MD_TEMPLATE, help="Override default template filename") parser.add_argument("--out", default=CARS_MD_OUT, help="Override default generated filename") args = parser.parse_args() with open(args.out, 'w') as f: - f.write(generate_cars_md(get_all_car_info(args.tier_columns), args.template, args.tier_columns)) + f.write(generate_cars_md(get_all_car_info(), args.template)) print(f"Generated and written to {args.out}") diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index 46222d10040fc1..52d6388d7edf6b 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -2,7 +2,7 @@ from collections import namedtuple from dataclasses import dataclass, field from enum import Enum -from typing import Dict, List, Optional, Tuple, Union, no_type_check +from typing import Dict, List, Optional, Tuple, Union from cereal import car from common.conversions import Conversions as CV @@ -10,21 +10,20 @@ GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2 MODEL_YEARS_RE = r"(?<= )((\d{4}-\d{2})|(\d{4}))(,|$)" - -class Tier(Enum): - GOLD = 0 - SILVER = 1 - BRONZE = 2 +# Makes that lack auto-resume with stock long, and auto resume in any configuration +NO_AUTO_RESUME_STOCK_LONG = {"toyota", "gm"} +NO_AUTO_RESUME = NO_AUTO_RESUME_STOCK_LONG | {"nissan", "subaru"} class Column(Enum): MAKE = "Make" MODEL = "Model" PACKAGE = "Supported Package" - LONGITUDINAL = "openpilot ACC" - FSR_LONGITUDINAL = "Stop and Go" - FSR_STEERING = "Steer to 0" + LONGITUDINAL = "ACC" + FSR_LONGITUDINAL = "No ACC accel below" + FSR_STEERING = "No ALC below" STEERING_TORQUE = "Steering Torque" + HARNESS = "Harness" class Star(Enum): @@ -33,9 +32,46 @@ class Star(Enum): EMPTY = "empty" -StarColumns = list(Column)[3:] -TierColumns = (Column.FSR_LONGITUDINAL, Column.FSR_STEERING, Column.STEERING_TORQUE) -CarFootnote = namedtuple("CarFootnote", ["text", "column", "star"], defaults=[None]) +class Harness(Enum): + nidec = "Honda Nidec" + bosch_a = "Honda Bosch A" + bosch_b = "Honda Bosch B" + toyota = "Toyota" + subaru_a = "Subaru A" + subaru_b = "Subaru B" + fca = "FCA" + ram = "Ram" + vw = "VW" + j533 = "J533" + hyundai_a = "Hyundai A" + hyundai_b = "Hyundai B" + hyundai_c = "Hyundai C" + hyundai_d = "Hyundai D" + hyundai_e = "Hyundai E" + hyundai_f = "Hyundai F" + hyundai_g = "Hyundai G" + hyundai_h = "Hyundai H" + hyundai_i = "Hyundai I" + hyundai_j = "Hyundai J" + hyundai_k = "Hyundai K" + hyundai_l = "Hyundai L" + hyundai_m = "Hyundai M" + hyundai_n = "Hyundai N" + hyundai_o = "Hyundai O" + hyundai_p = "Hyundai P" + hyundai_q = "Hyundai Q" + custom = "Developer" + obd_ii = "OBD-II" + gm = "GM" + nissan_a = "Nissan A" + nissan_b = "Nissan B" + mazda = "Mazda" + ford_q3 = "Ford Q3" + ford_q4 = "Ford Q4" + none = "None" + + +CarFootnote = namedtuple("CarFootnote", ["text", "column"], defaults=[None]) def get_footnotes(footnotes: List[Enum], column: Column) -> List[Enum]: @@ -79,7 +115,7 @@ class CarInfo: footnotes: List[Enum] = field(default_factory=list) min_steer_speed: Optional[float] = None min_enable_speed: Optional[float] = None - harness: Optional[Enum] = None + harness: Enum = Harness.none def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): # TODO: set all the min steer speeds in carParams and remove this @@ -88,8 +124,6 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): else: self.min_steer_speed = CP.minSteerSpeed - assert self.harness is not None, f"{CP.carFingerprint}: Need to specify car harness" - # TODO: set all the min enable speeds in carParams correctly and remove this if self.min_enable_speed is None: self.min_enable_speed = CP.minEnableSpeed @@ -101,11 +135,11 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): Column.MAKE: self.make, Column.MODEL: self.model, Column.PACKAGE: self.package, - # StarColumns - Column.LONGITUDINAL: Star.FULL if CP.openpilotLongitudinalControl and not CP.radarOffCan else Star.EMPTY, - Column.FSR_LONGITUDINAL: Star.FULL if self.min_enable_speed <= 0. else Star.EMPTY, - Column.FSR_STEERING: Star.FULL if self.min_steer_speed <= 0. else Star.EMPTY, + Column.LONGITUDINAL: "openpilot" if CP.openpilotLongitudinalControl and not CP.radarOffCan else "Stock", + Column.FSR_LONGITUDINAL: f"{max(self.min_enable_speed * CV.MS_TO_MPH, 0):.0f} mph", + Column.FSR_STEERING: f"{max(self.min_steer_speed * CV.MS_TO_MPH, 0):.0f} mph", Column.STEERING_TORQUE: Star.EMPTY, + Column.HARNESS: self.harness.value, } # Set steering torque star from max lateral acceleration @@ -113,26 +147,7 @@ def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): if CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD: self.row[Column.STEERING_TORQUE] = Star.FULL - if CP.notCar: - for col in StarColumns: - self.row[col] = Star.FULL - self.all_footnotes = all_footnotes - for column in StarColumns: - # Demote if footnote specifies a star - for fn in get_footnotes(self.footnotes, column): - if fn.value.star is not None: - self.row[column] = fn.value.star - - # openpilot ACC star doesn't count for tiers - full_stars = [s for col, s in self.row.items() if col in TierColumns].count(Star.FULL) - if full_stars == len(TierColumns): - self.tier = Tier.GOLD - elif full_stars == len(TierColumns) - 1: - self.tier = Tier.SILVER - else: - self.tier = Tier.BRONZE - self.year_list = get_year_list(self.years) self.detail_sentence = self.get_detail_sentence(CP) @@ -147,11 +162,11 @@ def get_detail_sentence(self, CP): else: alc = "" - # Exception for Nissan, Subaru, and stock long Toyota which do not auto-resume yet + # Exception for cars which do not auto-resume yet acc = "" if self.min_enable_speed > 0: acc = f" while driving above {self.min_enable_speed * CV.MS_TO_MPH:.0f} mph" - elif CP.carName not in ("nissan", "subaru", "toyota") or (CP.carName == "toyota" and CP.openpilotLongitudinalControl): + elif CP.carName not in NO_AUTO_RESUME or (CP.carName in NO_AUTO_RESUME_STOCK_LONG and CP.openpilotLongitudinalControl): acc = " that automatically resumes from a stop" if self.row[Column.STEERING_TORQUE] != Star.FULL: @@ -165,10 +180,9 @@ def get_detail_sentence(self, CP): else: raise Exception(f"This notCar does not have a detail sentence: {CP.carFingerprint}") - @no_type_check def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str: item: Union[str, Star] = self.row[column] - if column in StarColumns: + if isinstance(item, Star): item = star_icon.format(item.value) elif column == Column.MODEL and len(self.years): item += f" {self.years}" @@ -180,56 +194,3 @@ def get_column(self, column: Column, star_icon: str, footnote_tag: str) -> str: return item - -class Harness(Enum): - nidec = "Honda Nidec" - bosch_a = "Honda Bosch A" - bosch_b = "Honda Bosch B" - toyota = "Toyota" - subaru = "Subaru" - fca = "FCA" - ram = "Ram" - vw = "VW" - j533 = "J533" - hyundai_a = "Hyundai A" - hyundai_b = "Hyundai B" - hyundai_c = "Hyundai C" - hyundai_d = "Hyundai D" - hyundai_e = "Hyundai E" - hyundai_f = "Hyundai F" - hyundai_g = "Hyundai G" - hyundai_h = "Hyundai H" - hyundai_i = "Hyundai I" - hyundai_j = "Hyundai J" - hyundai_k = "Hyundai K" - hyundai_l = "Hyundai L" - hyundai_m = "Hyundai M" - hyundai_n = "Hyundai N" - hyundai_o = "Hyundai O" - hyundai_p = "Hyundai P" - custom = "Developer" - obd_ii = "OBD-II" - nissan_a = "Nissan A" - nissan_b = "Nissan B" - mazda = "Mazda" - none = "None" - - -STAR_DESCRIPTIONS = { - "Gas & Brakes": { # icon and row name - Column.FSR_LONGITUDINAL.value: [ - [Star.FULL.value, "openpilot operates down to 0 mph."], - [Star.EMPTY.value, "openpilot operates only above a minimum speed. See your car's manual for the minimum speed."], - ], - }, - "Steering": { - Column.FSR_STEERING.value: [ - [Star.FULL.value, "openpilot can control the steering wheel down to 0 mph."], - [Star.EMPTY.value, "No steering control below certain speeds. See your car's manual for the minimum speed."], - ], - Column.STEERING_TORQUE.value: [ - [Star.FULL.value, "Car has enough steering torque to comfortably take most highway turns."], - [Star.EMPTY.value, "Limited ability to make tighter turns."], - ], - }, -} diff --git a/selfdrive/car/ford/radar_interface.py b/selfdrive/car/ford/radar_interface.py index 866602cf09888b..c942703002ff29 100644 --- a/selfdrive/car/ford/radar_interface.py +++ b/selfdrive/car/ford/radar_interface.py @@ -1,33 +1,63 @@ #!/usr/bin/env python3 +from math import cos, sin from cereal import car -from common.conversions import Conversions as CV from opendbc.can.parser import CANParser -from selfdrive.car.ford.values import CANBUS, DBC +from common.conversions import Conversions as CV +from selfdrive.car.ford.values import CANBUS, DBC, RADAR from selfdrive.car.interfaces import RadarInterfaceBase -RADAR_MSGS = list(range(0x500, 0x540)) +DELPHI_ESR_RADAR_MSGS = list(range(0x500, 0x540)) +DELPHI_MRR_RADAR_START_ADDR = 0x120 +DELPHI_MRR_RADAR_MSG_COUNT = 64 -def _create_radar_can_parser(car_fingerprint): - if DBC[car_fingerprint]['radar'] is None: - return None - msg_n = len(RADAR_MSGS) +def _create_delphi_esr_radar_can_parser(): + msg_n = len(DELPHI_ESR_RADAR_MSGS) signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n, - RADAR_MSGS * 3)) - checks = list(zip(RADAR_MSGS, [20] * msg_n)) + DELPHI_ESR_RADAR_MSGS * 3)) + checks = list(zip(DELPHI_ESR_RADAR_MSGS, [20] * msg_n)) + + return CANParser(RADAR.DELPHI_ESR, signals, checks, CANBUS.radar) + + +def _create_delphi_mrr_radar_can_parser(): + signals = [] + checks = [] + + for i in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1): + msg = f"MRR_Detection_{i:03d}" + signals += [ + (f"CAN_DET_VALID_LEVEL_{i:02d}", msg), + (f"CAN_DET_AZIMUTH_{i:02d}", msg), + (f"CAN_DET_RANGE_{i:02d}", msg), + (f"CAN_DET_RANGE_RATE_{i:02d}", msg), + (f"CAN_DET_AMPLITUDE_{i:02d}", msg), + (f"CAN_SCAN_INDEX_2LSB_{i:02d}", msg), + ] + checks += [(msg, 20)] + + return CANParser(RADAR.DELPHI_MRR, signals, checks, CANBUS.radar) - return CANParser(DBC[car_fingerprint]['radar'], signals, checks, CANBUS.radar) class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) - self.validCnt = {key: 0 for key in RADAR_MSGS} - self.track_id = 0 - self.rcp = _create_radar_can_parser(CP.carFingerprint) - self.trigger_msg = 0x53f self.updated_messages = set() + self.track_id = 0 + self.radar = DBC[CP.carFingerprint]['radar'] + if self.radar is None: + self.rcp = None + elif self.radar == RADAR.DELPHI_ESR: + self.rcp = _create_delphi_esr_radar_can_parser() + self.trigger_msg = DELPHI_ESR_RADAR_MSGS[-1] + self.valid_cnt = {key: 0 for key in DELPHI_ESR_RADAR_MSGS} + elif self.radar == RADAR.DELPHI_MRR: + self.rcp = _create_delphi_mrr_radar_can_parser() + self.trigger_msg = DELPHI_MRR_RADAR_START_ADDR + DELPHI_MRR_RADAR_MSG_COUNT - 1 + else: + raise ValueError(f"Unsupported radar: {self.radar}") def update(self, can_strings): if self.rcp is None: @@ -45,20 +75,30 @@ def update(self, can_strings): errors.append("canError") ret.errors = errors + if self.radar == RADAR.DELPHI_ESR: + self._update_delphi_esr() + elif self.radar == RADAR.DELPHI_MRR: + self._update_delphi_mrr() + + ret.points = list(self.pts.values()) + self.updated_messages.clear() + return ret + + def _update_delphi_esr(self): for ii in sorted(self.updated_messages): cpt = self.rcp.vl[ii] if cpt['X_Rel'] > 0.00001: - self.validCnt[ii] = 0 # reset counter + self.valid_cnt[ii] = 0 # reset counter if cpt['X_Rel'] > 0.00001: - self.validCnt[ii] += 1 + self.valid_cnt[ii] += 1 else: - self.validCnt[ii] = max(self.validCnt[ii] - 1, 0) - #print ii, self.validCnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle'] + self.valid_cnt[ii] = max(self.valid_cnt[ii] - 1, 0) + #print ii, self.valid_cnt[ii], cpt['VALID'], cpt['X_Rel'], cpt['Angle'] # radar point only valid if there have been enough valid measurements - if self.validCnt[ii] > 0: + if self.valid_cnt[ii] > 0: if ii not in self.pts: self.pts[ii] = car.RadarData.RadarPoint.new_message() self.pts[ii].trackId = self.track_id @@ -73,6 +113,36 @@ def update(self, can_strings): if ii in self.pts: del self.pts[ii] - ret.points = list(self.pts.values()) - self.updated_messages.clear() - return ret + def _update_delphi_mrr(self): + for ii in range(1, DELPHI_MRR_RADAR_MSG_COUNT + 1): + msg = self.rcp.vl[f"MRR_Detection_{ii:03d}"] + + # SCAN_INDEX rotates through 0..3 on each message + # treat these as separate points + scanIndex = msg[f"CAN_SCAN_INDEX_2LSB_{ii:02d}"] + i = (ii - 1) * 4 + scanIndex + + if i not in self.pts: + self.pts[i] = car.RadarData.RadarPoint.new_message() + self.pts[i].trackId = self.track_id + self.pts[i].aRel = float('nan') + self.pts[i].yvRel = float('nan') + self.track_id += 1 + + valid = bool(msg[f"CAN_DET_VALID_LEVEL_{ii:02d}"]) + amplitude = msg[f"CAN_DET_AMPLITUDE_{ii:02d}"] # dBsm [-64|63] + + if valid and 0 < amplitude <= 15: + azimuth = msg[f"CAN_DET_AZIMUTH_{ii:02d}"] # rad [-3.1416|3.13964] + dist = msg[f"CAN_DET_RANGE_{ii:02d}"] # m [0|255.984] + distRate = msg[f"CAN_DET_RANGE_RATE_{ii:02d}"] # m/s [-128|127.984] + + # *** openpilot radar point *** + self.pts[i].dRel = cos(azimuth) * dist # m from front of car + self.pts[i].yRel = -sin(azimuth) * dist # in car frame's y axis, left is positive + self.pts[i].vRel = distRate # m/s + + self.pts[i].measured = True + + else: + del self.pts[i] diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 6b8396cf072ae6..825d515da9cbd6 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -24,6 +24,11 @@ class CarControllerParams: STEER_RATE_LIMIT_DOWN = AngleRateLimit(speed_points=[0., 5., 15.], max_angle_diff_points=[5., 3.5, 0.4]) +class RADAR: + DELPHI_ESR = 'ford_fusion_2018_adas' + DELPHI_MRR = 'FORD_CADS' + + class CANBUS: main = 0 radar = 1 @@ -80,6 +85,6 @@ class CAR: DBC = { - CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', None), - CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', None), + CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR), + CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', RADAR.DELPHI_MRR), } diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 0ed8cc5425d14d..66a19a610d640d 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -208,12 +208,14 @@ class Request: "chrysler", [CHRYSLER_VERSION_REQUEST], [CHRYSLER_VERSION_RESPONSE], + whitelist_ecus=[Ecu.esp, Ecu.eps, Ecu.srs, Ecu.gateway, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.combinationMeter], rx_offset=CHRYSLER_RX_OFFSET, ), Request( "chrysler", [CHRYSLER_VERSION_REQUEST], [CHRYSLER_VERSION_RESPONSE], + whitelist_ecus=[Ecu.engine, Ecu.transmission], ), # Ford Request( @@ -337,17 +339,21 @@ def match_fw_to_car_exact(fw_versions_dict): return set(candidates.keys()) - set(invalid) -def match_fw_to_car(fw_versions, allow_fuzzy=True): +def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True): # Try exact matching first - exact_matches = [(True, match_fw_to_car_exact)] + exact_matches = [] + if allow_exact: + exact_matches = [(True, match_fw_to_car_exact)] if allow_fuzzy: exact_matches.append((False, match_fw_to_car_fuzzy)) + brands = get_interface_attr('FW_VERSIONS', ignore_none=True).keys() for exact_match, match_func in exact_matches: - # TODO: For each brand, attempt to fingerprint using only FW returned from its queries + # For each brand, attempt to fingerprint using all FW returned from its queries matches = set() - fw_versions_dict = build_fw_dict(fw_versions, filter_brand=None) - matches |= match_func(fw_versions_dict) + for brand in brands: + fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand) + matches |= match_func(fw_versions_dict) if len(matches): return exact_match, matches @@ -416,9 +422,8 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True): car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress) all_car_fw.extend(car_fw) - - # TODO: Until erroneous FW versions are removed, try to fingerprint on all possible combinations so far - _, matches = match_fw_to_car(all_car_fw, allow_fuzzy=False) + # Try to match using FW returned from this brand only + matches = match_fw_to_car_exact(build_fw_dict(car_fw)) if len(matches) == 1: break diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 0e7406514315fe..977d20c5b3c2e1 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -5,7 +5,7 @@ from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car.gm import gmcan -from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, EV_CAR +from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams, CruiseButtons, EV_CAR VisualAlert = car.CarControl.HUDControl.VisualAlert NetworkLocation = car.CarParams.NetworkLocation @@ -19,6 +19,7 @@ def __init__(self, dbc_name, CP, VM): self.apply_gas = 0 self.apply_brake = 0 self.frame = 0 + self.last_button_frame = 0 self.lka_steering_cmd_counter_last = -1 self.lka_icon_status_last = (False, False) @@ -46,8 +47,7 @@ def update(self, CC, CS): if CS.lka_steering_cmd_counter != self.lka_steering_cmd_counter_last: self.lka_steering_cmd_counter_last = CS.lka_steering_cmd_counter elif (self.frame % self.params.STEER_STEP) == 0: - lkas_enabled = CC.latActive and CS.out.vEgo > self.params.MIN_STEER_SPEED - if lkas_enabled: + if CC.latActive: new_steer = int(round(actuators.steer * self.params.STEER_MAX)) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) else: @@ -58,7 +58,7 @@ def update(self, CC, CS): # moment of disengaging, increment the counter based on the last message known to pass Panda safety checks. idx = (CS.lka_steering_cmd_counter + 1) % 4 - can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, lkas_enabled)) + can_sends.append(gmcan.create_steering_control(self.packer_pt, CanBus.POWERTRAIN, apply_steer, idx, CC.latActive)) if self.CP.openpilotLongitudinalControl: # Gas/regen, brakes, and UI commands - all at 25Hz @@ -107,6 +107,13 @@ def update(self, CC, CS): if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0: can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN) + else: + # Stock longitudinal, integrated at camera + if (self.frame - self.last_button_frame) * DT_CTRL > 0.04: + if CC.cruiseControl.cancel: + self.last_button_frame = self.frame + can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL)) + # Show green icon when LKA torque is applied, and # alarming orange icon when approaching torque limit. # If not sent again, LKA icon disappears in about 5 seconds. @@ -114,6 +121,8 @@ def update(self, CC, CS): lka_active = CS.lkas_status == 1 lka_critical = lka_active and abs(actuators.steer) > 0.9 lka_icon_status = (lka_active, lka_critical) + + # SW_GMLAN not yet on cam harness, no HUD alerts if self.CP.networkLocation != NetworkLocation.fwdCamera and (self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last): steer_alert = hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw) can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert)) diff --git a/selfdrive/car/gm/carstate.py b/selfdrive/car/gm/carstate.py index b174d0327ed7c4..0bba1d29b8c01f 100644 --- a/selfdrive/car/gm/carstate.py +++ b/selfdrive/car/gm/carstate.py @@ -1,4 +1,5 @@ from cereal import car +from common.conversions import Conversions as CV from common.numpy_fast import mean from opendbc.can.can_define import CANDefine from opendbc.can.parser import CANParser @@ -6,6 +7,7 @@ from selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD TransmissionType = car.CarParams.TransmissionType +NetworkLocation = car.CarParams.NetworkLocation class CarState(CarStateBase): @@ -14,12 +16,14 @@ def __init__(self, CP): can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"] self.lka_steering_cmd_counter = 0 + self.buttons_counter = 0 - def update(self, pt_cp, loopback_cp): + def update(self, pt_cp, cam_cp, loopback_cp): ret = car.CarState.new_message() self.prev_cruise_buttons = self.cruise_buttons self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"] + self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"] ret.wheelSpeeds = self.get_wheel_speeds( pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"], @@ -77,9 +81,21 @@ def update(self, pt_cp, loopback_cp): ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL + if self.CP.networkLocation == NetworkLocation.fwdCamera: + ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS return ret + @staticmethod + def get_cam_can_parser(CP): + signals = [] + checks = [] + if CP.networkLocation == NetworkLocation.fwdCamera: + signals.append(("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus")) + checks.append(("ASCMActiveCruiseControlStatus", 25)) + + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.CAMERA) + @staticmethod def get_can_parser(CP): signals = [ @@ -95,6 +111,7 @@ def get_can_parser(CP): ("AcceleratorPedal2", "AcceleratorPedal2"), ("CruiseState", "AcceleratorPedal2"), ("ACCButtons", "ASCMSteeringButton"), + ("RollingCounter", "ASCMSteeringButton"), ("SteeringWheelAngle", "PSCMSteeringAngle"), ("SteeringWheelRate", "PSCMSteeringAngle"), ("FLWheelSpd", "EBCMWheelSpdFront"), diff --git a/selfdrive/car/gm/gmcan.py b/selfdrive/car/gm/gmcan.py index 47e6090a03a32a..20e4c4ab6efb69 100644 --- a/selfdrive/car/gm/gmcan.py +++ b/selfdrive/car/gm/gmcan.py @@ -1,5 +1,12 @@ from selfdrive.car import make_can_msg +def create_buttons(packer, bus, idx, button): + values = { + "ACCButtons": button, + "RollingCounter": idx, + } + return packer.make_can_msg("ASCMSteeringButton", bus, values) + def create_steering_control(packer, bus, apply_steer, idx, lkas_active): values = { @@ -59,8 +66,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, near_stop, at_f return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values) def create_acc_dashboard_command(packer, bus, acc_engaged, target_speed_kph, lead_car_in_sight, fcw): - # Not a bit shift, dash can round up based on low 4 bits. - target_speed = int(target_speed_kph * 16) & 0xfff + target_speed = min(target_speed_kph, 255) values = { "ACCAlwaysOne" : 1, diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 10ae670ac8e5b5..09d950ea8d0072 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 from cereal import car from math import fabs +from panda import Panda from common.conversions import Conversions as CV -from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config -from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams +from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR from selfdrive.car.interfaces import CarInterfaceBase ButtonType = car.CarState.ButtonEvent.Type @@ -48,29 +49,36 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret.carName = "gm" ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] - ret.pcmCruise = False # For ASCM, stock non-adaptive cruise control is kept off - ret.radarOffCan = False # For ASCM, radar exists - ret.transmissionType = TransmissionType.automatic - # NetworkLocation.gateway: OBD-II harness (typically ASCM), NetworkLocation.fwdCamera: non-ASCM - ret.networkLocation = NetworkLocation.gateway + + if candidate in EV_CAR: + ret.transmissionType = TransmissionType.direct + else: + ret.transmissionType = TransmissionType.automatic + + if candidate in CAMERA_ACC_CAR: + ret.openpilotLongitudinalControl = False + ret.networkLocation = NetworkLocation.fwdCamera + ret.radarOffCan = True # no radar + ret.pcmCruise = True + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM + else: # ASCM, OBD-II harness + ret.openpilotLongitudinalControl = True + ret.networkLocation = NetworkLocation.gateway + ret.radarOffCan = False + ret.pcmCruise = False # stock non-adaptive cruise control is kept off # These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars likely still work fine. Once a user confirms each car works and a test route is # added to selfdrive/car/tests/routes.py, we can remove it from this list. ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL} - # Presence of a camera on the object bus is ok. - # Have to go to read_only if ASCM is online (ACC-enabled cars), - # or camera is on powertrain bus (LKA cars without ACC). - ret.openpilotLongitudinalControl = True - tire_stiffness_factor = 0.444 # not optimized yet - # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. ret.minSteerSpeed = 7 * CV.MPH_TO_MS ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.00]] ret.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594 ret.steerActuatorDelay = 0.1 # Default delay, not measured yet + tire_stiffness_factor = 0.444 # not optimized yet ret.longitudinalTuning.kpBP = [5., 35.] ret.longitudinalTuning.kpV = [2.4, 1.5] @@ -84,7 +92,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.minEnableSpeed = 18 * CV.MPH_TO_MS if candidate == CAR.VOLT: - ret.transmissionType = TransmissionType.direct ret.mass = 1607. + STD_CARGO_KG ret.wheelbase = 2.69 ret.steerRatio = 17.7 # Stock 15.7, LiveParameters @@ -143,6 +150,25 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.lateralTuning.pid.kf = 0.000045 tire_stiffness_factor = 1.0 + elif candidate == CAR.BOLT_EUV: + ret.minEnableSpeed = -1 + ret.mass = 1669. + STD_CARGO_KG + ret.wheelbase = 2.675 + ret.steerRatio = 16.8 + ret.centerToFront = ret.wheelbase * 0.4 + tire_stiffness_factor = 1.0 + ret.steerActuatorDelay = 0.2 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + + elif candidate == CAR.SILVERADO: + ret.minEnableSpeed = -1 + ret.mass = 2200. + STD_CARGO_KG + ret.wheelbase = 3.75 + ret.steerRatio = 16.3 + ret.centerToFront = ret.wheelbase * 0.5 + tire_stiffness_factor = 1.0 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + # TODO: get actual value, for now starting with reasonable value for # civic and scaling by mass and wheelbase ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) @@ -156,7 +182,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa # returns a car.CarState def _update(self, c): - ret = self.CS.update(self.cp, self.cp_loopback) + ret = self.CS.update(self.cp, self.cp_cam, self.cp_loopback) if self.CS.cruise_buttons != self.CS.prev_cruise_buttons and self.CS.prev_cruise_buttons != CruiseButtons.INIT: be = create_button_event(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, CruiseButtons.UNPRESS) @@ -178,9 +204,6 @@ def _update(self, c): if ret.vEgo < self.CP.minSteerSpeed: events.add(car.CarEvent.EventName.belowSteerSpeed) - # handle button presses - events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise)) - ret.events = events.to_msg() return ret diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 016910a40fafde..21ede171e04202 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -14,7 +14,6 @@ class CarControllerParams: STEER_STEP = 2 # Control frames per command (50hz) STEER_DELTA_UP = 7 # Delta rates require review due to observed EPS weakness STEER_DELTA_DOWN = 17 - MIN_STEER_SPEED = 3. # m/s STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_MULTIPLIER = 4 STEER_DRIVER_FACTOR = 100 @@ -59,10 +58,8 @@ class CAR: ACADIA = "GMC ACADIA DENALI 2018" BUICK_REGAL = "BUICK REGAL ESSENCE 2018" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" - - -EV_CAR = {CAR.VOLT} -STEER_THRESHOLD = 1.0 + BOLT_EUV = "CHEVROLET BOLT EUV 2022" + SILVERADO = "CHEVROLET SILVERADO 1500 2020" class Footnote(Enum): @@ -87,6 +84,11 @@ class GMCarInfo(CarInfo): CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"), + CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210", footnotes=[], harness=Harness.gm), + CAR.SILVERADO: [ + GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II", footnotes=[], harness=Harness.gm), + GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", footnotes=[], harness=Harness.gm), + ], } @@ -107,6 +109,7 @@ class AccState: class CanBus: POWERTRAIN = 0 OBSTACLE = 1 + CAMERA = 2 CHASSIS = 2 SW_GMLAN = 3 LOOPBACK = 128 @@ -155,6 +158,21 @@ class CanBus: { 309: 1, 848: 8, 849: 8, 850: 8, 851: 8, 852: 8, 853: 8, 854: 3, 1056: 6, 1057: 8, 1058: 8, 1059: 8, 1060: 8, 1061: 8, 1062: 8, 1063: 8, 1064: 8, 1065: 8, 1066: 8, 1067: 8, 1068: 8, 1120: 8, 1121: 8, 1122: 8, 1123: 8, 1124: 8, 1125: 8, 1126: 8, 1127: 8, 1128: 8, 1129: 8, 1130: 8, 1131: 8, 1132: 8, 1133: 8, 1134: 8, 1135: 8, 1136: 8, 1137: 8, 1138: 8, 1139: 8, 1140: 8, 1141: 8, 1142: 8, 1143: 8, 1146: 8, 1147: 8, 1148: 8, 1149: 8, 1150: 8, 1151: 8, 1216: 8, 1217: 8, 1218: 8, 1219: 8, 1220: 8, 1221: 8, 1222: 8, 1223: 8, 1224: 8, 1225: 8, 1226: 8, 1232: 8, 1233: 8, 1234: 8, 1235: 8, 1236: 8, 1237: 8, 1238: 8, 1239: 8, 1240: 8, 1241: 8, 1242: 8, 1787: 8, 1788: 8 }], + CAR.BOLT_EUV: [ + { + 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7 + }], + CAR.SILVERADO: [ + { + 190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7 + }], } DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')) + +EV_CAR = {CAR.VOLT, CAR.BOLT_EUV} + +# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness) +CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO} + +STEER_THRESHOLD = 1.0 diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index a78189b697448c..a235ba6cda7306 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -4,7 +4,7 @@ from common.conversions import Conversions as CV from common.numpy_fast import interp from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, HondaFlags, CAR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS -from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu @@ -86,7 +86,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl eps_modified = True if candidate == CAR.CIVIC: - stop_and_go = True ret.mass = CivicParams.MASS ret.wheelbase = CivicParams.WHEELBASE ret.centerToFront = CivicParams.CENTER_TO_FRONT @@ -106,7 +105,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl tire_stiffness_factor = 1. elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022): - stop_and_go = True ret.mass = CivicParams.MASS ret.wheelbase = CivicParams.WHEELBASE ret.centerToFront = CivicParams.CENTER_TO_FRONT @@ -116,7 +114,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate in (CAR.ACCORD, CAR.ACCORDH): - stop_and_go = True ret.mass = 3279. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.83 ret.centerToFront = ret.wheelbase * 0.39 @@ -130,7 +127,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] elif candidate == CAR.ACURA_ILX: - stop_and_go = False ret.mass = 3095. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.67 ret.centerToFront = ret.wheelbase * 0.37 @@ -140,7 +136,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate in (CAR.CRV, CAR.CRV_EU): - stop_and_go = False ret.mass = 3572. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.62 ret.centerToFront = ret.wheelbase * 0.41 @@ -151,7 +146,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.wheelSpeedFactor = 1.025 elif candidate == CAR.CRV_5G: - stop_and_go = True ret.mass = 3410. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.66 ret.centerToFront = ret.wheelbase * 0.41 @@ -169,7 +163,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.wheelSpeedFactor = 1.025 elif candidate == CAR.CRV_HYBRID: - stop_and_go = True ret.mass = 1667. + STD_CARGO_KG # mean of 4 models in kg ret.wheelbase = 2.66 ret.centerToFront = ret.wheelbase * 0.41 @@ -180,7 +173,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.wheelSpeedFactor = 1.025 elif candidate == CAR.FIT: - stop_and_go = False ret.mass = 2644. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.53 ret.centerToFront = ret.wheelbase * 0.39 @@ -190,7 +182,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]] elif candidate == CAR.FREED: - stop_and_go = False ret.mass = 3086. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.74 # the remaining parameters were copied from FIT @@ -201,7 +192,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]] elif candidate == CAR.HRV: - stop_and_go = False ret.mass = 3125 * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.61 ret.centerToFront = ret.wheelbase * 0.41 @@ -212,7 +202,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.wheelSpeedFactor = 1.025 elif candidate == CAR.ACURA_RDX: - stop_and_go = False ret.mass = 3935. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.68 ret.centerToFront = ret.wheelbase * 0.38 @@ -222,7 +211,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] elif candidate == CAR.ACURA_RDX_3G: - stop_and_go = True ret.mass = 4068. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.75 ret.centerToFront = ret.wheelbase * 0.41 @@ -232,7 +220,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl tire_stiffness_factor = 0.677 elif candidate == CAR.ODYSSEY: - stop_and_go = False ret.mass = 4471. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 3.00 ret.centerToFront = ret.wheelbase * 0.41 @@ -242,7 +229,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]] elif candidate == CAR.ODYSSEY_CHN: - stop_and_go = False ret.mass = 1849.2 + STD_CARGO_KG # mean of 4 models in kg ret.wheelbase = 2.90 ret.centerToFront = ret.wheelbase * 0.41 # from CAR.ODYSSEY @@ -252,7 +238,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]] elif candidate in (CAR.PILOT, CAR.PASSPORT): - stop_and_go = False ret.mass = 4204. * CV.LB_TO_KG + STD_CARGO_KG # average weight ret.wheelbase = 2.82 ret.centerToFront = ret.wheelbase * 0.428 @@ -262,7 +247,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] elif candidate == CAR.RIDGELINE: - stop_and_go = False ret.mass = 4515. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 3.18 ret.centerToFront = ret.wheelbase * 0.41 @@ -272,7 +256,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] elif candidate == CAR.INSIGHT: - stop_and_go = True ret.mass = 2987. * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.7 ret.centerToFront = ret.wheelbase * 0.39 @@ -282,7 +265,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] elif candidate == CAR.HONDA_E: - stop_and_go = True ret.mass = 3338.8 * CV.LB_TO_KG + STD_CARGO_KG ret.wheelbase = 2.5 ret.centerToFront = ret.wheelbase * 0.5 @@ -311,7 +293,8 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl # min speed to enable ACC. if car can do stop and go, then set enabling speed # to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not # conflict with PCM acc - ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptor) else 25.5 * CV.MPH_TO_MS + stop_and_go = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor + ret.minEnableSpeed = -1. if stop_and_go else 25.5 * CV.MPH_TO_MS # TODO: get actual value, for now starting with reasonable value for # civic and scaling by mass and wheelbase @@ -369,9 +352,6 @@ def _update(self, c): if self.CS.CP.minEnableSpeed > 0 and ret.vEgo < 0.001: events.add(EventName.manualRestart) - # handle button presses - events.events.extend(create_button_enable_events(ret.buttonEvents, self.CP.pcmCruise)) - ret.events = events.to_msg() return ret diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 88e118cc3d5a1d..ad4f73c011d4b2 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -113,7 +113,7 @@ class HondaCarInfo(CarInfo): HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), ], CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), - CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec), + CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC_BOSCH: [ HondaCarInfo("Honda Civic 2019-21", "All", video_link="https://www.youtube.com/watch?v=4Iz1Mz5LGF8", footnotes=[Footnote.CIVIC_DIESEL], min_steer_speed=2. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a), @@ -131,7 +131,7 @@ class HondaCarInfo(CarInfo): CAR.FIT: HondaCarInfo("Honda Fit 2018-20", harness=Harness.nidec), CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec), CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", harness=Harness.nidec), - CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-22", min_steer_speed=0., harness=Harness.nidec), + CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", min_steer_speed=0., harness=Harness.nidec), CAR.ODYSSEY_CHN: None, # Chinese version of Odyssey CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec), CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 4d181e3821b012..971330a33516ce 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -4,8 +4,8 @@ from common.realtime import DT_CTRL from opendbc.can.packer import CANPacker from selfdrive.car import apply_std_steer_torque_limits -from selfdrive.car.hyundai import hda2can, hyundaican -from selfdrive.car.hyundai.values import Buttons, CarControllerParams, HDA2_CAR, CAR +from selfdrive.car.hyundai import hyundaicanfd, hyundaican +from selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR VisualAlert = car.CarControl.HUDControl.VisualAlert LongCtrlState = car.CarControl.Actuators.LongControlState @@ -69,24 +69,35 @@ def update(self, CC, CS): can_sends = [] - if self.CP.carFingerprint in HDA2_CAR: + if self.CP.carFingerprint in CANFD_CAR: # steering control - can_sends.append(hda2can.create_lkas(self.packer, CC.enabled, CC.latActive, apply_steer)) + can_sends.append(hyundaicanfd.create_lkas(self.packer, self.CP, CC.enabled, CC.latActive, apply_steer)) - if self.frame % 5 == 0: - can_sends.append(hda2can.create_cam_0x2a4(self.packer, CS.cam_0x2a4)) + # block LFA on HDA2 + if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.CANFD_HDA2): + can_sends.append(hyundaicanfd.create_cam_0x2a4(self.packer, CS.cam_0x2a4)) - # cruise cancel + # LFA and HDA icons + if self.frame % 2 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_HDA2): + can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, CC.enabled)) + + # button presses if (self.frame - self.last_button_frame) * DT_CTRL > 0.25: + # cruise cancel if CC.cruiseControl.cancel: - for _ in range(20): - can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL)) - self.last_button_frame = self.frame + if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS: + can_sends.append(hyundaicanfd.create_cruise_info(self.packer, CS.cruise_info_copy, True)) + self.last_button_frame = self.frame + else: + for _ in range(20): + can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.CANCEL)) + self.last_button_frame = self.frame # cruise standstill resume elif CC.cruiseControl.resume: - can_sends.append(hda2can.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL)) - self.last_button_frame = self.frame + if not (self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS): + can_sends.append(hyundaicanfd.create_buttons(self.packer, CS.buttons_counter+1, Buttons.RES_ACCEL)) + self.last_button_frame = self.frame else: # tester present - w/ no response (keeps radar disabled) diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index f1428555a88f52..eab6e73f1f429b 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -5,7 +5,7 @@ from common.conversions import Conversions as CV from opendbc.can.parser import CANParser from opendbc.can.can_define import CANDefine -from selfdrive.car.hyundai.values import DBC, FEATURES, CAMERA_SCC_CAR, HDA2_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams +from selfdrive.car.hyundai.values import HyundaiFlags, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams from selfdrive.car.interfaces import CarStateBase PREV_BUTTON_SAMPLES = 8 @@ -19,8 +19,8 @@ def __init__(self, CP): self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) - if CP.carFingerprint in HDA2_CAR: - self.shifter_values = can_define.dv["ACCELERATOR"]["GEAR"] + if CP.carFingerprint in CANFD_CAR: + self.shifter_values = can_define.dv["GEAR_SHIFTER"]["GEAR"] elif self.CP.carFingerprint in FEATURES["use_cluster_gears"]: self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"] elif self.CP.carFingerprint in FEATURES["use_tcu_gears"]: @@ -35,8 +35,8 @@ def __init__(self, CP): self.params = CarControllerParams(CP) def update(self, cp, cp_cam): - if self.CP.carFingerprint in HDA2_CAR: - return self.update_hda2(cp, cp_cam) + if self.CP.carFingerprint in CANFD_CAR: + return self.update_canfd(cp, cp_cam) ret = car.CarState.new_message() @@ -133,17 +133,20 @@ def update(self, cp, cp_cam): return ret - def update_hda2(self, cp, cp_cam): + def update_canfd(self, cp, cp_cam): ret = car.CarState.new_message() - ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255. - ret.gasPressed = ret.gas > 1e-3 + if self.CP.flags & HyundaiFlags.CANFD_HDA2: + ret.gas = cp.vl["ACCELERATOR"]["ACCELERATOR_PEDAL"] / 255. + else: + ret.gas = cp.vl["ACCELERATOR_ALT"]["ACCELERATOR_PEDAL"] / 1023. + ret.gasPressed = ret.gas > 1e-5 ret.brakePressed = cp.vl["BRAKE"]["BRAKE_PRESSED"] == 1 ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1 ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0 - gear = cp.vl["ACCELERATOR"]["GEAR"] + gear = cp.vl["GEAR_SHIFTER"]["GEAR"] ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) # TODO: figure out positions @@ -168,23 +171,26 @@ def update_hda2(self, cp, cp_cam): ret.cruiseState.available = True ret.cruiseState.enabled = cp.vl["SCC1"]["CRUISE_ACTIVE"] == 1 - ret.cruiseState.standstill = cp.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1 - + cp_cruise_info = cp if self.CP.flags & HyundaiFlags.CANFD_HDA2 else cp_cam speed_factor = CV.MPH_TO_MS if cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] == 1 else CV.KPH_TO_MS - ret.cruiseState.speed = cp.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor + ret.cruiseState.speed = cp_cruise_info.vl["CRUISE_INFO"]["SET_SPEED"] * speed_factor + ret.cruiseState.standstill = cp_cruise_info.vl["CRUISE_INFO"]["CRUISE_STANDSTILL"] == 1 - self.cruise_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["CRUISE_BUTTONS"]) - self.main_buttons.extend(cp.vl_all["CRUISE_BUTTONS"]["ADAPTIVE_CRUISE_MAIN_BTN"]) - self.buttons_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"] + cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS" + self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"]) + self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"]) + self.buttons_counter = cp.vl[cruise_btn_msg]["COUNTER"] + self.cruise_info_copy = copy.copy(cp_cruise_info.vl["CRUISE_INFO"]) - self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"]) + if self.CP.flags & HyundaiFlags.CANFD_HDA2: + self.cam_0x2a4 = copy.copy(cp_cam.vl["CAM_0x2a4"]) return ret @staticmethod def get_can_parser(CP): - if CP.carFingerprint in HDA2_CAR: - return CarState.get_can_parser_hda2(CP) + if CP.carFingerprint in CANFD_CAR: + return CarState.get_can_parser_canfd(CP) signals = [ # signal_name, signal_address @@ -318,10 +324,8 @@ def get_can_parser(CP): @staticmethod def get_cam_can_parser(CP): - if CP.carFingerprint in HDA2_CAR: - signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)] - checks = [("CAM_0x2a4", 20)] - return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6) + if CP.carFingerprint in CANFD_CAR: + return CarState.get_cam_can_parser_canfd(CP) signals = [ # signal_name, signal_address @@ -373,15 +377,16 @@ def get_cam_can_parser(CP): return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2) @staticmethod - def get_can_parser_hda2(CP): + def get_can_parser_canfd(CP): + + cruise_btn_msg = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS" signals = [ ("WHEEL_SPEED_1", "WHEEL_SPEEDS"), ("WHEEL_SPEED_2", "WHEEL_SPEEDS"), ("WHEEL_SPEED_3", "WHEEL_SPEEDS"), ("WHEEL_SPEED_4", "WHEEL_SPEEDS"), - ("ACCELERATOR_PEDAL", "ACCELERATOR"), - ("GEAR", "ACCELERATOR"), + ("GEAR", "GEAR_SHIFTER"), ("BRAKE_PRESSED", "BRAKE"), ("STEERING_RATE", "STEERING_SENSORS"), @@ -390,11 +395,9 @@ def get_can_parser_hda2(CP): ("STEERING_OUT_TORQUE", "MDPS"), ("CRUISE_ACTIVE", "SCC1"), - ("SET_SPEED", "CRUISE_INFO"), - ("CRUISE_STANDSTILL", "CRUISE_INFO"), - ("COUNTER", "CRUISE_BUTTONS"), - ("CRUISE_BUTTONS", "CRUISE_BUTTONS"), - ("ADAPTIVE_CRUISE_MAIN_BTN", "CRUISE_BUTTONS"), + ("COUNTER", cruise_btn_msg), + ("CRUISE_BUTTONS", cruise_btn_msg), + ("ADAPTIVE_CRUISE_MAIN_BTN", cruise_btn_msg), ("DISTANCE_UNIT", "CLUSTER_INFO"), @@ -407,16 +410,64 @@ def get_can_parser_hda2(CP): checks = [ ("WHEEL_SPEEDS", 100), - ("ACCELERATOR", 100), + ("GEAR_SHIFTER", 100), ("BRAKE", 100), ("STEERING_SENSORS", 100), ("MDPS", 100), ("SCC1", 50), - ("CRUISE_INFO", 50), - ("CRUISE_BUTTONS", 50), + (cruise_btn_msg, 50), ("CLUSTER_INFO", 4), ("BLINKERS", 4), ("DOORS_SEATBELTS", 4), ] - return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 5) + if CP.flags & HyundaiFlags.CANFD_HDA2: + signals += [ + ("ACCELERATOR_PEDAL", "ACCELERATOR"), + ("GEAR", "ACCELERATOR"), + ("SET_SPEED", "CRUISE_INFO"), + ("CRUISE_STANDSTILL", "CRUISE_INFO"), + ] + checks += [ + ("CRUISE_INFO", 50), + ("ACCELERATOR", 100), + ] + else: + signals += [ + ("ACCELERATOR_PEDAL", "ACCELERATOR_ALT"), + ] + checks += [ + ("ACCELERATOR_ALT", 100), + ] + + bus = 5 if CP.flags & HyundaiFlags.CANFD_HDA2 else 4 + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus) + + @staticmethod + def get_cam_can_parser_canfd(CP): + if CP.flags & HyundaiFlags.CANFD_HDA2: + signals = [(f"BYTE{i}", "CAM_0x2a4") for i in range(3, 24)] + checks = [("CAM_0x2a4", 20)] + else: + signals = [ + ("COUNTER", "CRUISE_INFO"), + ("NEW_SIGNAL_1", "CRUISE_INFO"), + ("CRUISE_MAIN", "CRUISE_INFO"), + ("CRUISE_STATUS", "CRUISE_INFO"), + ("CRUISE_INACTIVE", "CRUISE_INFO"), + ("NEW_SIGNAL_2", "CRUISE_INFO"), + ("CRUISE_STANDSTILL", "CRUISE_INFO"), + ("NEW_SIGNAL_3", "CRUISE_INFO"), + ("BYTE11", "CRUISE_INFO"), + ("SET_SPEED", "CRUISE_INFO"), + ("NEW_SIGNAL_4", "CRUISE_INFO"), + ] + + signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(3, 7)] + signals += [(f"BYTE{i}", "CRUISE_INFO") for i in range(13, 31)] + + checks = [ + ("CRUISE_INFO", 50), + ] + + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 6) diff --git a/selfdrive/car/hyundai/hda2can.py b/selfdrive/car/hyundai/hda2can.py deleted file mode 100644 index 36207aa113280d..00000000000000 --- a/selfdrive/car/hyundai/hda2can.py +++ /dev/null @@ -1,27 +0,0 @@ -def create_lkas(packer, enabled, lat_active, apply_steer): - values = { - "LKA_MODE": 2, - "LKA_ICON": 2 if enabled else 1, - "TORQUE_REQUEST": apply_steer, - "LKA_ASSIST": 0, - "STEER_REQ": 1 if lat_active else 0, - "STEER_MODE": 0, - "SET_ME_1": 0, - "NEW_SIGNAL_1": 0, - "NEW_SIGNAL_2": 0, - } - return packer.make_can_msg("LKAS", 4, values) - -def create_cam_0x2a4(packer, camera_values): - camera_values.update({ - "BYTE7": 0, - }) - return packer.make_can_msg("CAM_0x2a4", 4, camera_values) - -def create_buttons(packer, cnt, btn): - values = { - "COUNTER": cnt, - "SET_ME_1": 1, - "CRUISE_BUTTONS": btn, - } - return packer.make_can_msg("CRUISE_BUTTONS", 5, values) diff --git a/selfdrive/car/hyundai/hyundaicanfd.py b/selfdrive/car/hyundai/hyundaicanfd.py new file mode 100644 index 00000000000000..a53be7627db2d8 --- /dev/null +++ b/selfdrive/car/hyundai/hyundaicanfd.py @@ -0,0 +1,46 @@ +from selfdrive.car.hyundai.values import HyundaiFlags + + +def create_lkas(packer, CP, enabled, lat_active, apply_steer): + values = { + "LKA_MODE": 2, + "LKA_ICON": 2 if enabled else 1, + "TORQUE_REQUEST": apply_steer, + "LKA_ASSIST": 0, + "STEER_REQ": 1 if lat_active else 0, + "STEER_MODE": 0, + "SET_ME_1": 0, + "NEW_SIGNAL_1": 0, + "NEW_SIGNAL_2": 0, + } + + msg = "LKAS" if CP.flags & HyundaiFlags.CANFD_HDA2 else "LFA" + return packer.make_can_msg(msg, 4, values) + +def create_cam_0x2a4(packer, camera_values): + camera_values.update({ + "BYTE7": 0, + }) + return packer.make_can_msg("CAM_0x2a4", 4, camera_values) + +def create_buttons(packer, cnt, btn): + values = { + "COUNTER": cnt, + "SET_ME_1": 1, + "CRUISE_BUTTONS": btn, + } + return packer.make_can_msg("CRUISE_BUTTONS", 5, values) + +def create_cruise_info(packer, cruise_info_copy, cancel): + values = cruise_info_copy + if cancel: + values["CRUISE_STATUS"] = 0 + values["CRUISE_INACTIVE"] = 1 + return packer.make_can_msg("CRUISE_INFO", 4, values) + +def create_lfahda_cluster(packer, enabled): + values = { + "HDA_ICON": 1 if enabled else 0, + "LFA_ICON": 2 if enabled else 0, + } + return packer.make_can_msg("LFAHDA_CLUSTER", 4, values) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 892f62c4279873..fbf2334980ef44 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -2,9 +2,9 @@ from cereal import car from panda import Panda from common.conversions import Conversions as CV -from selfdrive.car.hyundai.values import CAR, DBC, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams +from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR -from selfdrive.car import STD_CARGO_KG, create_button_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from selfdrive.car import STD_CARGO_KG, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu @@ -25,7 +25,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret.carName = "hyundai" - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)] ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None # WARNING: disabling radar also disables AEB (and we show the same warning on the instrument cluster as if you manually disabled AEB) @@ -116,13 +115,11 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.indi.actuatorEffectivenessV = [2.3] ret.minSteerSpeed = 60 * CV.KPH_TO_MS elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022): - ret.lateralTuning.pid.kf = 0.00005 ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743.}.get(candidate, 1275.) + STD_CARGO_KG ret.wheelbase = 2.6 ret.steerRatio = 13.42 # Spec tire_stiffness_factor = 0.385 - ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] - ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022): ret.lateralTuning.pid.kf = 0.00006 ret.mass = 1490. + STD_CARGO_KG # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx @@ -163,6 +160,12 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl tire_stiffness_factor = 0.385 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] + elif candidate == CAR.TUCSON_HYBRID_4TH_GEN: + ret.mass = 1680. + STD_CARGO_KG # average of all 3 trims + ret.wheelbase = 2.756 + ret.steerRatio = 16. + tire_stiffness_factor = 0.385 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) # Kia elif candidate == CAR.KIA_SORENTO: @@ -172,7 +175,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] - elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021): + elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021): ret.lateralTuning.pid.kf = 0.00006 ret.mass = 1737. + STD_CARGO_KG ret.wheelbase = 2.7 @@ -180,7 +183,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl tire_stiffness_factor = 0.385 ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] - if candidate == CAR.KIA_NIRO_HEV: + if candidate == CAR.KIA_NIRO_PHEV: ret.minSteerSpeed = 32 * CV.MPH_TO_MS elif candidate == CAR.KIA_SELTOS: ret.mass = 1337. + STD_CARGO_KG @@ -238,8 +241,12 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.mass = 2055 + STD_CARGO_KG ret.wheelbase = 2.9 ret.steerRatio = 16. - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), - get_safety_config(car.CarParams.SafetyModel.hyundaiHDA2)] + tire_stiffness_factor = 0.65 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.IONIQ_5: + ret.mass = 2012 + STD_CARGO_KG + ret.wheelbase = 3.0 + ret.steerRatio = 16. tire_stiffness_factor = 0.65 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) @@ -279,15 +286,39 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.01]] - # these cars require a special panda safety mode due to missing counters and checksums in the messages - if candidate in LEGACY_SAFETY_MODE_CAR: - ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] - - # set appropriate safety param for gas signal - if candidate in HYBRID_CAR: - ret.safetyConfigs[0].safetyParam = 2 - elif candidate in EV_CAR: - ret.safetyConfigs[0].safetyParam = 1 + # panda safety config + if candidate in CANFD_CAR: + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), + get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd)] + + # detect HDA2 with LKAS message + if 0x50 in fingerprint[6]: + ret.flags |= HyundaiFlags.CANFD_HDA2.value + ret.safetyConfigs[1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2 + else: + # non-HDA2 + if 0x1cf not in fingerprint[4]: + ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value + else: + ret.enableBsm = 0x58b in fingerprint[0] + + if candidate in LEGACY_SAFETY_MODE_CAR: + # these cars require a special panda safety mode due to missing counters and checksums in the messages + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundaiLegacy)] + else: + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hyundai, 0)] + + # set appropriate safety param for gas signal + if candidate in HYBRID_CAR: + ret.safetyConfigs[0].safetyParam = 2 + elif candidate in EV_CAR: + ret.safetyConfigs[0].safetyParam = 1 + + if ret.openpilotLongitudinalControl: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LONG + + if candidate in CAMERA_SCC_CAR: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC ret.centerToFront = ret.wheelbase * 0.4 @@ -300,14 +331,6 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], disabl ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, tire_stiffness_factor=tire_stiffness_factor) - ret.enableBsm = 0x58b in fingerprint[0] - - if ret.openpilotLongitudinalControl: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LONG - - if candidate in CAMERA_SCC_CAR: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC - return ret @staticmethod @@ -318,6 +341,14 @@ def init(CP, logcan, sendcan): def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam) + if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons: + buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)] + # Handle CF_Clu_CruiseSwState changing buttons mid-press + if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0: + buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT)) + + ret.buttonEvents = buttonEvents + # On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state # To avoid re-engaging when openpilot cancels, check user engagement intention via buttons # Main button also can trigger an engagement on these cars @@ -327,15 +358,6 @@ def _update(self, c): if self.CS.brake_error: events.add(EventName.brakeUnavailable) - if self.CS.CP.openpilotLongitudinalControl and self.CS.cruise_buttons[-1] != self.CS.prev_cruise_buttons: - buttonEvents = [create_button_event(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)] - # Handle CF_Clu_CruiseSwState changing buttons mid-press - if self.CS.cruise_buttons[-1] != 0 and self.CS.prev_cruise_buttons != 0: - buttonEvents.append(create_button_event(0, self.CS.prev_cruise_buttons, BUTTONS_DICT)) - - ret.buttonEvents = buttonEvents - events.events.extend(create_button_enable_events(ret.buttonEvents)) - # low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s) if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.: self.low_speed_alert = True diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index ec38123fa1a03b..aa18235223a6eb 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -1,3 +1,4 @@ +from enum import IntFlag from dataclasses import dataclass from typing import Dict, List, Optional, Union @@ -20,7 +21,7 @@ def __init__(self, CP): self.STEER_DRIVER_FACTOR = 1 self.STEER_THRESHOLD = 150 - if CP.carFingerprint in HDA2_CAR: + if CP.carFingerprint in CANFD_CAR: self.STEER_MAX = 270 self.STEER_DRIVER_ALLOWANCE = 250 self.STEER_DRIVER_MULTIPLIER = 2 @@ -29,7 +30,7 @@ def __init__(self, CP): # To determine the limit for your car, find the maximum value that the stock LKAS will request. # If the max stock LKAS request is <384, add your car to this list. elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, - CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_HEV, + CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV, CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): self.STEER_MAX = 255 @@ -38,6 +39,11 @@ def __init__(self, CP): self.STEER_MAX = 384 +class HyundaiFlags(IntFlag): + CANFD_HDA2 = 1 + CANFD_ALT_BUTTONS = 2 + + class CAR: # Hyundai ELANTRA = "HYUNDAI ELANTRA 2017" @@ -65,12 +71,14 @@ class CAR: PALISADE = "HYUNDAI PALISADE 2020" VELOSTER = "HYUNDAI VELOSTER 2019" SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" + IONIQ_5 = "HYUNDAI IONIQ 5 2022" + TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN" # Kia KIA_FORTE = "KIA FORTE E 2018 & GT 2021" KIA_K5_2021 = "KIA K5 2021" KIA_NIRO_EV = "KIA NIRO EV 2020" - KIA_NIRO_HEV = "KIA NIRO HYBRID 2019" + KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019" KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021" KIA_OPTIMA = "KIA OPTIMA SX 2019 & 2016" KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019" @@ -111,7 +119,7 @@ class HyundaiCarInfo(CarInfo): CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", "Smart Cruise Control (SCC)", harness=Harness.hyundai_o), CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", harness=Harness.hyundai_i), CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", harness=Harness.hyundai_d), - CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", harness=Harness.hyundai_l), + CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", video_link="https://youtu.be/VnHzSTygTS4", harness=Harness.hyundai_l), CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022", "All", harness=Harness.hyundai_l), CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", harness=Harness.hyundai_l), CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-22", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a), @@ -126,6 +134,8 @@ class HyundaiCarInfo(CarInfo): ], CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", "Smart Cruise Control (SCC)", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e), CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a), + CAR.IONIQ_5: HyundaiCarInfo("Hyundai Ioniq 5 2022", "Highway Driving Assist II", harness=Harness.hyundai_q), + CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n), # Kia CAR.KIA_FORTE: [ @@ -139,7 +149,7 @@ class HyundaiCarInfo(CarInfo): HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Niro Electric 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_h), ], - CAR.KIA_NIRO_HEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c), + CAR.KIA_NIRO_PHEV: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", min_enable_speed=10. * CV.MPH_TO_MS, harness=Harness.hyundai_c), CAR.KIA_NIRO_HEV_2021: [ HyundaiCarInfo("Kia Niro Hybrid 2021", harness=Harness.hyundai_f), # TODO: could be hyundai_d, verify HyundaiCarInfo("Kia Niro Hybrid 2022", harness=Harness.hyundai_h), @@ -156,7 +166,7 @@ class HyundaiCarInfo(CarInfo): ], CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e), - CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "All", harness=Harness.hyundai_p), + CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "Highway Driving Assist II", harness=Harness.hyundai_p), # Genesis CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness=Harness.hyundai_f), @@ -611,6 +621,7 @@ class Buttons: b'\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ', b'\xf1\x8799110S1500\xf1\x00TM__ SCC F-CUP 1.00 1.00 99110-S1500 ', b'\xf1\x8799110S1500\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ', + b'\xf1\x00TM__ SCC FHCUP 1.00 1.00 99110-S1500 ', ], (Ecu.esp, 0x7d1, None): [ b'\xf1\x00TM ESC \x02 101 \x08\x04 58910-S2GA0', @@ -619,6 +630,7 @@ class Buttons: b'\xf1\x8758910-S2GA0\xf1\x00TM ESC \x02 101 \x08\x04 58910-S2GA0', b'\xf1\x8758910-S1DA0\xf1\x00TM ESC \x1e 102 \x08\x08 58910-S1DA0', b'\xf1\x8758910-S2GA0\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0', + b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x82TACVN5GMI3XXXH0A', @@ -627,6 +639,7 @@ class Buttons: b'\xf1\x82TMCFD5MMCXXXXG0A', b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82TMDWN5TMD3TXXJ1A', b'\xf1\x81HM6M2_0a0_G00', + b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M1_0a0_J10', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00TM MDPS C 1.00 1.02 56370-S2AA0 0B19', @@ -646,6 +659,8 @@ class Buttons: b'\xf1\x87KMMYBU034207SB72x\x89\x88\x98h\x88\x98\x89\x87fhvvfWf33_\xff\x87\xff\x8f\xfa\x81\xe5\xf1\x89HT6TAF00A1\xf1\x82STM0M25GS1\x00\x00\x00\x00\x00\x00', b'\xf1\x87954A02N250\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 VTMPT25XXX730NS2\xa6', b'\xf1\x00HT6TA290BLHT6TAF00A1STM0M25GS1\x00\x00\x00\x00\x00\x006\xd8\x97\x15', + b'\xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc', + b'\xf1\x87954A02N250\x00\x00\x00\x00\x00\xf1\x81T02900A1 \xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc', ], }, CAR.SANTA_FE_HEV_2022: { @@ -688,6 +703,7 @@ class Buttons: (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ', b'\xf1\x00CK__ SCC F_CUP 1.00 1.03 96400-J5100 ', + b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x81606DE051\x00\x00\x00\x00\x00\x00\x00\x00', @@ -705,6 +721,7 @@ class Buttons: (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822', b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504', + b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x87VCJLE17622572DK0vd6D\x99\x98y\x97vwVffUfvfC%CuT&Dx\x87o\xff{\x1c\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', @@ -714,6 +731,7 @@ class Buttons: b'\xf1\x87VDHLG17118862DK2\x8awWwgu\x96wVfUVwv\x97xWvfvUTGTx\x87o\xff\xc9\xed\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x87VDKLJ18675252DK6\x89vhgwwwwveVU\x88w\x87w\x99vgf\x97vXfgw_\xff\xc2\xfb\xf1\x89E25\x00\x00\x00\x00\x00\x00\x00\xf1\x82TCK0T33NB2', b'\xf1\x87WAJTE17552812CH4vfFffvfVeT5DwvvVVdFeegeg\x88\x88o\xff\x1a]\xf1\x81E21\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00TCK2T20NB1\x19\xd2\x00\x94', + b'\xf1\x87VDHLG17274082DK2wfFf\x89x\x98wUT5T\x88v\x97xgeGefTGTVvO\xff\x1c\x14\xf1\x81E19\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E19\x00\x00\x00\x00\x00\x00\x00SCK0T33UB2\xee[\x97S', ], }, CAR.PALISADE: { @@ -808,6 +826,7 @@ class Buttons: b"\xf1\x87LBLUFN622950KF36\xa8\x88\x88\x88\x87w\x87xh\x99\x96\x89\x88\x99\x98\x89\x88\x99\x98\x89\x87o\xf6\xff\x98\x88o\xffx'\xf1\x81U891\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U891\x00\x00\x00\x00\x00\x00SLX2G38NB3\xd1\xc3\xf8\xa8", b'\xf1\x87LDMVBN950669KF37\x97www\x96fffy\x99\xa7\x99\xa9\x99\xaa\x99g\x88\x96x\xb8\x8f\xf9\xffTD/\xff\xa7\xcb\xf1\x81U922\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX4G38NB5\xb9\x94\xe8\x89', b'\xf1\x87LDLVAA4478824HH1\x87wwwvfvg\x89\x99\xa8\x99w\x88\x87x\x89\x99\xa8\x99\xa6o\xfa\xfffU/\xffu\x92\xf1\x81U903\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00TON4G38NB2[v\\\xb6', + b'\xf1\x87LDMVBN871852KF37\xb9\x99\x99\x99\xa8\x88\x88\x88y\x99\xa7\x99x\x99\xa7\x89\x88\x88\x98\x88\x89o\xf7\xff\xaa\x88o\xff\x0e\xed\xf1\x81U922\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U922\x00\x00\x00\x00\x00\x00SLX4G38NB5\xb9\x94\xe8\x89', ], }, CAR.VELOSTER: { @@ -1008,6 +1027,7 @@ class Buttons: b'\xf1\x00DEev SCC F-CUP 1.00 1.02 96400-Q4000 ', b'\xf1\x00DEev SCC F-CUP 1.00 1.02 96400-Q4100 ', b'\xf1\x00DEev SCC F-CUP 1.00 1.03 96400-Q4100 ', + b'\xf1\x00DEev SCC FHCUP 1.00 1.03 96400-Q4000 ', b'\xf1\x8799110Q4000\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ', b'\xf1\x8799110Q4100\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4100 ', b'\xf1\x8799110Q4500\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4500 ', @@ -1027,9 +1047,10 @@ class Buttons: b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821', b'\xf1\x00DEE MFC AT USA LHD 1.00 1.01 99211-Q4500 210428', b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.03 95740-Q4000 180821', + b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821', ], }, - CAR.KIA_NIRO_HEV: { + CAR.KIA_NIRO_PHEV: { (Ecu.engine, 0x7e0, None): [ b'\xf1\x816H6F4051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x816H6D1051\x00\x00\x00\x00\x00\x00\x00\x00', @@ -1135,11 +1156,13 @@ class Buttons: b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-AB000 200819', b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA000 200819', b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205', + b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.06 99210-AA000 220111', ], (Ecu.esp, 0x7d1, None): [ b'\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800', b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 104 \x08\x03 58910-AA800', b'\xf1\x8758910-AB800\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800', + b'\xf1\x8758910-AA800\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x00HT6WA280BLHT6VA640A1CCN0N20NS5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', @@ -1154,7 +1177,7 @@ class Buttons: b'\xf1\x82CNCWD0AMFCXCSFFA', b'\xf1\x82CNCWD0AMFCXCSFFB', b'\xf1\x82CNCVD0AMFCXCSFFB', - b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82CNDWD0AMFCXCSG8A', + b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M2_0a0_G80', ], }, CAR.ELANTRA_HEV_2021: { @@ -1263,6 +1286,38 @@ class Buttons: b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.05 99210-CV000 211027', ], }, + CAR.IONIQ_5: { + (Ecu.esp, 0x7d1, None): [ + b'\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010', + b'\xf1\x8758520GI010\xf1\x00NE1 IEB \x07 106!\x11) 58520-GI010', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106', + b'\xf1\x8757700GI000 \xf1\x00NE MDPS R 1.00 1.06 57700GI000 4NEDR106', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ', + b'\xf1\x8799110GI000\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206', + ], + }, + CAR.TUCSON_HYBRID_4TH_GEN: { + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00NX4 MDPS C 1.00 1.01 56300-P0100 2228', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x87391312MND0', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa', + b'\xf1\x8795441-3D220\x00\xf1\x81G19_Rev\x00\x00\x00\xf1\x00PSBG2441 G19_Rev\x00\x00\x00SNX4T16XXHS01NS2lS\xdfa', + ], + }, } CHECKSUM = { @@ -1274,18 +1329,18 @@ class Buttons: # which message has the gear "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, "use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON}, - "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022}, + "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022}, # these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.ELANTRA_GT_I30, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022}, } -HDA2_CAR = {CAR.KIA_EV6, } +CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN} # The camera does SCC on these cars, rather than the radar CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } -HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_HEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal +HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019} # these cars use a different gas signal EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022} # these cars require a special panda safety mode due to missing counters and checksums in the messages @@ -1312,7 +1367,7 @@ class Buttons: CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None), CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None), CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), - CAR.KIA_NIRO_HEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), + CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None), CAR.KIA_OPTIMA: dbc_dict('hyundai_kia_generic', None), CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None), @@ -1333,6 +1388,8 @@ class Buttons: CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None), CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None), - CAR.KIA_EV6: dbc_dict('kia_ev6', None), + CAR.KIA_EV6: dbc_dict('hyundai_canfd', None), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), + CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None), + CAR.IONIQ_5: dbc_dict('hyundai_canfd', None), } diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 1e55e72ac4bec8..9da6aabd67b7cf 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -9,7 +9,7 @@ from common.conversions import Conversions as CV from common.kalman.simple_kalman import KF1D from common.realtime import DT_CTRL -from selfdrive.car import gen_empty_fingerprint +from selfdrive.car import create_button_enable_events, gen_empty_fingerprint from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.vehicle_model import VehicleModel @@ -213,6 +213,9 @@ def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_ if cs_out.accFaulted: events.add(EventName.accFaulted) + # Handle button presses + events.events.extend(create_button_enable_events(cs_out.buttonEvents, pcm_cruise=self.CP.pcmCruise)) + # Handle permanent and temporary steering faults self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1 if cs_out.steerFaultTemporary: diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py index bb96572c33e9a2..65122ab8971b9b 100644 --- a/selfdrive/car/isotp_parallel_query.py +++ b/selfdrive/car/isotp_parallel_query.py @@ -135,6 +135,7 @@ def get_data(self, timeout, total_timeout=60.): if self.debug: cloudlog.warning(f"iso-tp query response pending: {tx_addr}") else: + response_timeouts[tx_addr] = 0 request_done[tx_addr] = True cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}") diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 7db401e1617779..ed246f15da54db 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -40,7 +40,7 @@ class MazdaCarInfo(CarInfo): CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-20"), CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017-18"), CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017-20"), - CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22"), + CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021-22", video_link="https://youtu.be/dA3duO4a0O4"), CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"), } @@ -67,6 +67,7 @@ class Buttons: b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'SH54-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PXFG-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x764, None): [ b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', @@ -80,6 +81,7 @@ class Buttons: (Ecu.transmission, 0x7e1, None): [ b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + b'PXFG-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', ], }, CAR.CX5: { diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py index de5e62cb7e91ab..7fc5456d98f92f 100644 --- a/selfdrive/car/subaru/carstate.py +++ b/selfdrive/car/subaru/carstate.py @@ -4,7 +4,7 @@ from common.conversions import Conversions as CV from selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser -from selfdrive.car.subaru.values import DBC, STEER_THRESHOLD, CAR, GLOBAL_GEN2, PREGLOBAL_CARS +from selfdrive.car.subaru.values import DBC, CAR, GLOBAL_GEN2, PREGLOBAL_CARS class CarState(CarStateBase): @@ -50,7 +50,9 @@ def update(self, cp, cp_cam, cp_body): ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"] ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"] ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"] - ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD[self.car_fingerprint] + + steer_threshold = 75 if self.CP.carFingerprint in PREGLOBAL_CARS else 80 + ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold cp_cruise = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp ret.cruiseState.enabled = cp_cruise.vl["CruiseControl"]["Cruise_Activated"] != 0 diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 049da10a16b184..a464734a30b9bb 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -71,7 +71,7 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]] - elif candidate == CAR.OUTBACK: + elif candidate in (CAR.OUTBACK, CAR.LEGACY): ret.mass = 1568. + STD_CARGO_KG ret.wheelbase = 2.67 ret.centerToFront = ret.wheelbase * 0.5 diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 9465f75edf807d..7d8365a11f3435 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -35,6 +35,7 @@ class CAR: IMPREZA_2020 = "SUBARU IMPREZA SPORT 2020" FORESTER = "SUBARU FORESTER 2019" OUTBACK = "SUBARU OUTBACK 6TH GEN" + LEGACY = "SUBARU LEGACY 7TH GEN" # Pre-global FORESTER_PREGLOBAL = "SUBARU FORESTER 2017 - 2018" @@ -46,12 +47,13 @@ class CAR: @dataclass class SubaruCarInfo(CarInfo): package: str = "EyeSight Driver Assistance" - harness: Enum = Harness.subaru + harness: Enum = Harness.subaru_a CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"), - CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", harness=Harness.none), + CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", harness=Harness.subaru_b), + CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", harness=Harness.subaru_b), CAR.IMPREZA: [ SubaruCarInfo("Subaru Impreza 2017-19"), SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), @@ -99,7 +101,23 @@ class SubaruCarInfo(CarInfo): b'\x00\xfe\xf7\x00\x00', b'\001\xfe\xf9\000\000', b'\x01\xfe\xf7\x00\x00', - b'\xf1\x00\xa4\x10@', + ], + }, + CAR.LEGACY: { + (Ecu.esp, 0x7b0, None): [ + b'\xa1\\ x04\x01', + ], + (Ecu.eps, 0x746, None): [ + b'\x9b\xc0\x11\x00', + ], + (Ecu.fwdCamera, 0x787, None): [ + b'\x00\x00e\x80\x00\x1f@ \x19\x00', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xde\"a0\x07', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xa5\xf6\x05@\x00', ], }, CAR.IMPREZA: { @@ -116,6 +134,7 @@ class SubaruCarInfo(CarInfo): b'z\x94\x08\x90\x00', b'z\x84\x19\x90\x00', b'\xf1\x00\xb2\x06\x04', + b'z\x94\x0c\x90\x00', ], (Ecu.eps, 0x746, None): [ b'\x7a\xc0\x0c\x00', @@ -139,6 +158,7 @@ class SubaruCarInfo(CarInfo): b'\x00\x00c\xf4\x00\x00\x00\x00', b'\x00\x00d\xdc\x00\x00\x00\x00', b'\x00\x00dd\x00\x00\x00\x00', + b'\x00\x00c\xf4\x1f@ \x07', ], (Ecu.engine, 0x7e0, None): [ b'\xaa\x61\x66\x73\x07', @@ -148,7 +168,6 @@ class SubaruCarInfo(CarInfo): b'\xaa!`u\a', b'\xaa!dq\a', b'\xaa!dt\a', - b'\xf1\x00\xa2\x10\t', b'\xc5!ar\a', b'\xbe!as\a', b'\xc5!ds\a', @@ -158,6 +177,7 @@ class SubaruCarInfo(CarInfo): b'\xaa\x00Bu\x07', b'\xc5!dr\x07', b'\xaa!aw\x07', + b'\xaa!av\x07', ], (Ecu.transmission, 0x7e1, None): [ b'\xe3\xe5\x46\x31\x00', @@ -173,7 +193,6 @@ class SubaruCarInfo(CarInfo): b'\xe4\xf5\002\000\000', b'\xe3\xd0\x081\x00', b'\xe3\xf5\x06\x00\x00', - b'\xf1\x00\xa4\x10@', ], }, CAR.IMPREZA_2020: { @@ -202,7 +221,6 @@ class SubaruCarInfo(CarInfo): b'\xca!`0\a', b'\xcc\"f0\a', b'\xcc!fp\a', - b'\xf1\x00\xa2\x10\t', b'\xca!f@\x07', b'\xca!fp\x07', ], @@ -247,7 +265,6 @@ class SubaruCarInfo(CarInfo): b'\x1a\xf6F`\x00', b'\032\xf6b`\000', b'\x1a\xf6B`\x00', - b'\xf1\x00\xa4\x10@', b'\x1a\xf6b0\x00', ], }, @@ -450,29 +467,18 @@ class SubaruCarInfo(CarInfo): }, } -STEER_THRESHOLD = { - CAR.ASCENT: 80, - CAR.IMPREZA: 80, - CAR.IMPREZA_2020: 80, - CAR.FORESTER: 80, - CAR.OUTBACK: 80, - CAR.FORESTER_PREGLOBAL: 75, - CAR.LEGACY_PREGLOBAL: 75, - CAR.OUTBACK_PREGLOBAL: 75, - CAR.OUTBACK_PREGLOBAL_2018: 75, -} - DBC = { CAR.ASCENT: dbc_dict('subaru_global_2017_generated', None), CAR.IMPREZA: dbc_dict('subaru_global_2017_generated', None), CAR.IMPREZA_2020: dbc_dict('subaru_global_2017_generated', None), CAR.FORESTER: dbc_dict('subaru_global_2017_generated', None), CAR.OUTBACK: dbc_dict('subaru_global_2017_generated', None), + CAR.LEGACY: dbc_dict('subaru_global_2017_generated', None), CAR.FORESTER_PREGLOBAL: dbc_dict('subaru_forester_2017_generated', None), CAR.LEGACY_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None), CAR.OUTBACK_PREGLOBAL: dbc_dict('subaru_outback_2015_generated', None), CAR.OUTBACK_PREGLOBAL_2018: dbc_dict('subaru_outback_2019_generated', None), } -GLOBAL_GEN2 = (CAR.OUTBACK, ) -PREGLOBAL_CARS = (CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018) \ No newline at end of file +GLOBAL_GEN2 = (CAR.OUTBACK, CAR.LEGACY) +PREGLOBAL_CARS = (CAR.FORESTER_PREGLOBAL, CAR.LEGACY_PREGLOBAL, CAR.OUTBACK_PREGLOBAL, CAR.OUTBACK_PREGLOBAL_2018) diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 7b6355d73f7137..b769cbe5f7086a 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -26,195 +26,201 @@ HYUNDAI.KIA_OPTIMA_H, ] -TestRoute = namedtuple('TestRoute', ['route', 'car_model', 'segment'], defaults=(None,)) +CarTestRoute = namedtuple('CarTestRoute', ['route', 'car_model', 'segment'], defaults=(None,)) routes = [ - TestRoute("efdf9af95e71cd84|2022-05-13--19-03-31", COMMA.BODY), - - TestRoute("0c94aa1e1296d7c6|2021-05-05--19-48-37", CHRYSLER.JEEP_CHEROKEE), - TestRoute("91dfedae61d7bd75|2021-05-22--20-07-52", CHRYSLER.JEEP_CHEROKEE_2019), - TestRoute("420a8e183f1aed48|2020-03-05--07-15-29", CHRYSLER.PACIFICA_2017_HYBRID), - TestRoute("43a685a66291579b|2021-05-27--19-47-29", CHRYSLER.PACIFICA_2018), - TestRoute("378472f830ee7395|2021-05-28--07-38-43", CHRYSLER.PACIFICA_2018_HYBRID), - TestRoute("8190c7275a24557b|2020-01-29--08-33-58", CHRYSLER.PACIFICA_2019_HYBRID), - TestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.PACIFICA_2020), - TestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500), + CarTestRoute("efdf9af95e71cd84|2022-05-13--19-03-31", COMMA.BODY), + + CarTestRoute("0c94aa1e1296d7c6|2021-05-05--19-48-37", CHRYSLER.JEEP_CHEROKEE), + CarTestRoute("91dfedae61d7bd75|2021-05-22--20-07-52", CHRYSLER.JEEP_CHEROKEE_2019), + CarTestRoute("420a8e183f1aed48|2020-03-05--07-15-29", CHRYSLER.PACIFICA_2017_HYBRID), + CarTestRoute("43a685a66291579b|2021-05-27--19-47-29", CHRYSLER.PACIFICA_2018), + CarTestRoute("378472f830ee7395|2021-05-28--07-38-43", CHRYSLER.PACIFICA_2018_HYBRID), + CarTestRoute("8190c7275a24557b|2020-01-29--08-33-58", CHRYSLER.PACIFICA_2019_HYBRID), + CarTestRoute("3d84727705fecd04|2021-05-25--08-38-56", CHRYSLER.PACIFICA_2020), + CarTestRoute("221c253375af4ee9|2022-06-15--18-38-24", CHRYSLER.RAM_1500), + CarTestRoute("8fb5eabf914632ae|2022-08-04--17-28-53", CHRYSLER.RAM_HD, segment=6), #TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION), - TestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA), - TestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL), - TestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), - TestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), - - TestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G), - TestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC), - TestRoute("a859a044a447c2b0|2020-03-03--18-42-45", HONDA.CRV_EU), - TestRoute("68aac44ad69f838e|2021-05-18--20-40-52", HONDA.CRV), - TestRoute("14fed2e5fa0aa1a5|2021-05-25--14-59-42", HONDA.CRV_HYBRID), - TestRoute("52f3e9ae60c0d886|2021-05-23--15-59-43", HONDA.FIT), - TestRoute("2c4292a5cd10536c|2021-08-19--21-32-15", HONDA.FREED), - TestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HRV), - TestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX), - TestRoute("81722949a62ea724|2019-04-06--15-19-25", HONDA.ODYSSEY_CHN), - TestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T - TestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T - TestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs - TestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH), - TestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs - TestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G), - TestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY), - TestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL), - TestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.INSIGHT), - TestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.PILOT), - TestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PASSPORT), - TestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.CIVIC_BOSCH), - TestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX), - TestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE), - TestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E), - TestRoute("f44aa96ace22f34a|2021-12-22--06-22-31", HONDA.CIVIC_2022), - - TestRoute("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS), - TestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70), - TestRoute("6b301bf83f10aa90|2020-11-22--16-45-07", HYUNDAI.GENESIS_G80), - TestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE), - TestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022), - TestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022), - TestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022), - TestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED), - TestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA), - TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS), - TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), - TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), - TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON), - TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), - TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), - TestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019), - TestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV), - TestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020), - TestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD), - TestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ), - TestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022), - TestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA), - TestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV), - TestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11), - TestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV), - TestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER), - TestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER), - TestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6), - TestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021), - TestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), - TestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_HEV), - TestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021), - TestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE), - TestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA), - TestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021), - TestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021), - TestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID), - TestRoute("715ac05b594e9c59|2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020), - - TestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2), - TestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2), - TestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON), - TestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019), - TestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019), - TestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2), - TestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2), - TestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY), - TestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2), - TestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2), - TestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH), - TestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA), - TestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2), - TestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLAH_TSS2), - TestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.PRIUS), - TestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.RAV4), - TestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.RAV4H), - TestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2), - TestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022), - TestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4H_TSS2), - TestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022), - TestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2), - TestRoute("25057fa6a5a63dfb|2020-03-04--08-44-23", TOYOTA.LEXUS_CTH), - TestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ESH_TSS2), - TestRoute("37041c500fd30100|2020-12-30--12-17-24", TOYOTA.LEXUS_ESH), - TestRoute("32696cea52831b02|2021-11-19--18-13-30", TOYOTA.LEXUS_RC), - TestRoute("886fcd8408d570e9|2020-01-29--05-11-22", TOYOTA.LEXUS_RX), - TestRoute("886fcd8408d570e9|2020-01-29--02-18-55", TOYOTA.LEXUS_RX), - TestRoute("d27ad752e9b08d4f|2021-05-26--19-39-51", TOYOTA.LEXUS_RXH), - TestRoute("01b22eb2ed121565|2020-02-02--11-25-51", TOYOTA.LEXUS_RX_TSS2), - TestRoute("b74758c690a49668|2020-05-20--15-58-57", TOYOTA.LEXUS_RXH_TSS2), - TestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH), - TestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX), - TestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2), - TestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NXH_TSS2), - TestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2), - TestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2), - TestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER), - TestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH), - TestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA), - TestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS), - TestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2), - TestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI), - TestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR), - TestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH), - TestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), - - TestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), - TestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1), - TestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7), - TestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7), - TestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.PASSAT_MK8), - TestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.POLO_MK6), - TestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.TAOS_MK1), - TestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.TCROSS_MK1), - TestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2), - TestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2), - TestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61), - TestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1), - TestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3), - TestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1), - TestRoute("0cd0b7f7e31a3853|2021-12-03--03-12-05", VOLKSWAGEN.AUDI_Q3_MK2), - TestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1), - TestRoute("fc6b6c9a3471c846|2021-05-27--13-39-56", VOLKSWAGEN.SEAT_LEON_MK3), - TestRoute("12d6ae3057c04b0d|2021-09-15--00-04-07", VOLKSWAGEN.SKODA_KAMIQ_MK1), - TestRoute("12d6ae3057c04b0d|2021-09-04--21-21-21", VOLKSWAGEN.SKODA_KAROQ_MK1), - TestRoute("90434ff5d7c8d603|2021-03-15--12-07-31", VOLKSWAGEN.SKODA_KODIAQ_MK1), - TestRoute("66e5edc3a16459c5|2021-05-25--19-00-29", VOLKSWAGEN.SKODA_OCTAVIA_MK3), - TestRoute("026b6d18fba6417f|2021-03-26--09-17-04", VOLKSWAGEN.SKODA_SCALA_MK1), - TestRoute("b2e9858e29db492b|2021-03-26--16-58-42", VOLKSWAGEN.SKODA_SUPERB_MK3), - - TestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.ASCENT), - TestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER), - TestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA), - TestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020), - TestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3), + CarTestRoute("7cc2a8365b4dd8a9|2018-12-02--12-10-44", GM.ACADIA), + CarTestRoute("aa20e335f61ba898|2019-02-05--16-59-04", GM.BUICK_REGAL), + CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), + CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), + CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV), + CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.SILVERADO), + + CarTestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G), + CarTestRoute("a74b011b32b51b56|2020-07-26--17-09-36", HONDA.CIVIC), + CarTestRoute("a859a044a447c2b0|2020-03-03--18-42-45", HONDA.CRV_EU), + CarTestRoute("68aac44ad69f838e|2021-05-18--20-40-52", HONDA.CRV), + CarTestRoute("14fed2e5fa0aa1a5|2021-05-25--14-59-42", HONDA.CRV_HYBRID), + CarTestRoute("52f3e9ae60c0d886|2021-05-23--15-59-43", HONDA.FIT), + CarTestRoute("2c4292a5cd10536c|2021-08-19--21-32-15", HONDA.FREED), + CarTestRoute("03be5f2fd5c508d1|2020-04-19--18-44-15", HONDA.HRV), + CarTestRoute("917b074700869333|2021-05-24--20-40-20", HONDA.ACURA_ILX), + CarTestRoute("81722949a62ea724|2019-04-06--15-19-25", HONDA.ODYSSEY_CHN), + CarTestRoute("08a3deb07573f157|2020-03-06--16-11-19", HONDA.ACCORD), # 1.5T + CarTestRoute("1da5847ac2488106|2021-05-24--19-31-50", HONDA.ACCORD), # 2.0T + CarTestRoute("085ac1d942c35910|2021-03-25--20-11-15", HONDA.ACCORD), # 2021 with new style HUD msgs + CarTestRoute("07585b0da3c88459|2021-05-26--18-52-04", HONDA.ACCORDH), + CarTestRoute("f29e2b57a55e7ad5|2021-03-24--20-52-38", HONDA.ACCORDH), # 2021 with new style HUD msgs + CarTestRoute("1ad763dd22ef1a0e|2020-02-29--18-37-03", HONDA.CRV_5G), + CarTestRoute("0a96f86fcfe35964|2020-02-05--07-25-51", HONDA.ODYSSEY), + CarTestRoute("d83f36766f8012a5|2020-02-05--18-42-21", HONDA.CIVIC_BOSCH_DIESEL), + CarTestRoute("f0890d16a07a236b|2021-05-25--17-27-22", HONDA.INSIGHT), + CarTestRoute("07d37d27996096b6|2020-03-04--21-57-27", HONDA.PILOT), + CarTestRoute("684e8f96bd491a0e|2021-11-03--11-08-42", HONDA.PASSPORT), + CarTestRoute("0a78dfbacc8504ef|2020-03-04--13-29-55", HONDA.CIVIC_BOSCH), + CarTestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX), + CarTestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE), + CarTestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E), + CarTestRoute("f44aa96ace22f34a|2021-12-22--06-22-31", HONDA.CIVIC_2022), + + CarTestRoute("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS), + CarTestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70), + CarTestRoute("6b301bf83f10aa90|2020-11-22--16-45-07", HYUNDAI.GENESIS_G80), + CarTestRoute("4dbd55df87507948|2022-03-01--09-45-38", HYUNDAI.SANTA_FE), + CarTestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022), + CarTestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022), + CarTestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022, segment=1), + CarTestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED), + CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA), + CarTestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS), + CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), + CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), + CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON), + CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN), + CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), + CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), + CarTestRoute("22de8111a8c5463c|2022-07-29--13-34-49", HYUNDAI.IONIQ_5), + CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019), + CarTestRoute("fa8db5869167f821|2021-06-10--22-50-10", HYUNDAI.IONIQ_PHEV), + CarTestRoute("2c5cf2dd6102e5da|2020-12-17--16-06-44", HYUNDAI.IONIQ_EV_2020), + CarTestRoute("610ebb9faaad6b43|2020-06-13--15-28-36", HYUNDAI.IONIQ_EV_LTD), + CarTestRoute("2c5cf2dd6102e5da|2020-06-26--16-00-08", HYUNDAI.IONIQ), + CarTestRoute("ab59fe909f626921|2021-10-18--18-34-28", HYUNDAI.IONIQ_HEV_2022), + CarTestRoute("22d955b2cd499c22|2020-08-10--19-58-21", HYUNDAI.KONA), + CarTestRoute("efc48acf44b1e64d|2021-05-28--21-05-04", HYUNDAI.KONA_EV), + CarTestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11), + CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV), + CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER), + CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER), + CarTestRoute("d824e27e8c60172c|2022-05-19--16-15-28", HYUNDAI.KIA_EV6), + CarTestRoute("007d5e4ad9f86d13|2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021), + CarTestRoute("50c6c9b85fd1ff03|2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV), + CarTestRoute("173219cf50acdd7b|2021-07-05--10-27-41", HYUNDAI.KIA_NIRO_PHEV), + CarTestRoute("34a875f29f69841a|2021-07-29--13-02-09", HYUNDAI.KIA_NIRO_HEV_2021), + CarTestRoute("50a2212c41f65c7b|2021-05-24--16-22-06", HYUNDAI.KIA_FORTE), + CarTestRoute("c5ac319aa9583f83|2021-06-01--18-18-31", HYUNDAI.ELANTRA), + CarTestRoute("82e9cdd3f43bf83e|2021-05-15--02-42-51", HYUNDAI.ELANTRA_2021), + CarTestRoute("715ac05b594e9c59|2021-06-20--16-21-07", HYUNDAI.ELANTRA_HEV_2021), + CarTestRoute("7120aa90bbc3add7|2021-08-02--07-12-31", HYUNDAI.SONATA_HYBRID), + CarTestRoute("715ac05b594e9c59|2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020), + + CarTestRoute("00c829b1b7613dea|2021-06-24--09-10-10", TOYOTA.ALPHARD_TSS2), + CarTestRoute("912119ebd02c7a42|2022-03-19--07-24-50", TOYOTA.ALPHARDH_TSS2), + CarTestRoute("000cf3730200c71c|2021-05-24--10-42-05", TOYOTA.AVALON), + CarTestRoute("0bb588106852abb7|2021-05-26--12-22-01", TOYOTA.AVALON_2019), + CarTestRoute("87bef2930af86592|2021-05-30--09-40-54", TOYOTA.AVALONH_2019), + CarTestRoute("e9966711cfb04ce3|2022-01-11--07-59-43", TOYOTA.AVALON_TSS2), + CarTestRoute("eca1080a91720a54|2022-03-17--13-32-29", TOYOTA.AVALONH_TSS2), + CarTestRoute("6cdecc4728d4af37|2020-02-23--15-44-18", TOYOTA.CAMRY), + CarTestRoute("3456ad0cd7281b24|2020-12-13--17-45-56", TOYOTA.CAMRY_TSS2), + CarTestRoute("ffccc77938ddbc44|2021-01-04--16-55-41", TOYOTA.CAMRYH_TSS2), + CarTestRoute("54034823d30962f5|2021-05-24--06-37-34", TOYOTA.CAMRYH), + CarTestRoute("4e45c89c38e8ec4d|2021-05-02--02-49-28", TOYOTA.COROLLA), + CarTestRoute("5f5afb36036506e4|2019-05-14--02-09-54", TOYOTA.COROLLA_TSS2), + CarTestRoute("5ceff72287a5c86c|2019-10-19--10-59-02", TOYOTA.COROLLAH_TSS2), + CarTestRoute("d2525c22173da58b|2021-04-25--16-47-04", TOYOTA.PRIUS), + CarTestRoute("b14c5b4742e6fc85|2020-07-28--19-50-11", TOYOTA.RAV4), + CarTestRoute("32a7df20486b0f70|2020-02-06--16-06-50", TOYOTA.RAV4H), + CarTestRoute("cdf2f7de565d40ae|2019-04-25--03-53-41", TOYOTA.RAV4_TSS2), + CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022), + CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4H_TSS2), + CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022), + CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2), + CarTestRoute("da23c367491f53e2|2021-05-21--09-09-11", TOYOTA.LEXUS_CTH, segment=3), + CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ESH_TSS2), + CarTestRoute("37041c500fd30100|2020-12-30--12-17-24", TOYOTA.LEXUS_ESH), + CarTestRoute("32696cea52831b02|2021-11-19--18-13-30", TOYOTA.LEXUS_RC), + CarTestRoute("886fcd8408d570e9|2020-01-29--02-18-55", TOYOTA.LEXUS_RX), + CarTestRoute("d27ad752e9b08d4f|2021-05-26--19-39-51", TOYOTA.LEXUS_RXH), + CarTestRoute("01b22eb2ed121565|2020-02-02--11-25-51", TOYOTA.LEXUS_RX_TSS2), + CarTestRoute("b74758c690a49668|2020-05-20--15-58-57", TOYOTA.LEXUS_RXH_TSS2), + CarTestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH), + CarTestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX), + CarTestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2), + CarTestRoute("09ae96064ed85a14|2022-06-09--12-22-31", TOYOTA.LEXUS_NXH_TSS2), + CarTestRoute("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2), + CarTestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2), + CarTestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER), + CarTestRoute("80d16a262e33d57f|2021-05-23--20-01-43", TOYOTA.HIGHLANDERH), + CarTestRoute("eb6acd681135480d|2019-06-20--20-00-00", TOYOTA.SIENNA), + CarTestRoute("2e07163a1ba9a780|2019-08-25--13-15-13", TOYOTA.LEXUS_IS), + CarTestRoute("0a0de17a1e6a2d15|2020-09-21--21-24-41", TOYOTA.PRIUS_TSS2), + CarTestRoute("9b36accae406390e|2021-03-30--10-41-38", TOYOTA.MIRAI), + CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.CHR), + CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.CHRH), + CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.PRIUS_V), + + CarTestRoute("202c40641158a6e5|2021-09-21--09-43-24", VOLKSWAGEN.ARTEON_MK1), + CarTestRoute("2c68dda277d887ac|2021-05-11--15-22-20", VOLKSWAGEN.ATLAS_MK1), + CarTestRoute("cae14e88932eb364|2021-03-26--14-43-28", VOLKSWAGEN.GOLF_MK7), + CarTestRoute("58a7d3b707987d65|2021-03-25--17-26-37", VOLKSWAGEN.JETTA_MK7), + CarTestRoute("4d134e099430fba2|2021-03-26--00-26-06", VOLKSWAGEN.PASSAT_MK8), + CarTestRoute("3cfdec54aa035f3f|2022-07-19--23-45-10", VOLKSWAGEN.PASSAT_NMS), + CarTestRoute("0cd0b7f7e31a3853|2021-11-03--19-30-22", VOLKSWAGEN.POLO_MK6), + CarTestRoute("7d82b2f3a9115f1f|2021-10-21--15-39-42", VOLKSWAGEN.TAOS_MK1), + CarTestRoute("2744c89a8dda9a51|2021-07-24--21-28-06", VOLKSWAGEN.TCROSS_MK1), + CarTestRoute("2cef8a0b898f331a|2021-03-25--20-13-57", VOLKSWAGEN.TIGUAN_MK2), + CarTestRoute("a589dcc642fdb10a|2021-06-14--20-54-26", VOLKSWAGEN.TOURAN_MK2), + CarTestRoute("a459f4556782eba1|2021-09-19--09-48-00", VOLKSWAGEN.TRANSPORTER_T61), + CarTestRoute("0cd0b7f7e31a3853|2021-11-18--00-38-32", VOLKSWAGEN.TROC_MK1), + CarTestRoute("07667b885add75fd|2021-01-23--19-48-42", VOLKSWAGEN.AUDI_A3_MK3), + CarTestRoute("6c6b466346192818|2021-06-06--14-17-47", VOLKSWAGEN.AUDI_Q2_MK1), + CarTestRoute("0cd0b7f7e31a3853|2021-12-03--03-12-05", VOLKSWAGEN.AUDI_Q3_MK2), + CarTestRoute("8f205bdd11bcbb65|2021-03-26--01-00-17", VOLKSWAGEN.SEAT_ATECA_MK1), + CarTestRoute("fc6b6c9a3471c846|2021-05-27--13-39-56", VOLKSWAGEN.SEAT_LEON_MK3), + CarTestRoute("12d6ae3057c04b0d|2021-09-15--00-04-07", VOLKSWAGEN.SKODA_KAMIQ_MK1), + CarTestRoute("12d6ae3057c04b0d|2021-09-04--21-21-21", VOLKSWAGEN.SKODA_KAROQ_MK1), + CarTestRoute("90434ff5d7c8d603|2021-03-15--12-07-31", VOLKSWAGEN.SKODA_KODIAQ_MK1), + CarTestRoute("66e5edc3a16459c5|2021-05-25--19-00-29", VOLKSWAGEN.SKODA_OCTAVIA_MK3), + CarTestRoute("026b6d18fba6417f|2021-03-26--09-17-04", VOLKSWAGEN.SKODA_SCALA_MK1), + CarTestRoute("b2e9858e29db492b|2021-03-26--16-58-42", VOLKSWAGEN.SKODA_SUPERB_MK3), + + CarTestRoute("3c8f0c502e119c1c|2020-06-30--12-58-02", SUBARU.ASCENT), + CarTestRoute("c321c6b697c5a5ff|2020-06-23--11-04-33", SUBARU.FORESTER), + CarTestRoute("791340bc01ed993d|2019-03-10--16-28-08", SUBARU.IMPREZA), + CarTestRoute("8bf7e79a3ce64055|2021-05-24--09-36-27", SUBARU.IMPREZA_2020), + CarTestRoute("1bbe6bf2d62f58a8|2022-07-14--17-11-43", SUBARU.OUTBACK, segment=3), + CarTestRoute("c56e69bbc74b8fad|2022-08-18--09-43-51", SUBARU.LEGACY, segment=3), # Pre-global, dashcam - TestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL), - TestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL), - TestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL), - TestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018), - - TestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL), - TestRoute("5b7c365c50084530|2020-03-25--22-10-13", NISSAN.LEAF), - TestRoute("22c3dcce2dd627eb|2020-12-30--16-38-48", NISSAN.LEAF_IC), - TestRoute("059ab9162e23198e|2020-05-30--09-41-01", NISSAN.ROGUE), - TestRoute("b72d3ec617c0a90f|2020-12-11--15-38-17", NISSAN.ALTIMA), - - TestRoute("32a319f057902bb3|2020-04-27--15-18-58", MAZDA.CX5), - TestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9), - TestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3), - TestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6), - TestRoute("f6d5b1a9d7a1c92e|2021-07-08--06-56-59", MAZDA.CX9_2021), - TestRoute("a4af1602d8e668ac|2022-02-03--12-17-07", MAZDA.CX5_2022), - - TestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS), - TestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS), + CarTestRoute("95441c38ae8c130e|2020-06-08--12-10-17", SUBARU.FORESTER_PREGLOBAL), + CarTestRoute("df5ca7660000fba8|2020-06-16--17-37-19", SUBARU.LEGACY_PREGLOBAL), + CarTestRoute("5ab784f361e19b78|2020-06-08--16-30-41", SUBARU.OUTBACK_PREGLOBAL), + CarTestRoute("e19eb5d5353b1ac1|2020-08-09--14-37-56", SUBARU.OUTBACK_PREGLOBAL_2018), + + CarTestRoute("fbbfa6af821552b9|2020-03-03--08-09-43", NISSAN.XTRAIL), + CarTestRoute("5b7c365c50084530|2020-03-25--22-10-13", NISSAN.LEAF), + CarTestRoute("22c3dcce2dd627eb|2020-12-30--16-38-48", NISSAN.LEAF_IC), + CarTestRoute("059ab9162e23198e|2020-05-30--09-41-01", NISSAN.ROGUE), + CarTestRoute("b72d3ec617c0a90f|2020-12-11--15-38-17", NISSAN.ALTIMA), + + CarTestRoute("32a319f057902bb3|2020-04-27--15-18-58", MAZDA.CX5), + CarTestRoute("10b5a4b380434151|2020-08-26--17-11-45", MAZDA.CX9), + CarTestRoute("74f1038827005090|2020-08-26--20-05-50", MAZDA.MAZDA3), + CarTestRoute("fb53c640f499b73d|2021-06-01--04-17-56", MAZDA.MAZDA6), + CarTestRoute("f6d5b1a9d7a1c92e|2021-07-08--06-56-59", MAZDA.CX9_2021), + CarTestRoute("a4af1602d8e668ac|2022-02-03--12-17-07", MAZDA.CX5_2022), + + CarTestRoute("6c14ee12b74823ce|2021-06-30--11-49-02", TESLA.AP1_MODELS), + CarTestRoute("bb50caf5f0945ab1|2021-06-19--17-20-18", TESLA.AP2_MODELS), # Segments that test specific issues # Controls mismatch due to interceptor threshold - TestRoute("cfb32f0fb91b173b|2022-04-06--14-54-45", HONDA.CIVIC, segment=21), - TestRoute("5a8762b91fc70467|2022-04-14--21-26-20", TOYOTA.RAV4, segment=2), + CarTestRoute("cfb32f0fb91b173b|2022-04-06--14-54-45", HONDA.CIVIC, segment=21), + CarTestRoute("5a8762b91fc70467|2022-04-14--21-26-20", TOYOTA.RAV4, segment=2), # Controls mismatch due to standstill threshold - TestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.CRV_HYBRID, segment=22), + CarTestRoute("bec2dcfde6a64235|2022-04-08--14-21-32", HONDA.CRV_HYBRID, segment=22), ] diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 412874c813e661..aabf652c8ccfce 100755 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -5,6 +5,7 @@ from parameterized import parameterized from cereal import car +from selfdrive.car import gen_empty_fingerprint from selfdrive.car.fingerprints import all_known_cars from selfdrive.car.car_helpers import interfaces from selfdrive.car.fingerprints import _FINGERPRINTS as FINGERPRINTS @@ -19,11 +20,8 @@ def test_car_interfaces(self, car_name): fingerprint = {} CarInterface, CarController, CarState = interfaces[car_name] - fingerprints = { - 0: fingerprint, - 1: fingerprint, - 2: fingerprint, - } + fingerprints = gen_empty_fingerprint() + fingerprints.update({k: fingerprint for k in fingerprints.keys()}) car_fw = [] diff --git a/selfdrive/car/tests/test_docs.py b/selfdrive/car/tests/test_docs.py index e31ad0d5c1325c..af58bb5e5931fd 100755 --- a/selfdrive/car/tests/test_docs.py +++ b/selfdrive/car/tests/test_docs.py @@ -4,7 +4,7 @@ from selfdrive.car.car_helpers import interfaces, get_interface_attr from selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info -from selfdrive.car.docs_definitions import Column, Star +from selfdrive.car.docs_definitions import Column, Harness, Star from selfdrive.car.honda.values import CAR as HONDA @@ -13,7 +13,7 @@ def setUp(self): self.all_cars = get_all_car_info() def test_generator(self): - generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE, False) + generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE) with open(CARS_MD_OUT, "r") as f: current_cars_md = f.read() @@ -56,6 +56,14 @@ def test_year_format(self): with self.subTest(car=car): self.assertIsNone(re.search(r"\d{4}-\d{4}", car.name), f"Format years correctly: {car.name}") + def test_harnesses(self): + for car in self.all_cars: + with self.subTest(car=car): + if car.name == "comma body": + raise unittest.SkipTest + + self.assertNotIn(car.harness, [None, Harness.none], f"Need to specify car harness: {car.name}") + if __name__ == "__main__": unittest.main() diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py index 929c539842bca1..502df3fe3f1524 100755 --- a/selfdrive/car/tests/test_models.py +++ b/selfdrive/car/tests/test_models.py @@ -15,7 +15,7 @@ from selfdrive.car.gm.values import CAR as GM from selfdrive.car.honda.values import CAR as HONDA, HONDA_BOSCH from selfdrive.car.hyundai.values import CAR as HYUNDAI -from selfdrive.car.tests.routes import non_tested_cars, routes, TestRoute +from selfdrive.car.tests.routes import non_tested_cars, routes, CarTestRoute from selfdrive.test.openpilotci import get_url from tools.lib.logreader import LogReader from tools.lib.route import Route @@ -38,7 +38,7 @@ for r in routes: routes_by_car[r.car_model].add(r) -test_cases: List[Tuple[str, Optional[TestRoute]]] = [] +test_cases: List[Tuple[str, Optional[CarTestRoute]]] = [] for i, c in enumerate(sorted(all_known_cars())): if i % NUM_JOBS == JOB_ID: test_cases.extend((c, r) for r in routes_by_car.get(c, (None, ))) @@ -58,7 +58,6 @@ def setUpClass(cls): raise unittest.SkipTest if 'FILTER' in os.environ: - print(tuple(os.environ.get('FILTER').split(', '))) if not cls.car_model.startswith(tuple(os.environ.get('FILTER').split(','))): raise unittest.SkipTest @@ -82,6 +81,7 @@ def setUpClass(cls): except Exception: continue + car_fw = [] can_msgs = [] fingerprint = defaultdict(dict) for msg in lr: @@ -91,6 +91,7 @@ def setUpClass(cls): fingerprint[m.src][m.address] = len(m.dat) can_msgs.append(msg) elif msg.which() == "carParams": + car_fw = msg.carParams.carFw if msg.carParams.openpilotLongitudinalControl: disable_radar = True if cls.car_model is None and not cls.ci: @@ -104,7 +105,7 @@ def setUpClass(cls): cls.can_msgs = sorted(can_msgs, key=lambda msg: msg.logMonoTime) cls.CarInterface, cls.CarController, cls.CarState = interfaces[cls.car_model] - cls.CP = cls.CarInterface.get_params(cls.car_model, fingerprint, [], disable_radar) + cls.CP = cls.CarInterface.get_params(cls.car_model, fingerprint, car_fw, disable_radar) assert cls.CP assert cls.CP.carFingerprint == cls.car_model @@ -139,19 +140,23 @@ def test_car_params(self): raise Exception("unkown tuning") def test_car_interface(self): - # TODO: also check for checkusm and counter violations from can parser + # TODO: also check for checksum violations from can parser can_invalid_cnt = 0 + can_valid = False CC = car.CarControl.new_message() for i, msg in enumerate(self.can_msgs): CS = self.CI.update(CC, (msg.as_builder().to_bytes(),)) self.CI.apply(CC) - # wait 2s for low frequency msgs to be seen - if i > 200: + if CS.canValid: + can_valid = True + + # wait max of 2s for low frequency msgs to be seen + if i > 200 or can_valid: can_invalid_cnt += not CS.canValid - self.assertLess(can_invalid_cnt, 50) + self.assertEqual(can_invalid_cnt, 0) def test_radar_interface(self): os.environ['NO_RADAR_SLEEP'] = "1" @@ -219,6 +224,8 @@ def test_panda_safety_carstate(self): for can in self.can_msgs: CS = self.CI.update(CC, (can.as_builder().to_bytes(), )) for msg in can_capnp_to_can_list(can.can, src_filter=range(64)): + msg = list(msg) + msg[3] %= 4 to_send = package_can_msg(msg) ret = self.safety.safety_rx_hook(to_send) self.assertEqual(1, ret, f"safety rx failed ({ret=}): {to_send}") diff --git a/selfdrive/car/torque_data/override.yaml b/selfdrive/car/torque_data/override.yaml index 27a8c26fd65b6b..14ee7e223962f8 100644 --- a/selfdrive/car/torque_data/override.yaml +++ b/selfdrive/car/torque_data/override.yaml @@ -22,7 +22,12 @@ COMMA BODY: [.nan, 1000, .nan] # Totally new cars KIA EV6 2022: [3.5, 2.5, 0.0] RAM 1500 5TH GEN: [2.0, 2.0, 0.0] +RAM HD 5TH GEN: [1.4, 1.4, 0.0] SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11] +CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05] +CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112] +VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1] +HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0] # Dashcam or fallback configured as ideal car mock: [10.0, 10, 0.0] diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index f7288ec731cf46..de64a5544c111f 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -29,6 +29,7 @@ HYUNDAI I30 N LINE 2019 & GT 2018 DCT: HYUNDAI SONATA 2019 HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019 +HYUNDAI IONIQ 5 2022: KIA EV6 2022 HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019 @@ -68,6 +69,8 @@ VOLKSWAGEN POLO 6TH GEN: VOLKSWAGEN GOLF 7TH GEN SEAT LEON 3RD GEN: VOLKSWAGEN GOLF 7TH GEN SEAT ATECA 1ST GEN: VOLKSWAGEN GOLF 7TH GEN +SUBARU LEGACY 7TH GEN: SUBARU OUTBACK 6TH GEN + # Old subarus don't have much data guessing it's like low torque impreza SUBARU OUTBACK 2018 - 2019: SUBARU IMPREZA LIMITED 2019 SUBARU OUTBACK 2015 - 2017: SUBARU IMPREZA LIMITED 2019 diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 281d01026f3a40..843e7806dc9bba 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -94,12 +94,11 @@ def update(self, cp, cp_cam): ret.cruiseState.available = cp.vl["PCM_CRUISE_2"]["MAIN_ON"] != 0 ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]["SET_SPEED"] * CV.KPH_TO_MS - if self.CP.carFingerprint in RADAR_ACC_CAR: - self.acc_type = cp.vl["ACC_CONTROL"]["ACC_TYPE"] - ret.stockFcw = bool(cp.vl["ACC_HUD"]["FCW"]) - elif self.CP.carFingerprint in TSS2_CAR: - self.acc_type = cp_cam.vl["ACC_CONTROL"]["ACC_TYPE"] - ret.stockFcw = bool(cp_cam.vl["ACC_HUD"]["FCW"]) + cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp + + if self.CP.carFingerprint in (TSS2_CAR | RADAR_ACC_CAR): + self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"] + ret.stockFcw = bool(cp_acc.vl["ACC_HUD"]["FCW"]) # some TSS2 cars have low speed lockout permanently set, so ignore on those cars # these cars are identified by an ACC_TYPE value of 2. @@ -120,10 +119,11 @@ def update(self, cp, cp_cam): ret.cruiseState.nonAdaptive = cp.vl["PCM_CRUISE"]["CRUISE_STATE"] in (1, 2, 3, 4, 5, 6) ret.genericToggle = bool(cp.vl["LIGHT_STALK"]["AUTO_HIGH_BEAM"]) - ret.stockAeb = bool(cp_cam.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_cam.vl["PRE_COLLISION"]["FORCE"] < -1e-5) - ret.espDisabled = cp.vl["ESP_CONTROL"]["TC_DISABLED"] != 0 + if not self.CP.enableDsu: + ret.stockAeb = bool(cp_acc.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_acc.vl["PRE_COLLISION"]["FORCE"] < -1e-5) + if self.CP.enableBsm: ret.leftBlindspot = (cp.vl["BSM"]["L_ADJACENT"] == 1) or (cp.vl["BSM"]["L_APPROACHING"] == 1) ret.rightBlindspot = (cp.vl["BSM"]["R_ADJACENT"] == 1) or (cp.vl["BSM"]["R_APPROACHING"] == 1) @@ -219,27 +219,31 @@ def get_can_parser(CP): ("ACC_HUD", 1), ] + if CP.carFingerprint not in (TSS2_CAR - RADAR_ACC_CAR) and not CP.enableDsu: + signals += [ + ("FORCE", "PRE_COLLISION"), + ("PRECOLLISION_ACTIVE", "PRE_COLLISION"), + ] + checks += [ + ("PRE_COLLISION", 33), + ] + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 0) @staticmethod def get_cam_can_parser(CP): - signals = [ - ("FORCE", "PRE_COLLISION"), - ("PRECOLLISION_ACTIVE", "PRE_COLLISION"), - ] - - # use steering message to check if panda is connected to frc - checks = [ - ("STEERING_LKA", 42), - ("PRE_COLLISION", 0), # TODO: figure out why freq is inconsistent - ] + signals = [] + checks = [] if CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR): signals += [ + ("PRECOLLISION_ACTIVE", "PRE_COLLISION"), + ("FORCE", "PRE_COLLISION"), ("ACC_TYPE", "ACC_CONTROL"), ("FCW", "ACC_HUD"), ] checks += [ + ("PRE_COLLISION", 33), ("ACC_CONTROL", 33), ("ACC_HUD", 1), ] diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 9457eaf68b941d..7b4031ca64ebcc 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -6,7 +6,7 @@ from cereal import car from common.conversions import Conversions as CV from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, Star +from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness Ecu = car.CarParams.Ecu MIN_ACC_SPEED = 19. * CV.MPH_TO_MS @@ -89,7 +89,7 @@ class Footnote(Enum): DSU = CarFootnote( "When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace " + "stock Adaptive Cruise Control (ACC). NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).", - Column.LONGITUDINAL, star=Star.HALF) + Column.LONGITUDINAL) CAMRY = CarFootnote( "openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.", Column.FSR_LONGITUDINAL) @@ -114,7 +114,7 @@ class ToyotaCarInfo(CarInfo): CAR.AVALON_TSS2: ToyotaCarInfo("Toyota Avalon 2022"), CAR.AVALONH_TSS2: ToyotaCarInfo("Toyota Avalon Hybrid 2022"), CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), - CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk", footnotes=[Footnote.CAMRY]), + CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]), CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-22"), CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-21"), @@ -170,7 +170,7 @@ class ToyotaCarInfo(CarInfo): CAR.LEXUS_NX_TSS2: ToyotaCarInfo("Lexus NX 2020-21"), CAR.LEXUS_NXH_TSS2: ToyotaCarInfo("Lexus NX Hybrid 2020-21"), CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2017-20"), - CAR.LEXUS_RX: ToyotaCarInfo("Lexus RX 2016-18", footnotes=[Footnote.DSU]), + CAR.LEXUS_RX: ToyotaCarInfo("Lexus RX 2016-19", footnotes=[Footnote.DSU]), CAR.LEXUS_RXH: ToyotaCarInfo("Lexus RX Hybrid 2016-19", footnotes=[Footnote.DSU]), CAR.LEXUS_RX_TSS2: ToyotaCarInfo("Lexus RX 2020-22"), CAR.LEXUS_RXH_TSS2: ToyotaCarInfo("Lexus RX Hybrid 2020-21"), @@ -687,6 +687,7 @@ class ToyotaCarInfo(CarInfo): b'\x01896630ZP2000\x00\x00\x00\x00', b'\x01896630ZQ5000\x00\x00\x00\x00', b'\x01896630ZU9000\x00\x00\x00\x00', + b'\x01896630ZX4000\x00\x00\x00\x00', b'\x018966312L8000\x00\x00\x00\x00', b'\x018966312M0000\x00\x00\x00\x00', b'\x018966312M9000\x00\x00\x00\x00', @@ -782,6 +783,7 @@ class ToyotaCarInfo(CarInfo): b'\x01896637626000\x00\x00\x00\x00', b'\x01896637648000\x00\x00\x00\x00', b'\x01896637643000\x00\x00\x00\x00', + b'\x02896630A21000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZJ5000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZN8000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630ZQ3000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', @@ -826,6 +828,7 @@ class ToyotaCarInfo(CarInfo): b'F152612A10\x00\x00\x00\x00\x00\x00', b'F152612D00\x00\x00\x00\x00\x00\x00', b'F152616011\x00\x00\x00\x00\x00\x00', + b'F152616060\x00\x00\x00\x00\x00\x00', b'F152642540\x00\x00\x00\x00\x00\x00', b'F152676293\x00\x00\x00\x00\x00\x00', b'F152676303\x00\x00\x00\x00\x00\x00', @@ -1823,15 +1826,18 @@ class ToyotaCarInfo(CarInfo): b'\x02348Y3000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0234D14000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0234D16000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', + b'\x02348X4000\x00\x00\x00\x00\x00\x00\x00\x00A4802000\x00\x00\x00\x00\x00\x00\x00\x00', ], (Ecu.esp, 0x7b0, None): [ b'F152648831\x00\x00\x00\x00\x00\x00', b'F152648891\x00\x00\x00\x00\x00\x00', b'F152648D00\x00\x00\x00\x00\x00\x00', b'F152648D60\x00\x00\x00\x00\x00\x00', + b'F152648811\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B48271\x00\x00\x00\x00\x00\x00', + b'8965B48261\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'\x018821F3301400\x00\x00\x00\x00', diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index 99a3ce55f4621b..f14c119892c95a 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -1,8 +1,10 @@ from cereal import car from opendbc.can.packer import CANPacker +from common.numpy_fast import clip +from common.conversions import Conversions as CV from selfdrive.car import apply_std_steer_torque_limits -from selfdrive.car.volkswagen import volkswagencan -from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, MQB_LDW_MESSAGES, CarControllerParams as P +from selfdrive.car.volkswagen import mqbcan, pqcan +from selfdrive.car.volkswagen.values import CANBUS, PQ_CARS, CarControllerParams VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -10,11 +12,12 @@ class CarController: def __init__(self, dbc_name, CP, VM): self.CP = CP + self.CCP = CarControllerParams(CP) + self.CCS = pqcan if CP.carFingerprint in PQ_CARS else mqbcan + self.packer_pt = CANPacker(dbc_name) + self.apply_steer_last = 0 self.frame = 0 - - self.packer_pt = CANPacker(DBC_FILES.mqb) - self.hcaSameTorqueCount = 0 self.hcaEnabledFrameCount = 0 @@ -26,7 +29,7 @@ def update(self, CC, CS, ext_bus): # **** Steering Controls ************************************************ # - if self.frame % P.HCA_STEP == 0: + if self.frame % self.CCP.HCA_STEP == 0: # Logic to avoid HCA state 4 "refused": # * Don't steer unless HCA is in state 3 "ready" or 5 "active" # * Don't steer at standstill @@ -38,21 +41,21 @@ def update(self, CC, CS, ext_bus): # when exceeding ~1/3 the 360 second timer. if CC.latActive: - new_steer = int(round(actuators.steer * P.STEER_MAX)) - apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, P) + new_steer = int(round(actuators.steer * self.CCP.STEER_MAX)) + apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.CCP) if apply_steer == 0: hcaEnabled = False self.hcaEnabledFrameCount = 0 else: self.hcaEnabledFrameCount += 1 - if self.hcaEnabledFrameCount >= 118 * (100 / P.HCA_STEP): # 118s + if self.hcaEnabledFrameCount >= 118 * (100 / self.CCP.HCA_STEP): # 118s hcaEnabled = False self.hcaEnabledFrameCount = 0 else: hcaEnabled = True if self.apply_steer_last == apply_steer: self.hcaSameTorqueCount += 1 - if self.hcaSameTorqueCount > 1.9 * (100 / P.HCA_STEP): # 1.9s + if self.hcaSameTorqueCount > 1.9 * (100 / self.CCP.HCA_STEP): # 1.9s apply_steer -= (1, -1)[apply_steer < 0] self.hcaSameTorqueCount = 0 else: @@ -62,32 +65,41 @@ def update(self, CC, CS, ext_bus): apply_steer = 0 self.apply_steer_last = apply_steer - can_sends.append(volkswagencan.create_mqb_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled)) + can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_steer, hcaEnabled)) + + # **** Acceleration Controls ******************************************** # + + if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl: + tsk_status = self.CCS.tsk_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive) + accel = clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.longActive else 0 + can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, tsk_status, accel)) # **** HUD Controls ***************************************************** # - if self.frame % P.LDW_STEP == 0: + if self.frame % self.CCP.LDW_STEP == 0: + hud_alert = 0 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw): - hud_alert = MQB_LDW_MESSAGES["laneAssistTakeOverSilent"] - else: - hud_alert = MQB_LDW_MESSAGES["none"] + hud_alert = self.CCP.LDW_MESSAGES["laneAssistTakeOver"] + can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, CANBUS.pt, CS.ldw_stock_values, CC.enabled, + CS.out.steeringPressed, hud_alert, hud_control)) - can_sends.append(volkswagencan.create_mqb_hud_control(self.packer_pt, CANBUS.pt, CC.enabled, - CS.out.steeringPressed, hud_alert, hud_control.leftLaneVisible, - hud_control.rightLaneVisible, CS.ldw_stock_values, - hud_control.leftLaneDepart, hud_control.rightLaneDepart)) + if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl: + acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive) + set_speed = hud_control.setSpeed * CV.MS_TO_KPH # FIXME: follow the recent displayed-speed updates, also use mph_kmh toggle to fix display rounding problem? + can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, CANBUS.pt, acc_hud_status, set_speed, + hud_control.leadVisible)) - # **** ACC Button Controls ********************************************** # + # **** Stock ACC Button Controls **************************************** # - if self.CP.pcmCruise and self.frame % P.GRA_ACC_STEP == 0: + if self.CP.pcmCruise and self.frame % self.CCP.GRA_ACC_STEP == 0: idx = (CS.gra_stock_values["COUNTER"] + 1) % 16 if CC.cruiseControl.cancel: - can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True)) + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, cancel=True)) elif CC.cruiseControl.resume: - can_sends.append(volkswagencan.create_mqb_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True)) + can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, ext_bus, CS.gra_stock_values, idx, resume=True)) new_actuators = actuators.copy() - new_actuators.steer = self.apply_steer_last / P.STEER_MAX + new_actuators.steer = self.apply_steer_last / self.CCP.STEER_MAX self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/volkswagen/carstate.py b/selfdrive/car/volkswagen/carstate.py index 052a1262e43d7c..facc740a153a15 100644 --- a/selfdrive/car/volkswagen/carstate.py +++ b/selfdrive/car/volkswagen/carstate.py @@ -3,22 +3,34 @@ from common.conversions import Conversions as CV from selfdrive.car.interfaces import CarStateBase from opendbc.can.parser import CANParser -from opendbc.can.can_define import CANDefine -from selfdrive.car.volkswagen.values import DBC_FILES, CANBUS, NetworkLocation, TransmissionType, GearShifter, \ - CarControllerParams, MQB_BUTTONS +from selfdrive.car.volkswagen.values import DBC, CANBUS, PQ_CARS, NetworkLocation, TransmissionType, GearShifter, \ + CarControllerParams + class CarState(CarStateBase): def __init__(self, CP): super().__init__(CP) - self.button_states = {button.event_type: False for button in MQB_BUTTONS} - can_define = CANDefine(DBC_FILES.mqb) - if CP.transmissionType == TransmissionType.automatic: - self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"] - elif CP.transmissionType == TransmissionType.direct: - self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"] - self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"] + self.CCP = CarControllerParams(CP) + self.button_states = {button.event_type: False for button in self.CCP.BUTTONS} + + def create_button_events(self, pt_cp, buttons): + button_events = [] + + for button in buttons: + state = pt_cp.vl[button.can_addr][button.can_msg] in button.values + if self.button_states[button.event_type] != state: + event = car.CarState.ButtonEvent.new_message() + event.type = button.event_type + event.pressed = state + button_events.append(event) + self.button_states[button.event_type] = state + + return button_events def update(self, pt_cp, cam_cp, ext_cp, trans_type): + if self.CP.carFingerprint in PQ_CARS: + return self.update_pq(pt_cp, cam_cp, ext_cp, trans_type) + ret = car.CarState.new_message() # Update vehicle speed and acceleration from ABS wheel speeds. ret.wheelSpeeds = self.get_wheel_speeds( @@ -37,11 +49,11 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type): ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])] ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])] ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])] - ret.steeringPressed = abs(ret.steeringTorque) > CarControllerParams.STEER_DRIVER_ALLOWANCE + ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD # Verify EPS readiness to accept steering commands - hca_status = self.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"]) + hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"]) ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT") ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED") @@ -54,9 +66,9 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type): # Update gear and/or clutch position data. if trans_type == TransmissionType.automatic: - ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None)) + ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None)) elif trans_type == TransmissionType.direct: - ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None)) + ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["EV_Gearshift"]["GearPosition"], None)) elif trans_type == TransmissionType.manual: ret.clutchPressed = not pt_cp.vl["Motor_14"]["MO_Kuppl_schalter"] if bool(pt_cp.vl["Gateway_72"]["BCM1_Rueckfahrlicht_Schalter"]): @@ -118,25 +130,119 @@ def update(self, pt_cp, cam_cp, ext_cp, trans_type): # Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough ret.leftBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Left"]) ret.rightBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Right"]) + ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS) self.gra_stock_values = pt_cp.vl["GRA_ACC_01"] - buttonEvents = [] - for button in MQB_BUTTONS: - state = pt_cp.vl[button.can_addr][button.can_msg] in button.values - if self.button_states[button.event_type] != state: - event = car.CarState.ButtonEvent.new_message() - event.type = button.event_type - event.pressed = state - buttonEvents.append(event) - self.button_states[button.event_type] = state - ret.buttonEvents = buttonEvents # Additional safety checks performed in CarInterface. ret.espDisabled = pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"] != 0 return ret + def update_pq(self, pt_cp, cam_cp, ext_cp, trans_type): + ret = car.CarState.new_message() + # Update vehicle speed and acceleration from ABS wheel speeds. + ret.wheelSpeeds = self.get_wheel_speeds( + pt_cp.vl["Bremse_3"]["Radgeschw__VL_4_1"], + pt_cp.vl["Bremse_3"]["Radgeschw__VR_4_1"], + pt_cp.vl["Bremse_3"]["Radgeschw__HL_4_1"], + pt_cp.vl["Bremse_3"]["Radgeschw__HR_4_1"], + ) + + # vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF + ret.vEgoRaw = pt_cp.vl["Bremse_1"]["Geschwindigkeit_neu__Bremse_1_"] * CV.KPH_TO_MS + ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) + ret.standstill = ret.vEgo < 0.1 + + # Update steering angle, rate, yaw rate, and driver input torque. VW send + # the sign/direction in a separate signal so they must be recombined. + ret.steeringAngleDeg = pt_cp.vl["Lenkhilfe_3"]["LH3_BLW"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_BLWSign"])] + ret.steeringRateDeg = pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit_S"])] + ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])] + ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE + ret.yawRate = pt_cp.vl["Bremse_5"]["Giergeschwindigkeit"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["Vorzeichen_der_Giergeschwindigk"])] * CV.DEG_TO_RAD + + # Verify EPS readiness to accept steering commands + hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"]) + ret.steerFaultPermanent = hca_status in ("DISABLED", "FAULT") + ret.steerFaultTemporary = hca_status in ("INITIALIZING", "REJECTED") + + # Update gas, brakes, and gearshift. + ret.gas = pt_cp.vl["Motor_3"]["Fahrpedal_Rohsignal"] / 100.0 + ret.gasPressed = ret.gas > 0 + ret.brake = pt_cp.vl["Bremse_5"]["Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects + ret.brakePressed = bool(pt_cp.vl["Motor_2"]["Bremstestschalter"]) + ret.parkingBrake = bool(pt_cp.vl["Kombi_1"]["Bremsinfo"]) + + # Update gear and/or clutch position data. + if trans_type == TransmissionType.automatic: + ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"], None)) + elif trans_type == TransmissionType.manual: + ret.clutchPressed = not pt_cp.vl["Motor_1"]["Kupplungsschalter"] + reverse_light = bool(pt_cp.vl["Gate_Komf_1"]["GK1_Rueckfahr"]) + if reverse_light: + ret.gearShifter = GearShifter.reverse + else: + ret.gearShifter = GearShifter.drive + + # Update door and trunk/hatch lid open status. + ret.doorOpen = any([pt_cp.vl["Gate_Komf_1"]["GK1_Fa_Tuerkont"], + pt_cp.vl["Gate_Komf_1"]["BSK_BT_geoeffnet"], + pt_cp.vl["Gate_Komf_1"]["BSK_HL_geoeffnet"], + pt_cp.vl["Gate_Komf_1"]["BSK_HR_geoeffnet"], + pt_cp.vl["Gate_Komf_1"]["BSK_HD_Hauptraste"]]) + + # Update seatbelt fastened status. + ret.seatbeltUnlatched = not bool(pt_cp.vl["Airbag_1"]["Gurtschalter_Fahrer"]) + + # Consume blind-spot monitoring info/warning LED states, if available. + # Infostufe: BSM LED on, Warnung: BSM LED flashing + if self.CP.enableBsm: + ret.leftBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"]) + ret.rightBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"]) + + # Consume factory LDW data relevant for factory SWA (Lane Change Assist) + # and capture it for forwarding to the blind spot radar controller + self.ldw_stock_values = cam_cp.vl["LDW_Status"] if self.CP.networkLocation == NetworkLocation.fwdCamera else {} + + # Stock FCW is considered active if the release bit for brake-jerk warning + # is set. Stock AEB considered active if the partial braking or target + # braking release bits are set. + # Refer to VW Self Study Program 890253: Volkswagen Driver Assistance + # Systems, chapters on Front Assist with Braking and City Emergency + # Braking for the 2016 Passat NMS + # TODO: deferred until we can collect data on pre-MY2016 behavior, AWV message may be shorter with fewer signals + ret.stockFcw = False + ret.stockAeb = False + + # Update ACC radar status. + ret.cruiseState.available = bool(pt_cp.vl["Motor_5"]["GRA_Hauptschalter"]) + ret.cruiseState.enabled = bool(pt_cp.vl["Motor_2"]["GRA_Status"]) + if self.CP.pcmCruise: + ret.accFaulted = ext_cp.vl["ACC_GRA_Anziege"]["ACA_StaACC"] in (6, 7) + # TODO: update opendbc with PQ TSK state for OP long accFaulted + + # Update ACC setpoint. When the setpoint reads as 255, the driver has not + # yet established an ACC setpoint, so treat it as zero. + ret.cruiseState.speed = ext_cp.vl["ACC_GRA_Anziege"]["ACA_V_Wunsch"] * CV.KPH_TO_MS + if ret.cruiseState.speed > 70: # 255 kph in m/s == no current setpoint + ret.cruiseState.speed = 0 + + # Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough + ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"], + pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_re"]) + ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS) + self.gra_stock_values = pt_cp.vl["GRA_Neu"] + + # Additional safety checks performed in CarInterface. + ret.espDisabled = bool(pt_cp.vl["Bremse_1"]["ESP_Passiv_getastet"]) + + return ret + @staticmethod def get_can_parser(CP): + if CP.carFingerprint in PQ_CARS: + return CarState.get_can_parser_pq(CP) + signals = [ # sig_name, sig_address ("LWI_Lenkradwinkel", "LWI_01"), # Absolute steering angle @@ -218,10 +324,13 @@ def get_can_parser(CP): signals += MqbExtraSignals.bsm_radar_signals checks += MqbExtraSignals.bsm_radar_checks - return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.pt) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.pt) @staticmethod def get_cam_can_parser(CP): + if CP.carFingerprint in PQ_CARS: + return CarState.get_cam_can_parser_pq(CP) + signals = [] checks = [] @@ -246,7 +355,124 @@ def get_cam_can_parser(CP): signals += MqbExtraSignals.bsm_radar_signals checks += MqbExtraSignals.bsm_radar_checks - return CANParser(DBC_FILES.mqb, signals, checks, CANBUS.cam) + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.cam) + + @staticmethod + def get_can_parser_pq(CP): + signals = [ + # sig_name, sig_address, default + ("LH3_BLW", "Lenkhilfe_3"), # Absolute steering angle + ("LH3_BLWSign", "Lenkhilfe_3"), # Steering angle sign + ("LH3_LM", "Lenkhilfe_3"), # Absolute driver torque input + ("LH3_LMSign", "Lenkhilfe_3"), # Driver torque input sign + ("LH2_Sta_HCA", "Lenkhilfe_2"), # Steering rack HCA status + ("Lenkradwinkel_Geschwindigkeit", "Lenkwinkel_1"), # Absolute steering rate + ("Lenkradwinkel_Geschwindigkeit_S", "Lenkwinkel_1"), # Steering rate sign + ("Geschwindigkeit_neu__Bremse_1_", "Bremse_1"), # Vehicle speed from ABS + ("Radgeschw__VL_4_1", "Bremse_3"), # ABS wheel speed, front left + ("Radgeschw__VR_4_1", "Bremse_3"), # ABS wheel speed, front right + ("Radgeschw__HL_4_1", "Bremse_3"), # ABS wheel speed, rear left + ("Radgeschw__HR_4_1", "Bremse_3"), # ABS wheel speed, rear right + ("Giergeschwindigkeit", "Bremse_5"), # Absolute yaw rate + ("Vorzeichen_der_Giergeschwindigk", "Bremse_5"), # Yaw rate sign + ("Gurtschalter_Fahrer", "Airbag_1"), # Seatbelt status, driver + ("Gurtschalter_Beifahrer", "Airbag_1"), # Seatbelt status, passenger + ("Bremstestschalter", "Motor_2"), # Brake pedal pressed (brake light test switch) + ("Bremslichtschalter", "Motor_2"), # Brakes applied (brake light switch) + ("Bremsdruck", "Bremse_5"), # Brake pressure applied + ("Vorzeichen_Bremsdruck", "Bremse_5"), # Brake pressure applied sign (???) + ("Fahrpedal_Rohsignal", "Motor_3"), # Accelerator pedal value + ("ESP_Passiv_getastet", "Bremse_1"), # Stability control disabled + ("GRA_Hauptschalter", "Motor_5"), # ACC main switch + ("GRA_Status", "Motor_2"), # ACC engagement status + ("GK1_Fa_Tuerkont", "Gate_Komf_1"), # Door open, driver + ("BSK_BT_geoeffnet", "Gate_Komf_1"), # Door open, passenger + ("BSK_HL_geoeffnet", "Gate_Komf_1"), # Door open, rear left + ("BSK_HR_geoeffnet", "Gate_Komf_1"), # Door open, rear right + ("BSK_HD_Hauptraste", "Gate_Komf_1"), # Trunk or hatch open + ("GK1_Blinker_li", "Gate_Komf_1"), # Left turn signal on + ("GK1_Blinker_re", "Gate_Komf_1"), # Right turn signal on + ("Bremsinfo", "Kombi_1"), # Manual handbrake applied + ("GRA_Hauptschalt", "GRA_Neu"), # ACC button, on/off + ("GRA_Typ_Hauptschalt", "GRA_Neu"), # ACC button, momentary vs latching + ("GRA_Kodierinfo", "GRA_Neu"), # ACC button, configuration + ("GRA_Abbrechen", "GRA_Neu"), # ACC button, cancel + ("GRA_Neu_Setzen", "GRA_Neu"), # ACC button, set + ("GRA_Up_lang", "GRA_Neu"), # ACC button, increase or accel, long press + ("GRA_Down_lang", "GRA_Neu"), # ACC button, decrease or decel, long press + ("GRA_Up_kurz", "GRA_Neu"), # ACC button, increase or accel, short press + ("GRA_Down_kurz", "GRA_Neu"), # ACC button, decrease or decel, short press + ("GRA_Recall", "GRA_Neu"), # ACC button, resume + ("GRA_Zeitluecke", "GRA_Neu"), # ACC button, time gap adj + ("COUNTER", "GRA_Neu"), # ACC button, message counter + ("GRA_Sender", "GRA_Neu"), # ACC button, CAN message originator + ] + + checks = [ + # sig_address, frequency + ("Bremse_1", 100), # From J104 ABS/ESP controller + ("Bremse_3", 100), # From J104 ABS/ESP controller + ("Lenkhilfe_3", 100), # From J500 Steering Assist with integrated sensors + ("Lenkwinkel_1", 100), # From J500 Steering Assist with integrated sensors + ("Motor_3", 100), # From J623 Engine control module + ("Airbag_1", 50), # From J234 Airbag control module + ("Bremse_5", 50), # From J104 ABS/ESP controller + ("GRA_Neu", 50), # From J??? steering wheel control buttons + ("Kombi_1", 50), # From J285 Instrument cluster + ("Motor_2", 50), # From J623 Engine control module + ("Motor_5", 50), # From J623 Engine control module + ("Lenkhilfe_2", 20), # From J500 Steering Assist with integrated sensors + ("Gate_Komf_1", 10), # From J533 CAN gateway + ] + + if CP.transmissionType == TransmissionType.automatic: + signals += [("Waehlhebelposition__Getriebe_1_", "Getriebe_1", 0)] # Auto trans gear selector position + checks += [("Getriebe_1", 100)] # From J743 Auto transmission control module + elif CP.transmissionType == TransmissionType.manual: + signals += [("Kupplungsschalter", "Motor_1", 0), # Clutch switch + ("GK1_Rueckfahr", "Gate_Komf_1", 0)] # Reverse light from BCM + checks += [("Motor_1", 100)] # From J623 Engine control module + + if CP.networkLocation == NetworkLocation.fwdCamera: + # Extended CAN devices other than the camera are here on CANBUS.pt + signals += PqExtraSignals.fwd_radar_signals + checks += PqExtraSignals.fwd_radar_checks + if CP.enableBsm: + signals += PqExtraSignals.bsm_radar_signals + checks += PqExtraSignals.bsm_radar_checks + + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.pt) + + @staticmethod + def get_cam_can_parser_pq(CP): + + signals = [] + checks = [] + + if CP.networkLocation == NetworkLocation.fwdCamera: + signals += [ + # sig_name, sig_address + ("LDW_SW_Warnung_links", "LDW_Status"), # Blind spot in warning mode on left side due to lane departure + ("LDW_SW_Warnung_rechts", "LDW_Status"), # Blind spot in warning mode on right side due to lane departure + ("LDW_Seite_DLCTLC", "LDW_Status"), # Direction of most likely lane departure (left or right) + ("LDW_DLC", "LDW_Status"), # Lane departure, distance to line crossing + ("LDW_TLC", "LDW_Status"), # Lane departure, time to line crossing + ] + checks += [ + # sig_address, frequency + ("LDW_Status", 10) # From R242 Driver assistance camera + ] + + if CP.networkLocation == NetworkLocation.gateway: + # Radars are here on CANBUS.cam + signals += PqExtraSignals.fwd_radar_signals + checks += PqExtraSignals.fwd_radar_checks + if CP.enableBsm: + signals += PqExtraSignals.bsm_radar_signals + checks += PqExtraSignals.bsm_radar_checks + + return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CANBUS.cam) + class MqbExtraSignals: # Additional signal and message lists for optional or bus-portable controllers @@ -269,3 +495,22 @@ class MqbExtraSignals: bsm_radar_checks = [ ("SWA_01", 20), # From J1086 Lane Change Assist ] + +class PqExtraSignals: + # Additional signal and message lists for optional or bus-portable controllers + fwd_radar_signals = [ + ("ACA_StaACC", "ACC_GRA_Anziege", 0), # ACC drivetrain coordinator status + ("ACA_V_Wunsch", "ACC_GRA_Anziege", 0), # ACC set speed + ] + fwd_radar_checks = [ + ("ACC_GRA_Anziege", 25), # From J428 ACC radar control module + ] + bsm_radar_signals = [ + ("SWA_Infostufe_SWA_li", "SWA_1", 0), # Blind spot object info, left + ("SWA_Warnung_SWA_li", "SWA_1", 0), # Blind spot object warning, left + ("SWA_Infostufe_SWA_re", "SWA_1", 0), # Blind spot object info, right + ("SWA_Warnung_SWA_re", "SWA_1", 0), # Blind spot object warning, right + ] + bsm_radar_checks = [ + ("SWA_1", 20), # From J1086 Lane Change Assist + ] diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 8bab93d4a3dac5..7e686b970712b3 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -1,7 +1,10 @@ from cereal import car -from selfdrive.car.volkswagen.values import CAR, CANBUS, NetworkLocation, TransmissionType, GearShifter -from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config +from panda import Panda +from common.conversions import Conversions as CV +from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, \ + gen_empty_fingerprint, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase +from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter EventName = car.CarEvent.EventName @@ -23,7 +26,36 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.carName = "volkswagen" ret.radarOffCan = True - if True: # pylint: disable=using-constant-test + if candidate in PQ_CARS: + # Set global PQ35/PQ46/NMS parameters + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagenPq)] + ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1 + + if 0x440 in fingerprint[0]: # Getriebe_1 + ret.transmissionType = TransmissionType.automatic + else: + ret.transmissionType = TransmissionType.manual + + if any(msg in fingerprint[1] for msg in (0x1A0, 0xC2)): # Bremse_1, Lenkwinkel_1 + ret.networkLocation = NetworkLocation.gateway + else: + ret.networkLocation = NetworkLocation.fwdCamera + + # The PQ port is in dashcam-only mode due to a fixed six-minute maximum timer on HCA steering. An unsupported + # EPS flash update to work around this timer, and enable steering down to zero, is available from: + # https://github.com/pd0wm/pq-flasher + # It is documented in a four-part blog series: + # https://blog.willemmelching.nl/carhacking/2022/01/02/vw-part1/ + # Panda ALLOW_DEBUG firmware required. + ret.dashcamOnly = True + + if disable_radar and ret.networkLocation == NetworkLocation.gateway: + # Proof-of-concept, prep for E2E only. No radar points available. Follow-to-stop not yet supported, but should + # be simple to add when a suitable test car becomes available. Panda ALLOW_DEBUG firmware required. + ret.openpilotLongitudinalControl = True + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_VOLKSWAGEN_LONG_CONTROL + + else: # Set global MQB parameters ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.volkswagen)] ret.enableBsm = 0x30F in fingerprint[0] # SWA_01 @@ -52,6 +84,13 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.lateralTuning.pid.kpV = [0.6] ret.lateralTuning.pid.kiV = [0.2] + # Global longitudinal tuning defaults, can be overridden per-vehicle + + ret.pcmCruise = not ret.openpilotLongitudinalControl + ret.longitudinalActuatorDelayUpperBound = 0.5 # s + ret.longitudinalTuning.kpV = [0.1] + ret.longitudinalTuning.kiV = [0.0] + # Per-chassis tuning values, override tuning defaults here if desired if candidate == CAR.ARTEON_MK1: @@ -74,6 +113,14 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa ret.mass = 1551 + STD_CARGO_KG ret.wheelbase = 2.79 + elif candidate == CAR.PASSAT_NMS: + ret.mass = 1503 + STD_CARGO_KG + ret.wheelbase = 2.80 + ret.minEnableSpeed = 20 * CV.KPH_TO_MS # ACC "basic", no FtS + ret.minSteerSpeed = 50 * CV.KPH_TO_MS + ret.steerActuatorDelay = 0.2 + CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) + elif candidate == CAR.POLO_MK6: ret.mass = 1230 + STD_CARGO_KG ret.wheelbase = 2.55 @@ -160,7 +207,8 @@ def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, disa def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam, self.cp_ext, self.CP.transmissionType) - events = self.create_common_events(ret, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic]) + events = self.create_common_events(ret, extra_gears=[GearShifter.eco, GearShifter.sport, GearShifter.manumatic], + pcm_enable=not self.CS.CP.openpilotLongitudinalControl) # Low speed steer alert hysteresis logic if self.CP.minSteerSpeed > 0. and ret.vEgo < (self.CP.minSteerSpeed + 1.): @@ -170,6 +218,12 @@ def _update(self, c): if self.low_speed_alert: events.add(EventName.belowSteerSpeed) + if self.CS.CP.openpilotLongitudinalControl: + if ret.vEgo < self.CP.minEnableSpeed + 2.: + events.add(EventName.belowEngageSpeed) + if c.enabled and ret.vEgo < self.CP.minEnableSpeed: + events.add(EventName.speedTooLow) + ret.events = events.to_msg() return ret diff --git a/selfdrive/car/volkswagen/mqbcan.py b/selfdrive/car/volkswagen/mqbcan.py new file mode 100644 index 00000000000000..1a4ca339ab4f1e --- /dev/null +++ b/selfdrive/car/volkswagen/mqbcan.py @@ -0,0 +1,38 @@ +def create_steering_control(packer, bus, apply_steer, lkas_enabled): + values = { + "SET_ME_0X3": 0x3, + "Assist_Torque": abs(apply_steer), + "Assist_Requested": lkas_enabled, + "Assist_VZ": 1 if apply_steer < 0 else 0, + "HCA_Available": 1, + "HCA_Standby": not lkas_enabled, + "HCA_Active": lkas_enabled, + "SET_ME_0XFE": 0xFE, + "SET_ME_0X07": 0x07, + } + return packer.make_can_msg("HCA_01", bus, values) + + +def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, steering_pressed, hud_alert, hud_control): + values = ldw_stock_values.copy() + + values.update({ + "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0, + "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0, + "LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible, + "LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible, + "LDW_Texte": hud_alert, + }) + return packer.make_can_msg("LDW_02", bus, values) + + +def create_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False): + values = gra_stock_values.copy() + + values.update({ + "COUNTER": idx, + "GRA_Abbrechen": cancel, + "GRA_Tip_Wiederaufnahme": resume, + }) + + return packer.make_can_msg("GRA_ACC_01", bus, values) diff --git a/selfdrive/car/volkswagen/pqcan.py b/selfdrive/car/volkswagen/pqcan.py new file mode 100644 index 00000000000000..1e7ed21342b54b --- /dev/null +++ b/selfdrive/car/volkswagen/pqcan.py @@ -0,0 +1,84 @@ +def create_steering_control(packer, bus, apply_steer, lkas_enabled): + values = { + "LM_Offset": abs(apply_steer), + "LM_OffSign": 1 if apply_steer < 0 else 0, + "HCA_Status": 5 if (lkas_enabled and apply_steer != 0) else 3, + "Vib_Freq": 16, + } + + return packer.make_can_msg("HCA_1", bus, values) + + +def create_lka_hud_control(packer, bus, ldw_stock_values, enabled, steering_pressed, hud_alert, hud_control): + values = ldw_stock_values.copy() + + values.update({ + "LDW_Lampe_gelb": 1 if enabled and steering_pressed else 0, + "LDW_Lampe_gruen": 1 if enabled and not steering_pressed else 0, + "LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible, + "LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible, + "LDW_Textbits": hud_alert, + }) + + return packer.make_can_msg("LDW_Status", bus, values) + + +def create_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False): + values = gra_stock_values.copy() + + values.update({ + "COUNTER": idx, + "GRA_Abbrechen": cancel, + "GRA_Recall": resume, + }) + + return packer.make_can_msg("GRA_Neu", bus, values) + + +def tsk_status_value(main_switch_on, acc_faulted, long_active): + if long_active: + tsk_status = 1 + elif main_switch_on: + tsk_status = 2 + else: + tsk_status = 0 + + return tsk_status + + +def acc_hud_status_value(main_switch_on, acc_faulted, long_active): + if acc_faulted: + hud_status = 6 + elif long_active: + hud_status = 3 + elif main_switch_on: + hud_status = 2 + else: + hud_status = 0 + + return hud_status + + +def create_acc_accel_control(packer, bus, adr_status, accel): + values = { + "ACS_Sta_ADR": adr_status, + "ACS_StSt_Info": adr_status != 1, + "ACS_Typ_ACC": 0, # TODO: this is ACC "basic", find a way to detect FtS support (1) + "ACS_Sollbeschl": accel if adr_status == 1 else 3.01, + "ACS_zul_Regelabw": 0.2 if adr_status == 1 else 1.27, + "ACS_max_AendGrad": 3.0 if adr_status == 1 else 5.08, + } + + return packer.make_can_msg("ACC_System", bus, values) + + +def create_acc_hud_control(packer, bus, acc_status, set_speed, lead_visible): + values = { + "ACA_StaACC": acc_status, + "ACA_Zeitluecke": 2, + "ACA_V_Wunsch": set_speed, + "ACA_gemZeitl": 8 if lead_visible else 0, + } + # TODO: ACA_ID_StaACC, ACA_AnzDisplay, ACA_kmh_mph, ACA_PrioDisp, ACA_Aend_Zeitluecke + + return packer.make_can_msg("ACC_GRA_Anziege", bus, values) diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index 9f58d68f81bb8b..58f071901b990e 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -4,6 +4,7 @@ from typing import Dict, List, Union from cereal import car +from opendbc.can.can_define import CANDefine from selfdrive.car import dbc_dict from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness @@ -15,54 +16,89 @@ class CarControllerParams: - HCA_STEP = 2 # HCA_01 message frequency 50Hz - LDW_STEP = 10 # LDW_02 message frequency 10Hz - GRA_ACC_STEP = 3 # GRA_ACC_01 message frequency 33Hz + HCA_STEP = 2 # HCA_01/HCA_1 message frequency 50Hz + GRA_ACC_STEP = 3 # GRA_ACC_01/GRA_Neu message frequency 33Hz + ACC_CONTROL_STEP = 2 # ACC_06/ACC_07/ACC_System frequency 50Hz + ACC_HUD_STEP = 4 # ACC_GRA_Anziege frequency 25Hz - # Observed documented MQB limits: 3.00 Nm max, rate of change 5.00 Nm/sec. - # Limiting rate-of-change based on real-world testing and Comma's safety - # requirements for minimum time to lane departure. - STEER_MAX = 300 # Max heading control assist torque 3.00 Nm - STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50)) - STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) - STEER_DRIVER_ALLOWANCE = 80 - STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily - STEER_DRIVER_FACTOR = 1 # from dbc + ACCEL_MAX = 2.0 # 2.0 m/s max acceleration + ACCEL_MIN = -3.5 # 3.5 m/s max deceleration + def __init__(self, CP): + # Documented lateral limits: 3.00 Nm max, rate of change 5.00 Nm/sec. + # MQB vs PQ maximums are shared, but rate-of-change limited differently + # based on safety requirements driven by lateral accel testing. + self.STEER_MAX = 300 # Max heading control assist torque 3.00 Nm + self.STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily + self.STEER_DRIVER_FACTOR = 1 # from dbc -class CANBUS: - pt = 0 - cam = 2 + can_define = CANDefine(DBC[CP.carFingerprint]["pt"]) + if CP.carFingerprint in PQ_CARS: + self.LDW_STEP = 5 # LDW_1 message frequency 20Hz + self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm + self.STEER_DELTA_UP = 6 # Max HCA reached in 1.00s (STEER_MAX / (50Hz * 1.00)) + self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) -class DBC_FILES: - mqb = "vw_mqb_2010" # Used for all cars with MQB-style CAN messaging + if CP.transmissionType == TransmissionType.automatic: + self.shifter_values = can_define.dv["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"] + self.hca_status_values = can_define.dv["Lenkhilfe_2"]["LH2_Sta_HCA"] + self.BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_Neu", "GRA_Neu_Setzen", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_Neu", "GRA_Recall", [1]), + Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_Neu", "GRA_Up_kurz", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_Neu", "GRA_Down_kurz", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "GRA_Neu", "GRA_Abbrechen", [1]), + Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_Neu", "GRA_Zeitluecke", [1]), + ] -DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict(DBC_FILES.mqb, None)) + self.LDW_MESSAGES = { + "none": 0, # Nothing to display + "laneAssistUnavail": 1, # "Lane Assist currently not available." + "laneAssistUnavailSysError": 2, # "Lane Assist system error" + "laneAssistUnavailNoSensorView": 3, # "Lane Assist not available. No sensor view." + "laneAssistTakeOver": 4, # "Lane Assist: Please Take Over Steering" + "laneAssistDeactivTrailer": 5, # "Lane Assist: no function with trailer" + } + else: + self.LDW_STEP = 10 # LDW_02 message frequency 10Hz + self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm + self.STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50)) + self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60)) -MQB_BUTTONS = [ - Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]), - Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]), - Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]), - Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]), - Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]), - Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]), -] + if CP.transmissionType == TransmissionType.automatic: + self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"] + elif CP.transmissionType == TransmissionType.direct: + self.shifter_values = can_define.dv["EV_Gearshift"]["GearPosition"] + self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"] + self.BUTTONS = [ + Button(car.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]), + Button(car.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]), + Button(car.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]), + Button(car.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]), + Button(car.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]), + Button(car.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]), + ] -MQB_LDW_MESSAGES = { - "none": 0, # Nothing to display - "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime - "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime - "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep - "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep - "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime - "laneAssistTakeOverSilent": 8, # "Lane Assist: Please Take Over Steering" silent - "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep - "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward -} + self.LDW_MESSAGES = { + "none": 0, # Nothing to display + "laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime + "laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime + "laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep + "emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep + "laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime + "laneAssistTakeOver": 8, # "Lane Assist: Please Take Over Steering" silent + "emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep + "laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward + } + + +class CANBUS: + pt = 0 + cam = 2 # Check the 7th and 8th characters of the VIN before adding a new CAR. If the @@ -76,6 +112,7 @@ class CAR: GOLF_MK7 = "VOLKSWAGEN GOLF 7TH GEN" # Chassis 5G/AU/BA/BE, Mk7 VW Golf and variants JETTA_MK7 = "VOLKSWAGEN JETTA 7TH GEN" # Chassis BU, Mk7 VW Jetta PASSAT_MK8 = "VOLKSWAGEN PASSAT 8TH GEN" # Chassis 3G, Mk8 VW Passat and variants + PASSAT_NMS = "VOLKSWAGEN PASSAT NMS" # Chassis A3, North America/China/Mideast NMS Passat, incl. facelift POLO_MK6 = "VOLKSWAGEN POLO 6TH GEN" # Chassis AW, Mk6 VW Polo TAOS_MK1 = "VOLKSWAGEN TAOS 1ST GEN" # Chassis B2, Mk1 VW Taos and Tharu TCROSS_MK1 = "VOLKSWAGEN T-CROSS 1ST GEN" # Chassis C1, Mk1 VW T-Cross SWB and LWB variants @@ -96,18 +133,27 @@ class CAR: SKODA_OCTAVIA_MK3 = "SKODA OCTAVIA 3RD GEN" # Chassis NE, Mk3 Skoda Octavia and variants +PQ_CARS = {CAR.PASSAT_NMS} + + +DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("vw_mqb_2010", None)) +for car_type in PQ_CARS: + DBC[car_type] = dbc_dict("vw_golf_mk4", None) + + class Footnote(Enum): KAMIQ = CarFootnote( "Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.", Column.MODEL) PASSAT = CarFootnote( - "Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform.", + "Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.", Column.MODEL) VW_HARNESS = CarFootnote( "Model-years 2021 and beyond may have a new camera harness design, which isn't yet available from the comma " + "store. Before ordering, remove the Lane Assist camera cover and check to see if the connector is black " + - "(older design) or light brown (newer design). For the newer design, in the interim, choose \"VW J533 Development\" " + - "from the vehicle drop-down for a harness that integrates at the CAN gateway inside the dashboard.", + "(older design) or light brown (newer design). In the interim, if your car has a J533 connector CAN gateway " + + "inside the dashboard, choose \"VW J533 Development\" from the vehicle drop-down for a suitable harness. " + + "(Some newer models are also observed to not have a J533 connector.)", Column.MODEL) VW_VARIANT = CarFootnote( "Includes versions with extra rear cargo space (may be called Variant, Estate, SportWagen, Shooting Brake, etc.)", @@ -153,6 +199,7 @@ class VWCarInfo(CarInfo): VWCarInfo("Volkswagen Passat Alltrack 2015-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), VWCarInfo("Volkswagen Passat GTE 2015-22", footnotes=[Footnote.VW_HARNESS, Footnote.VW_VARIANT], harness=Harness.j533), ], + CAR.PASSAT_NMS: VWCarInfo("Volkswagen Passat NMS 2017-22", harness=Harness.j533), CAR.POLO_MK6: [ VWCarInfo("Volkswagen Polo 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), VWCarInfo("Volkswagen Polo GTI 2020-22", footnotes=[Footnote.VW_HARNESS], harness=Harness.j533), @@ -177,7 +224,7 @@ class VWCarInfo(CarInfo): CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"), CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"), CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.KAMIQ]), - CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21"), + CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21", footnotes=[Footnote.VW_HARNESS]), CAR.SKODA_KODIAQ_MK1: VWCarInfo("Škoda Kodiaq 2018-19"), CAR.SKODA_SCALA_MK1: VWCarInfo("Škoda Scala 2020"), CAR.SKODA_SUPERB_MK3: VWCarInfo("Škoda Superb 2015-18"), @@ -507,6 +554,26 @@ class VWCarInfo(CarInfo): b'\xf1\x875Q0907572R \xf1\x890771', ], }, + CAR.PASSAT_NMS: { + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8706K906016C \xf1\x899609', + b'\xf1\x8706K906016G \xf1\x891124', + b'\xf1\x8706K906071BJ\xf1\x894891', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x8709G927158AB\xf1\x893318', + b'\xf1\x8709G927158BD\xf1\x893121', + b'\xf1\x8709G927158FQ\xf1\x893745', + ], + (Ecu.srs, 0x715, None): [ + b'\xf1\x87561959655 \xf1\x890210\xf1\x82\02212121111113000102011--121012--101312', + b'\xf1\x87561959655C \xf1\x890508\xf1\x82\02215141111121100314919--153015--304831', + ], + (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x87561907567A \xf1\x890132', + b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\00152', + ], + }, CAR.POLO_MK6: { (Ecu.engine, 0x7e0, None): [ b'\xf1\x8704C906025H \xf1\x895177', diff --git a/selfdrive/car/volkswagen/volkswagencan.py b/selfdrive/car/volkswagen/volkswagencan.py deleted file mode 100644 index f4c21eeed4410a..00000000000000 --- a/selfdrive/car/volkswagen/volkswagencan.py +++ /dev/null @@ -1,42 +0,0 @@ -# CAN controls for MQB platform Volkswagen, Audi, Skoda, and SEAT. -# PQ35/PQ46/NMS, and any future MLB, to come later. - -def create_mqb_steering_control(packer, bus, apply_steer, lkas_enabled): - values = { - "SET_ME_0X3": 0x3, - "Assist_Torque": abs(apply_steer), - "Assist_Requested": lkas_enabled, - "Assist_VZ": 1 if apply_steer < 0 else 0, - "HCA_Available": 1, - "HCA_Standby": not lkas_enabled, - "HCA_Active": lkas_enabled, - "SET_ME_0XFE": 0xFE, - "SET_ME_0X07": 0x07, - } - return packer.make_can_msg("HCA_01", bus, values) - -def create_mqb_hud_control(packer, bus, enabled, steering_pressed, hud_alert, left_lane_visible, right_lane_visible, - ldw_stock_values, left_lane_depart, right_lane_depart): - # Lane color reference: - # 0 (LKAS disabled) - off - # 1 (LKAS enabled, no lane detected) - dark gray - # 2 (LKAS enabled, lane detected) - light gray on VW, green or white on Audi depending on year or virtual cockpit. On a color MFD on a 2015 A3 TDI it is white, virtual cockpit on a 2018 A3 e-Tron its green. - # 3 (LKAS enabled, lane departure detected) - white on VW, red on Audi - values = ldw_stock_values.copy() - values.update({ - "LDW_Status_LED_gelb": 1 if enabled and steering_pressed else 0, - "LDW_Status_LED_gruen": 1 if enabled and not steering_pressed else 0, - "LDW_Lernmodus_links": 3 if left_lane_depart else 1 + left_lane_visible, - "LDW_Lernmodus_rechts": 3 if right_lane_depart else 1 + right_lane_visible, - "LDW_Texte": hud_alert, - }) - return packer.make_can_msg("LDW_02", bus, values) - -def create_mqb_acc_buttons_control(packer, bus, gra_stock_values, idx, cancel=False, resume=False): - values = gra_stock_values.copy() - - values["COUNTER"] = idx - values["GRA_Abbrechen"] = cancel - values["GRA_Tip_Wiederaufnahme"] = resume - - return packer.make_can_msg("GRA_ACC_01", bus, values) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 2f7c85aa7d3324..1f394d487ada59 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -16,7 +16,7 @@ from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can from selfdrive.controls.lib.lane_planner import CAMERA_OFFSET -from selfdrive.controls.lib.drive_helpers import update_v_cruise, initialize_v_cruise +from selfdrive.controls.lib.drive_helpers import V_CRUISE_INITIAL, update_v_cruise, initialize_v_cruise from selfdrive.controls.lib.drive_helpers import get_lag_adjusted_curvature from selfdrive.controls.lib.latcontrol import LatControl from selfdrive.controls.lib.longcontrol import LongControl @@ -50,6 +50,7 @@ LaneChangeDirection = log.LateralPlan.LaneChangeDirection EventName = car.CarEvent.EventName ButtonEvent = car.CarState.ButtonEvent +ButtonType = car.CarState.ButtonEvent.Type SafetyModel = car.CarParams.SafetyModel IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) @@ -162,8 +163,8 @@ def __init__(self, sm=None, pm=None, can_sock=None, CI=None): self.active = False self.can_rcv_error = False self.soft_disable_timer = 0 - self.v_cruise_kph = 255 - self.v_cruise_cluster_kph = 255 + self.v_cruise_kph = V_CRUISE_INITIAL + self.v_cruise_cluster_kph = V_CRUISE_INITIAL self.v_cruise_kph_last = 0 self.mismatch_counter = 0 self.cruise_mismatch_counter = 0 @@ -203,6 +204,16 @@ def __init__(self, sm=None, pm=None, can_sock=None, CI=None): self.rk = Ratekeeper(100, print_delay_threshold=None) self.prof = Profiler(False) # off by default + def set_initial_state(self): + if REPLAY: + controls_state = Params().get("ReplayControlsState") + if controls_state is not None: + controls_state = log.ControlsState.from_bytes(controls_state) + self.v_cruise_kph = controls_state.vCruise + + if self.sm['pandaStates'][0].controlsAllowed: + self.state = State.enabled + def update_events(self, CS): """Compute carEvents from carState""" @@ -218,6 +229,11 @@ def update_events(self, CS): self.events.add(EventName.controlsInitializing) return + # Block resume if cruise never previously enabled + resume_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in CS.buttonEvents) + if not self.CP.pcmCruise and self.v_cruise_kph == V_CRUISE_INITIAL and resume_pressed: + self.events.add(EventName.resumeBlocked) + # Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0 if (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator) or \ (CS.brakePressed and (not self.CS_prev.brakePressed or not CS.standstill)): @@ -417,11 +433,9 @@ def data_sample(self): if all_valid or timed_out or SIMULATION: if not self.read_only: self.CI.init(self.CP, self.can_sock, self.pm.sock['sendcan']) - self.initialized = True - - if REPLAY and self.sm['pandaStates'][0].controlsAllowed: - self.state = State.enabled + self.initialized = True + self.set_initial_state() Params().put_bool("ControlsReady", True) # Check for CAN timeout @@ -452,18 +466,18 @@ def state_transition(self, CS): self.v_cruise_kph_last = self.v_cruise_kph - # if stock cruise is completely disabled, then we can use our own set speed logic - if not self.CP.pcmCruise: - self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.vEgo, CS.gasPressed, CS.buttonEvents, - self.button_timers, self.enabled, self.is_metric) - self.v_cruise_cluster_kph = self.v_cruise_kph - else: - if CS.cruiseState.available: + if CS.cruiseState.available: + # if stock cruise is completely disabled, then we can use our own set speed logic + if not self.CP.pcmCruise: + self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.vEgo, CS.gasPressed, CS.buttonEvents, + self.button_timers, self.enabled, self.is_metric) + self.v_cruise_cluster_kph = self.v_cruise_kph + else: self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH - else: - self.v_cruise_kph = 0 - self.v_cruise_cluster_kph = 0 + else: + self.v_cruise_kph = V_CRUISE_INITIAL + self.v_cruise_cluster_kph = V_CRUISE_INITIAL # decrement the soft disable timer at every step, as it's reset on # entrance in SOFT_DISABLING state @@ -626,8 +640,13 @@ def state_control(self, CS): if len(dpath_points): # Check if we deviated from the path # TODO use desired vs actual curvature - left_deviation = actuators.steer > 0 and dpath_points[0] < -0.20 - right_deviation = actuators.steer < 0 and dpath_points[0] > 0.20 + if self.CP.steerControlType == car.CarParams.SteerControlType.angle: + steering_value = actuators.steeringAngleDeg + else: + steering_value = actuators.steer + + left_deviation = steering_value > 0 and dpath_points[0] < -0.20 + right_deviation = steering_value < 0 and dpath_points[0] > 0.20 if left_deviation or right_deviation: self.events.add(EventName.steerSaturated) diff --git a/selfdrive/controls/lib/desire_helper.py b/selfdrive/controls/lib/desire_helper.py index 978c386530aa24..19ab2da214caef 100644 --- a/selfdrive/controls/lib/desire_helper.py +++ b/selfdrive/controls/lib/desire_helper.py @@ -40,12 +40,12 @@ def __init__(self): self.prev_one_blinker = False self.desire = log.LateralPlan.Desire.none - def update(self, carstate, active, lane_change_prob): + def update(self, carstate, lateral_active, lane_change_prob): v_ego = carstate.vEgo one_blinker = carstate.leftBlinker != carstate.rightBlinker below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN - if not active or self.lane_change_timer > LANE_CHANGE_TIME_MAX: + if not lateral_active or self.lane_change_timer > LANE_CHANGE_TIME_MAX: self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none else: diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index d79f94bbfdad82..ffa83738344825 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -11,6 +11,7 @@ V_CRUISE_MAX = 145 # kph V_CRUISE_MIN = 8 # kph V_CRUISE_ENABLE_MIN = 40 # kph +V_CRUISE_INITIAL = 255 # kph LAT_MPC_N = 16 LON_MPC_N = 32 diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index de666fd507c975..5139ead84bf195 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -635,6 +635,10 @@ def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, ET.NO_ENTRY: wrong_car_mode_alert, }, + EventName.resumeBlocked: { + ET.NO_ENTRY: NoEntryAlert("Press Set to Engage"), + }, + EventName.wrongCruiseMode: { ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage), ET.NO_ENTRY: NoEntryAlert("Adaptive Cruise Disabled"), diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py index a1037f040bfaf3..c0e7358160fd0c 100755 --- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py @@ -89,7 +89,7 @@ def gen_lat_ocp(): # TODO hacky weights to keep behavior the same ocp.model.cost_y_expr = vertcat(y_ego, ((v_ego +5.0) * psi_ego), - ((v_ego +5.0) * 4 * curv_rate)) + ((v_ego + 5.0) * 4.0 * curv_rate)) ocp.model.cost_y_expr_e = vertcat(y_ego, ((v_ego +5.0) * psi_ego)) @@ -147,7 +147,7 @@ def set_weights(self, path_weight, heading_weight, steer_rate_weight): #TODO hacky weights to keep behavior the same self.solver.cost_set(N, 'W', (3/20.)*W[:2,:2]) - def run(self, x0, p, y_pts, heading_pts): + def run(self, x0, p, y_pts, heading_pts, curv_rate_pts): x0_cp = np.copy(x0) p_cp = np.copy(p) self.solver.constraints_set(0, "lbx", x0_cp) @@ -156,6 +156,7 @@ def run(self, x0, p, y_pts, heading_pts): v_ego = p_cp[0] # rotation_radius = p_cp[1] self.yref[:,1] = heading_pts*(v_ego+5.0) + self.yref[:,2] = curv_rate_pts * (v_ego+5.0) * 4.0 for i in range(N): self.solver.cost_set(i, "yref", self.yref[i]) self.solver.set(i, "p", p_cp) diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py index 019a19fb1dfe66..29dfc771111c92 100644 --- a/selfdrive/controls/lib/lateral_planner.py +++ b/selfdrive/controls/lib/lateral_planner.py @@ -3,7 +3,7 @@ from common.numpy_fast import interp from system.swaglog import cloudlog from selfdrive.controls.lib.lateral_mpc_lib.lat_mpc import LateralMpc -from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N, CAR_ROTATION_RADIUS +from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N from selfdrive.controls.lib.lane_planner import LanePlanner, TRAJECTORY_SIZE from selfdrive.controls.lib.desire_helper import DesireHelper import cereal.messaging as messaging @@ -11,17 +11,21 @@ class LateralPlanner: - def __init__(self, use_lanelines=True, wide_camera=False): + def __init__(self, CP, use_lanelines=True, wide_camera=False): self.use_lanelines = use_lanelines self.LP = LanePlanner(wide_camera) self.DH = DesireHelper() + # Vehicle model parameters used to calculate lateral movement of car + self.factor1 = CP.wheelbase - CP.centerToFront + self.factor2 = (CP.centerToFront * CP.mass) / (CP.wheelbase * CP.tireStiffnessRear) self.last_cloudlog_t = 0 self.solution_invalid_cnt = 0 self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE,)) + self.plan_curv_rate = np.zeros((TRAJECTORY_SIZE,)) self.t_idxs = np.arange(TRAJECTORY_SIZE) self.y_pts = np.zeros(TRAJECTORY_SIZE) @@ -42,13 +46,13 @@ def update(self, sm): if len(md.position.x) == TRAJECTORY_SIZE and len(md.orientation.x) == TRAJECTORY_SIZE: self.path_xyz = np.column_stack([md.position.x, md.position.y, md.position.z]) self.t_idxs = np.array(md.position.t) - self.plan_yaw = list(md.orientation.z) + self.plan_yaw = np.array(md.orientation.z) if len(md.position.xStd) == TRAJECTORY_SIZE: self.path_xyz_stds = np.column_stack([md.position.xStd, md.position.yStd, md.position.zStd]) # Lane change logic lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob - self.DH.update(sm['carState'], sm['controlsState'].active, lane_change_prob) + self.DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob) # Turn off lanes during lane change if self.DH.desire == log.LateralPlan.Desire.laneChangeRight or self.DH.desire == log.LateralPlan.Desire.laneChangeLeft: @@ -67,16 +71,19 @@ def update(self, sm): y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) + curv_rate_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_curv_rate) self.y_pts = y_pts assert len(y_pts) == LAT_MPC_N + 1 assert len(heading_pts) == LAT_MPC_N + 1 - # self.x0[4] = v_ego - p = np.array([v_ego, CAR_ROTATION_RADIUS]) + assert len(curv_rate_pts) == LAT_MPC_N + 1 + lateral_factor = max(0, self.factor1 - (self.factor2 * v_ego**2)) + p = np.array([v_ego, lateral_factor]) self.lat_mpc.run(self.x0, p, y_pts, - heading_pts) + heading_pts, + curv_rate_pts) # init state for next # mpc.u_sol is the desired curvature rate given x0 curv state. # with x0[3] = measured_curvature, this would be the actual desired rate. diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index 9356a55d8433f8..ca7523f2eeffe3 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -22,10 +22,10 @@ def plannerd_thread(sm=None, pm=None): cloudlog.event("e2e mode", on=use_lanelines) longitudinal_planner = Planner(CP) - lateral_planner = LateralPlanner(use_lanelines=use_lanelines, wide_camera=wide_camera) + lateral_planner = LateralPlanner(CP, use_lanelines=use_lanelines, wide_camera=wide_camera) if sm is None: - sm = messaging.SubMaster(['carState', 'controlsState', 'radarState', 'modelV2'], + sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2'], poll=['radarState', 'modelV2'], ignore_avg_freq=['radarState']) if pm is None: diff --git a/selfdrive/controls/tests/test_lateral_mpc.py b/selfdrive/controls/tests/test_lateral_mpc.py index 4630e28a6154f5..4864dbdc068328 100644 --- a/selfdrive/controls/tests/test_lateral_mpc.py +++ b/selfdrive/controls/tests/test_lateral_mpc.py @@ -13,6 +13,7 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur y_pts = poly_shift * np.ones(LAT_MPC_N + 1) heading_pts = np.zeros(LAT_MPC_N + 1) + curv_rate_pts = np.zeros(LAT_MPC_N + 1) x0 = np.array([x_init, y_init, psi_init, curvature_init]) p = np.array([v_ref, CAR_ROTATION_RADIUS]) @@ -20,7 +21,7 @@ def run_mpc(lat_mpc=None, v_ref=30., x_init=0., y_init=0., psi_init=0., curvatur # converge in no more than 10 iterations for _ in range(10): lat_mpc.run(x0, p, - y_pts, heading_pts) + y_pts, heading_pts, curv_rate_pts) return lat_mpc.x_sol diff --git a/selfdrive/debug/test_car_model.py b/selfdrive/debug/test_car_model.py index 9082dbe3d6b4e2..4de5b267628810 100755 --- a/selfdrive/debug/test_car_model.py +++ b/selfdrive/debug/test_car_model.py @@ -4,11 +4,11 @@ from typing import List, Tuple import unittest -from selfdrive.car.tests.routes import TestRoute +from selfdrive.car.tests.routes import CarTestRoute from selfdrive.car.tests.test_models import TestCarModel -def create_test_models_suite(routes: List[Tuple[str, TestRoute]], ci=False) -> unittest.TestSuite: +def create_test_models_suite(routes: List[Tuple[str, CarTestRoute]], ci=False) -> unittest.TestSuite: test_suite = unittest.TestSuite() for car_model, test_route in routes: # create new test case and discover tests @@ -30,7 +30,7 @@ def create_test_models_suite(routes: List[Tuple[str, TestRoute]], ci=False) -> u parser.print_help() sys.exit() - test_route = TestRoute(args.route, args.car, segment=args.segment) + test_route = CarTestRoute(args.route, args.car, segment=args.segment) test_suite = create_test_models_suite([(args.car, test_route)], ci=args.ci) unittest.TextTestRunner().run(test_suite) diff --git a/selfdrive/debug/test_fw_query_on_routes.py b/selfdrive/debug/test_fw_query_on_routes.py index 0cc674cfc8220f..868813b0feba29 100755 --- a/selfdrive/debug/test_fw_query_on_routes.py +++ b/selfdrive/debug/test_fw_query_on_routes.py @@ -10,7 +10,7 @@ from tools.lib.route import Route from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.car_helpers import interface_names -from selfdrive.car.fw_versions import match_fw_to_car_exact, match_fw_to_car_fuzzy, build_fw_dict +from selfdrive.car.fw_versions import match_fw_to_car NO_API = "NO_API" in os.environ @@ -89,9 +89,8 @@ print("not in supported cars") break - fw_versions_dict = build_fw_dict(car_fw) - exact_matches = match_fw_to_car_exact(fw_versions_dict) - fuzzy_matches = match_fw_to_car_fuzzy(fw_versions_dict) + _, exact_matches = match_fw_to_car(car_fw, allow_exact=True, allow_fuzzy=False) + _, fuzzy_matches = match_fw_to_car(car_fw, allow_exact=False, allow_fuzzy=True) if (len(exact_matches) == 1) and (list(exact_matches)[0] == live_fingerprint): good_exact += 1 diff --git a/selfdrive/loggerd/bootlog.cc b/selfdrive/loggerd/bootlog.cc index 882b749fe9d7b6..90ba487ff0984e 100644 --- a/selfdrive/loggerd/bootlog.cc +++ b/selfdrive/loggerd/bootlog.cc @@ -7,12 +7,6 @@ static kj::Array build_boot_log() { - std::vector bootlog_commands; - if (Hardware::AGNOS()) { - bootlog_commands.push_back("journalctl"); - bootlog_commands.push_back("sudo nvme smart-log --output-format=json /dev/nvme0"); - } - MessageBuilder msg; auto boot = msg.initEvent().initBoot(); @@ -31,17 +25,19 @@ static kj::Array build_boot_log() { } // Gather output of commands - i = 0; + std::vector bootlog_commands = { + "[ -e /dev/nvme0 ] && sudo nvme smart-log --output-format=json /dev/nvme0", + "[ -x \"$(command -v journalctl)\" ] && journalctl", + }; + auto commands = boot.initCommands().initEntries(bootlog_commands.size()); - for (auto &command : bootlog_commands) { - auto lentry = commands[i]; + for (int j = 0; j < bootlog_commands.size(); j++) { + auto lentry = commands[j]; - lentry.setKey(command); + lentry.setKey(bootlog_commands[j]); - const std::string result = util::check_output(command); + const std::string result = util::check_output(bootlog_commands[j]); lentry.setValue(capnp::Data::Reader((const kj::byte*)result.data(), result.size())); - - i++; } boot.setLaunchLog(util::read_file("/tmp/launch_log")); diff --git a/selfdrive/loggerd/encoder/v4l_encoder.cc b/selfdrive/loggerd/encoder/v4l_encoder.cc index b3bd692b1639bd..016e6c57063f73 100644 --- a/selfdrive/loggerd/encoder/v4l_encoder.cc +++ b/selfdrive/loggerd/encoder/v4l_encoder.cc @@ -303,4 +303,10 @@ V4LEncoder::~V4LEncoder() { checked_ioctl(fd, VIDIOC_STREAMOFF, &buf_type); request_buffers(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0); close(fd); + + for (int i = 0; i < BUF_OUT_COUNT; i++) { + if (buf_out[i].free() != 0) { + LOGE("Failed to free buffer"); + } + } } diff --git a/selfdrive/loggerd/logger.cc b/selfdrive/loggerd/logger.cc index 8038f1926c8d47..da3710cc72a69b 100644 --- a/selfdrive/loggerd/logger.cc +++ b/selfdrive/loggerd/logger.cc @@ -24,9 +24,11 @@ kj::Array logger_build_init_data() { MessageBuilder msg; auto init = msg.initEvent().initInitData(); - init.setDeviceType(Hardware::get_device_type()); init.setVersion(COMMA_VERSION); + init.setDirty(!getenv("CLEAN")); + init.setDeviceType(Hardware::get_device_type()); + // log kernel args std::ifstream cmdline_stream("/proc/cmdline"); std::vector kernel_args; std::string buf; @@ -42,8 +44,6 @@ kj::Array logger_build_init_data() { init.setKernelVersion(util::read_file("/proc/version")); init.setOsVersion(util::read_file("/VERSION")); - init.setDirty(!getenv("CLEAN")); - // log params auto params = Params(); std::map params_map = params.readAll(); @@ -55,16 +55,31 @@ kj::Array logger_build_init_data() { init.setDongleId(params_map["DongleId"]); auto lparams = init.initParams().initEntries(params_map.size()); - int i = 0; + int j = 0; for (auto& [key, value] : params_map) { - auto lentry = lparams[i]; + auto lentry = lparams[j]; lentry.setKey(key); if ( !(params.getKeyType(key) & DONT_LOG) ) { lentry.setValue(capnp::Data::Reader((const kj::byte*)value.data(), value.size())); } - i++; + j++; + } + + // log commands + std::vector log_commands = { + "df -h", // usage for all filesystems + }; + + auto commands = init.initCommands().initEntries(log_commands.size()); + for (int i = 0; i < log_commands.size(); i++) { + auto lentry = commands[i]; + lentry.setKey(log_commands[i]); + + const std::string result = util::check_output(log_commands[i]); + lentry.setValue(capnp::Data::Reader((const kj::byte*)result.data(), result.size())); } + return capnp::messageToFlatArray(msg); } diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index dec51966a41dc8..927de563c5cb6f 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -2,7 +2,7 @@ from cereal import car from common.params import Params -from system.hardware import PC +from system.hardware import PC, TICI from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess WEBCAM = os.getenv("USE_WEBCAM") is not None @@ -57,7 +57,7 @@ def logging(started, params, CP: car.CarParams) -> bool: PythonProcess("webjoystick", "tools.joystick.web", onroad=False, callback=notcar), # Experimental - PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=os.path.isfile("/persist/comma/use-quectel-rawgps")), + PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=(TICI and os.path.isfile("/persist/comma/use-quectel-rawgps"))), ] managed_processes = {p.name: p for p in procs} diff --git a/selfdrive/modeld/models/supercombo.dlc b/selfdrive/modeld/models/supercombo.dlc index 23f6d904fb238b..fe133523fc20b8 100644 --- a/selfdrive/modeld/models/supercombo.dlc +++ b/selfdrive/modeld/models/supercombo.dlc @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:027cbb1fabae369878271cb0e3505071a8bdaa07473fad9a0b2e8d695c5dc1ff -size 76725611 +oid sha256:93d265fc88f05746ce47257e15fc2afe43b250b44715313049f829e8aa30a9d6 +size 94302331 diff --git a/selfdrive/modeld/models/supercombo.onnx b/selfdrive/modeld/models/supercombo.onnx index 9023c18dd7f970..7b11edbe0800ad 100644 --- a/selfdrive/modeld/models/supercombo.onnx +++ b/selfdrive/modeld/models/supercombo.onnx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:484976ea5bd4ddcabc82e95faf30d7311a27802c1e337472558699fa2395a499 -size 77472267 +oid sha256:50c7fc8565ac69a4b9a0de122e961326820e78bf13659255a89d0ed04be030d5 +size 95167481 diff --git a/selfdrive/modeld/thneed/optimizer.cc b/selfdrive/modeld/thneed/optimizer.cc index 03d20ff3861332..39737d3d7623ba 100644 --- a/selfdrive/modeld/thneed/optimizer.cc +++ b/selfdrive/modeld/thneed/optimizer.cc @@ -9,7 +9,7 @@ extern map g_program_source; -static int is_same_size_image(cl_mem a, cl_mem b) { +/*static int is_same_size_image(cl_mem a, cl_mem b) { size_t a_width, a_height, a_depth, a_array_size, a_row_pitch, a_slice_pitch; clGetImageInfo(a, CL_IMAGE_WIDTH, sizeof(a_width), &a_width, NULL); clGetImageInfo(a, CL_IMAGE_HEIGHT, sizeof(a_height), &a_height, NULL); @@ -29,7 +29,7 @@ static int is_same_size_image(cl_mem a, cl_mem b) { return (a_width == b_width) && (a_height == b_height) && (a_depth == b_depth) && (a_array_size == b_array_size) && (a_row_pitch == b_row_pitch) && (a_slice_pitch == b_slice_pitch); -} +}*/ static cl_mem make_image_like(cl_context context, cl_mem val) { cl_image_format format; @@ -138,7 +138,7 @@ int Thneed::optimize() { // delete useless copy layers // saves ~0.7 ms - if (kq[i]->name == "concatenation" || kq[i]->name == "flatten") { + /*if (kq[i]->name == "concatenation" || kq[i]->name == "flatten") { string in = kq[i]->args[kq[i]->get_arg_num("input")]; string out = kq[i]->args[kq[i]->get_arg_num("output")]; if (is_same_size_image(*(cl_mem*)in.data(), *(cl_mem*)out.data())) { @@ -148,7 +148,7 @@ int Thneed::optimize() { kq.erase(kq.begin()+i); --i; } - } + }*/ // NOTE: if activations/accumulation are done in the wrong order, this will be wrong diff --git a/selfdrive/test/process_replay/model_replay.py b/selfdrive/test/process_replay/model_replay.py index 8b41d6df1b66ba..d2090aa76aa625 100755 --- a/selfdrive/test/process_replay/model_replay.py +++ b/selfdrive/test/process_replay/model_replay.py @@ -22,7 +22,7 @@ TEST_ROUTE = "4cf7a6ad03080c90|2021-09-29--13-46-36" SEGMENT = 0 -MAX_FRAMES = 20 if PC else 1300 +MAX_FRAMES = 10 if PC else 1300 SEND_EXTRA_INPUTS = bool(os.getenv("SEND_EXTRA_INPUTS", "0")) @@ -176,7 +176,7 @@ def model_replay(lr, frs): # TODO this tolerence is absurdly large tolerance = 5e-1 if PC else None results: Any = {TEST_ROUTE: {}} - log_paths: Any = {TEST_ROUTE: {'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) diff1, diff2, failed = format_diff(results, log_paths, ref_commit) diff --git a/selfdrive/test/process_replay/model_replay_ref_commit b/selfdrive/test/process_replay/model_replay_ref_commit index 8b9483419308ca..80be9b464f5ca5 100644 --- a/selfdrive/test/process_replay/model_replay_ref_commit +++ b/selfdrive/test/process_replay/model_replay_ref_commit @@ -1 +1 @@ -5c2cb8fb9787584a1eb553968cb87e7e6782dac5 +ca90e11f8d59902af38d3785ddd91a27d0fbb411 diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index 0c642cde178999..8b9c77625f9ba1 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -280,7 +280,7 @@ def laika_rcv_callback(msg, CP, cfg, fsm): proc_name="plannerd", pub_sub={ "modelV2": ["lateralPlan", "longitudinalPlan"], - "carState": [], "controlsState": [], "radarState": [], + "carControl": [], "carState": [], "controlsState": [], "radarState": [], }, ignore=["logMonoTime", "valid", "longitudinalPlan.processingDelay", "longitudinalPlan.solverExecutionTime", "lateralPlan.solverExecutionTime"], init_callback=get_car_params, @@ -385,7 +385,7 @@ def replay_process(cfg, lr, fingerprint=None): return cpp_replay_process(cfg, lr, fingerprint) -def setup_env(simulation=False, CP=None, cfg=None): +def setup_env(simulation=False, CP=None, cfg=None, controlsState=None): params = Params() params.clear_all() params.put_bool("OpenpilotEnabledToggle", True) @@ -414,6 +414,12 @@ def setup_env(simulation=False, CP=None, cfg=None): elif "SIMULATION" in os.environ: del os.environ["SIMULATION"] + # Initialize controlsd with a controlsState packet + if controlsState is not None: + params.put("ReplayControlsState", controlsState.as_builder().to_bytes()) + else: + params.delete("ReplayControlsState") + # Regen or python process if CP is not None: if CP.alternativeExperience == ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS: @@ -440,13 +446,25 @@ def python_replay_process(cfg, lr, fingerprint=None): all_msgs = sorted(lr, key=lambda msg: msg.logMonoTime) pub_msgs = [msg for msg in all_msgs if msg.which() in list(cfg.pub_sub.keys())] + controlsState = None + initialized = False + for msg in lr: + if msg.which() == 'controlsState': + controlsState = msg.controlsState + if initialized: + break + elif msg.which() == 'carEvents': + initialized = car.CarEvent.EventName.controlsInitializing not in [e.name for e in msg.carEvents] + + assert controlsState is not None and initialized, "controlsState never initialized" + if fingerprint is not None: os.environ['SKIP_FW_QUERY'] = "1" os.environ['FINGERPRINT'] = fingerprint - setup_env(cfg=cfg) + setup_env(cfg=cfg, controlsState=controlsState) else: CP = [m for m in lr if m.which() == 'carParams'][0].carParams - setup_env(CP=CP, cfg=cfg) + setup_env(CP=CP, cfg=cfg, controlsState=controlsState) assert(type(managed_processes[cfg.proc_name]) is PythonProcess) managed_processes[cfg.proc_name].prepare() diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 27bea557408bef..f77f244594f725 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -c556add24a92c9d8593d9d3ebbeed9434b751d66 \ No newline at end of file +656daeb9de3680258527500ecae4ddff323b2e59 \ No newline at end of file diff --git a/selfdrive/test/process_replay/regen.py b/selfdrive/test/process_replay/regen.py index 39f75ce428aac6..4e1cbfd30d8684 100755 --- a/selfdrive/test/process_replay/regen.py +++ b/selfdrive/test/process_replay/regen.py @@ -201,14 +201,15 @@ def regen_segment(lr, frs=None, outdir=FAKEDATA, disable_tqdm=False): params = Params() os.environ["LOG_ROOT"] = outdir - for msg in lr: - if msg.which() == 'carParams': - setup_env(CP=msg.carParams) - elif msg.which() == 'liveCalibration': - params.put("CalibrationParams", msg.as_builder().to_bytes()) + # Get and setup initial state + CP = [m for m in lr if m.which() == 'carParams'][0].carParams + controlsState = [m for m in lr if m.which() == 'controlsState'][0].controlsState + liveCalibration = [m for m in lr if m.which() == 'liveCalibration'][0] - vs, cam_procs = replay_cameras(lr, frs, disable_tqdm=disable_tqdm) + setup_env(CP=CP, controlsState=controlsState) + params.put("CalibrationParams", liveCalibration.as_builder().to_bytes()) + vs, cam_procs = replay_cameras(lr, frs, disable_tqdm=disable_tqdm) fake_daemons = { 'sensord': [ multiprocessing.Process(target=replay_sensor_events, args=('sensorEvents', lr)), diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 9e6fee0de0c26f..e8c2e1dc9494e6 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -18,7 +18,7 @@ original_segments = [ ("BODY", "937ccb7243511b65|2022-05-24--16-03-09--1"), # COMMA.BODY ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"), # HYUNDAI.SONATA - ("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"), # HYUNDAI.KIA_EV6 + ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"), # HYUNDAI.KIA_EV6 ("TOYOTA", "0982d79ebb0de295|2021-01-04--17-13-21--13"), # TOYOTA.PRIUS (INDI) ("TOYOTA2", "0982d79ebb0de295|2021-01-03--20-03-36--6"), # TOYOTA.RAV4 (LQR) ("TOYOTA3", "f7d7e3538cda1a2a|2021-08-16--08-55-34--6"), # TOYOTA.COROLLA_TSS2 @@ -32,14 +32,15 @@ ("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.GOLF ("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--2"), # MAZDA.CX9_2021 - # Enable when port is tested and dascamOnly is no longer set + # Enable when port is tested and dashcamOnly is no longer set #("TESLA", "bb50caf5f0945ab1|2021-06-19--17-20-18--3"), # TESLA.AP2_MODELS + #("VOLKSWAGEN2", "3cfdec54aa035f3f|2022-07-19--23-45-10--2"), # VOLKSWAGEN.PASSAT_NMS ] segments = [ ("BODY", "regen660D86654BA|2022-07-06--14-27-15--0"), ("HYUNDAI", "regen114E5FF24D8|2022-07-14--17-08-47--0"), - ("HYUNDAI", "d824e27e8c60172c|2022-07-08--21-21-15--0"), + ("HYUNDAI", "d824e27e8c60172c|2022-08-19--17-58-07--2"), ("TOYOTA", "regenBA97410FBEC|2022-07-06--14-26-49--0"), ("TOYOTA2", "regenDEDB1D9C991|2022-07-06--14-54-08--0"), ("TOYOTA3", "regenDDC1FE60734|2022-07-06--14-32-06--0"), @@ -48,7 +49,7 @@ ("CHRYSLER", "regen38346FB33D0|2022-07-14--18-05-26--0"), ("RAM", "2f4452b03ccb98f0|2022-07-07--08-01-56--3"), ("SUBARU", "regen54A1E2BE5AA|2022-07-14--18-07-50--0"), - ("GM", "regen01D09D915B5|2022-07-06--14-36-20--0"), + ("GM", "regen76027B408B7|2022-08-16--19-56-58--0"), ("NISSAN", "regenCA0B0DC946E|2022-07-14--18-10-17--0"), ("VOLKSWAGEN", "regen007098CA0EF|2022-07-06--15-01-26--0"), ("MAZDA", "regen61BA413D53B|2022-07-06--14-39-42--0"), @@ -117,19 +118,19 @@ def format_diff(results, log_paths, ref_commit): for proc, diff in list(result.items()): # long diff diff2 += f"*** process: {proc} ***\n" - diff2 += f"\tref: {log_paths[segment]['ref']}\n\n" - diff2 += f"\tnew: {log_paths[segment]['new']}\n\n" + diff2 += f"\tref: {log_paths[segment][proc]['ref']}\n" + diff2 += f"\tnew: {log_paths[segment][proc]['new']}\n\n" # short diff diff1 += f" {proc}\n" if isinstance(diff, str): - diff1 += f" ref: {log_paths[segment]['ref']}\n" - diff1 += f" new: {log_paths[segment]['new']}\n\n" + diff1 += f" ref: {log_paths[segment][proc]['ref']}\n" + diff1 += f" new: {log_paths[segment][proc]['new']}\n\n" diff1 += f" {diff}\n" failed = True elif len(diff): - diff1 += f" ref: {log_paths[segment]['ref']}\n" - diff1 += f" new: {log_paths[segment]['new']}\n\n" + diff1 += f" ref: {log_paths[segment][proc]['ref']}\n" + diff1 += f" new: {log_paths[segment][proc]['new']}\n\n" cnt: Dict[str, int] = {} for d in diff: @@ -196,8 +197,7 @@ def format_diff(results, log_paths, ref_commit): untested = (set(interface_names) - set(excluded_interfaces)) - {c.lower() for c in tested_cars} assert len(untested) == 0, f"Cars missing routes: {str(untested)}" - - log_paths: DefaultDict[str, Dict[str, str]] = defaultdict(dict) + log_paths: DefaultDict[str, Dict[str, Dict[str, str]]] = defaultdict(lambda: defaultdict(dict)) with concurrent.futures.ProcessPoolExecutor(max_workers=args.jobs) as pool: if not args.upload_only: download_segments = [seg for car, seg in segments if car in tested_cars] @@ -225,8 +225,8 @@ def format_diff(results, log_paths, ref_commit): dat = None if args.upload_only else log_data[segment] pool_args.append((segment, cfg, args, cur_log_fn, ref_log_path, dat)) - log_paths[segment]['ref'] = ref_log_path - log_paths[segment]['new'] = cur_log_fn + log_paths[segment][cfg.proc_name + cfg.subtest_name]['ref'] = ref_log_path + log_paths[segment][cfg.proc_name + cfg.subtest_name]['new'] = cur_log_fn results: Any = defaultdict(dict) p2 = pool.map(run_test_process, pool_args) diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index 31651706c698a5..93598a1b066c4f 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -37,7 +37,7 @@ "selfdrive.thermald.thermald": 3.87, "selfdrive.locationd.calibrationd": 2.0, "./_soundd": 1.0, - "selfdrive.monitoring.dmonitoringd": 1.90, + "selfdrive.monitoring.dmonitoringd": 4.0, "./proclogd": 1.54, "system.logmessaged": 0.2, "./clocksd": 0.02, diff --git a/selfdrive/ui/qt/maps/map.cc b/selfdrive/ui/qt/maps/map.cc index f81c3dd8f07f20..046814d517d3e2 100644 --- a/selfdrive/ui/qt/maps/map.cc +++ b/selfdrive/ui/qt/maps/map.cc @@ -361,7 +361,7 @@ void MapWindow::offroadTransition(bool offroad) { } MapInstructions::MapInstructions(QWidget * parent) : QWidget(parent) { - is_rhd = Params().getBool("IsRHD"); + is_rhd = Params().getBool("IsRhdDetected"); QHBoxLayout *main_layout = new QHBoxLayout(this); main_layout->setContentsMargins(11, 50, 11, 11); { diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index e6e21bd8d3a4a6..5e0e87e64d0deb 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -62,7 +62,7 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { #ifdef ENABLE_MAPS { "NavSettingTime24h", - tr("Show ETA in 24h format"), + tr("Show ETA in 24h Format"), tr("Use 24h format instead of am/pm"), "../assets/offroad/icon_metric.png", }, diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index accab67bf071eb..a84542d29107b6 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -33,7 +33,7 @@ void Sidebar::drawMetric(QPainter &p, const QPair &label, QCol } Sidebar::Sidebar(QWidget *parent) : QFrame(parent) { - home_img = loadPixmap("../assets/images/button_home.png", {180, 180}); + home_img = loadPixmap("../assets/images/button_home.png", home_btn.size()); settings_img = loadPixmap("../assets/images/button_settings.png", settings_btn.size(), Qt::IgnoreAspectRatio); connect(this, &Sidebar::valueChanged, [=] { update(); }); @@ -43,9 +43,16 @@ Sidebar::Sidebar(QWidget *parent) : QFrame(parent) { setFixedWidth(300); QObject::connect(uiState(), &UIState::uiUpdate, this, &Sidebar::updateState); + + pm = std::make_unique>({"userFlag"}); } void Sidebar::mouseReleaseEvent(QMouseEvent *event) { + if (home_btn.contains(event->pos())) { + MessageBuilder msg; + msg.initEvent().initUserFlag(); + pm->send("userFlag", msg); + } if (settings_btn.contains(event->pos())) { emit openSettings(); } @@ -99,7 +106,7 @@ void Sidebar::paintEvent(QPaintEvent *event) { p.setOpacity(0.65); p.drawPixmap(settings_btn.x(), settings_btn.y(), settings_img); p.setOpacity(1.0); - p.drawPixmap(60, 1080 - 180 - 40, home_img); + p.drawPixmap(home_btn.x(), home_btn.y(), home_img); // network int x = 58; diff --git a/selfdrive/ui/qt/sidebar.h b/selfdrive/ui/qt/sidebar.h index 4c6d8f47e5bc35..0140673aac3fb1 100644 --- a/selfdrive/ui/qt/sidebar.h +++ b/selfdrive/ui/qt/sidebar.h @@ -43,6 +43,7 @@ public slots: {cereal::DeviceState::NetworkType::CELL5_G, tr("5G")} }; + const QRect home_btn = QRect(60, 860, 180, 180); const QRect settings_btn = QRect(50, 35, 200, 117); const QColor good_color = QColor(255, 255, 255); const QColor warning_color = QColor(218, 202, 37); @@ -52,4 +53,7 @@ public slots: ItemStatus connect_status, panda_status, temp_status; QString net_type; int net_strength = 0; + +private: + std::unique_ptr pm; }; diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py index 526dde93e85f75..fead288dfcad13 100755 --- a/selfdrive/ui/tests/test_translations.py +++ b/selfdrive/ui/tests/test_translations.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 import json import os +import re import shutil import unittest +import xml.etree.ElementTree as ET from selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations @@ -64,6 +66,30 @@ def test_vanished_translations(self): self.assertTrue(b"" not in cur_translations, f"{file} ({name}) translation file has obsolete translations. Run selfdrive/ui/update_translations.py --vanish to remove them") + def test_plural_translations(self): + """ + Tests: + - that any numerus (plural) translations marked "finished" have all plural forms non-empty + - that the correct format specifier is used (%n) + """ + for name, file in self.translation_files.items(): + with self.subTest(name=name, file=file): + tr_xml = ET.parse(os.path.join(TRANSLATIONS_DIR, f"{file}.ts")) + + for context in tr_xml.getroot(): + for message in context.iterfind("message"): + if message.get("numerus") == "yes": + translation = message.find("translation") + numerusform = [t.text for t in translation.findall("numerusform")] + + # Do not assert finished translations + if translation.get("type") == "unfinished": + continue + + self.assertNotIn(None, numerusform, "Ensure all plural translation forms are completed.") + self.assertTrue(all([re.search("%[0-9]+", t) is None for t in numerusform]), + "Plural translations must use %n, not %1, %2, etc.: {}".format(numerusform)) + if __name__ == "__main__": unittest.main() diff --git a/selfdrive/ui/translations/README.md b/selfdrive/ui/translations/README.md index 07f1a4825c3c64..91a6c3ca2f40c4 100644 --- a/selfdrive/ui/translations/README.md +++ b/selfdrive/ui/translations/README.md @@ -1,15 +1,19 @@ # Multilanguage -[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_en.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_en.ts) -[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHT.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHT.ts) -[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_zh-CHS.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_zh-CHS.ts) -[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ko.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ko.ts) -[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge_main_ja.svg)](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/main_ja.ts) +[![languages](https://raw.githubusercontent.com/commaai/openpilot/badges/translation_badge.svg)](#) ## Contributing Before getting started, make sure you have set up the openpilot Ubuntu development environment by reading the [tools README.md](/tools/README.md). +### Policy + +Most of the languages supported by openpilot come from and are maintained by the community via pull requests. A pull request likely to be merged is one that [fixes a translation or adds missing translations.](https://github.com/commaai/openpilot/blob/master/selfdrive/ui/translations/README.md#improving-an-existing-language) + +We also generally merge pull requests adding support for a new language if there are community members willing to maintain it. Maintaining a language is ensuring quality and completion of translations before each openpilot release. + +comma may remove or hide language support from releases depending on translation quality and completeness. + ### Adding a New Language openpilot provides a few tools to help contributors manage their translations and to ensure quality. To get started: @@ -38,7 +42,7 @@ Any time you edit source code in the UI, you need to update the translations to ### Testing -openpilot has a few unit tests to make sure all translations are up to date and that all strings are wrapped in a translation marker. They are run in CI, but you can also run them locally. +openpilot has a few unit tests to make sure all translations are up-to-date and that all strings are wrapped in a translation marker. They are run in CI, but you can also run them locally. Tests translation files up to date: diff --git a/selfdrive/ui/translations/create_badges.py b/selfdrive/ui/translations/create_badges.py index d9e2d443b969d4..451fbb23eb12b3 100755 --- a/selfdrive/ui/translations/create_badges.py +++ b/selfdrive/ui/translations/create_badges.py @@ -8,20 +8,17 @@ TRANSLATION_TAG = "'] + for idx, (name, file) in enumerate(translation_files.items()): with open(os.path.join(TRANSLATIONS_DIR, f"{file}.ts"), "r") as tr_f: tr_file = tr_f.read() - print(f"[![language](https://raw.githubusercontent.com/commaai/openpilot/badges/{TRANSLATION_BADGE.format(file)})]({TRANSLATION_LINK.format(file)}.ts)") - total_translations = 0 unfinished_translations = 0 for line in tr_file.splitlines(): @@ -35,6 +32,16 @@ r = requests.get(f"https://img.shields.io/badge/LANGUAGE {name}-{percent_finished}%25 complete-{color}") assert r.status_code == 200, "Error downloading badge" + content_svg = r.content.decode("utf-8") + + # make tag ids in each badge unique + for tag in ("r", "s"): + content_svg = content_svg.replace(f'id="{tag}"', f'id="{tag}{idx}"') + content_svg = content_svg.replace(f'"url(#{tag})"', f'"url(#{tag}{idx})"') + + badge_svg.extend([f'', content_svg, ""]) + + badge_svg.append("") - with open(os.path.join(BASEDIR, TRANSLATION_BADGE.format(file)), "wb") as badge_f: - badge_f.write(r.content) + with open(os.path.join(BASEDIR, "translation_badge.svg"), "w") as badge_f: + badge_f.write("\n".join(badge_svg)) diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json index f52a28ef9a2294..2559b2d26d502b 100644 --- a/selfdrive/ui/translations/languages.json +++ b/selfdrive/ui/translations/languages.json @@ -1,5 +1,6 @@ { "English": "main_en", + "Português": "main_pt", "中文(繁體)": "main_zh-CHT", "中文(简体)": "main_zh-CHS", "한국어": "main_ko", diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 20f96b8f4089be..dd9f933dcf75ff 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -876,71 +876,71 @@ location set Sidebar - - + + CONNECT 接続 - + OFFLINE オフライン - - + + ONLINE オンライン - + ERROR エラー - - - + + + TEMP 温度 - + HIGH 高温 - + GOOD 最適 - + OK OK - + VEHICLE 車両 - + NO NO - + PANDA PANDA - + GPS GPS - + SEARCH 検索 @@ -1025,23 +1025,23 @@ location set Switch Branch - + ブランチの切り替え ENTER - + 切替 The new branch will be pulled the next time the updater runs. - + updater を実行する時にブランチを切り替えます。 Enter branch name - + ブランチ名を入力 @@ -1204,7 +1204,7 @@ location set - Show ETA in 24h format + Show ETA in 24h Format 24時間表示 diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index cd9c73c34d5810..d4abe861962c3a 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -876,71 +876,71 @@ location set Sidebar - - + + CONNECT 연결 - + OFFLINE 오프라인 - - + + ONLINE 온라인 - + ERROR 오류 - - - + + + TEMP 온도 - + HIGH 높음 - + GOOD 좋음 - + OK 경고 - + VEHICLE 차량 - + NO NO - + PANDA PANDA - + GPS GPS - + SEARCH 검색중 @@ -1204,7 +1204,7 @@ location set - Show ETA in 24h format + Show ETA in 24h Format 24시간 형식으로 도착예정시간 표시 diff --git a/selfdrive/ui/translations/main_pt.ts b/selfdrive/ui/translations/main_pt.ts new file mode 100644 index 00000000000000..3d5eda118ce042 --- /dev/null +++ b/selfdrive/ui/translations/main_pt.ts @@ -0,0 +1,1307 @@ + + + + + AbstractAlert + + + Close + Fechar + + + + Snooze Update + Adiar Atualização + + + + Reboot and Update + Reiniciar e Atualizar + + + + AdvancedNetworking + + + Back + Voltar + + + + Enable Tethering + Ativar Theter + + + + Tethering Password + Senha Thetering + + + + + EDIT + EDITAR + + + + Enter new tethering password + Insira nova senha thetering + + + + IP Address + IP Endereço + + + + Enable Roaming + Ativar Roaming + + + + APN Setting + APN Config + + + + Enter APN + Insira APN + + + + leave blank for automatic configuration + deixe em branco para configuração automática + + + + ConfirmationDialog + + + + Ok + OK + + + + Cancel + Cancelar + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + Você precisa aceitar os Termos e Condições para utilizar openpilot. + + + + Back + Voltar + + + + Decline, uninstall %1 + Rejeitar, desintalar %1 + + + + DevicePanel + + + Dongle ID + Dongle ID + + + + N/A + N/A + + + + Serial + Serial + + + + Driver Camera + Câmera Motorista + + + + PREVIEW + PREVISUAL + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + Pré-visualizar a câmera voltada para o motorista para garantir que monitor tem uma boa visibilidade (veículo precisa estar desligado) + + + + Reset Calibration + Limpar Calibragem + + + + RESET + RESET + + + + Are you sure you want to reset calibration? + Tem certeza que quer limpar calibragem? + + + + Review Training Guide + Revisar o Treinamento + + + + REVIEW + REVISAR + + + + Review the rules, features, and limitations of openpilot + Revisar regras, features e limitações do openpilot + + + + Are you sure you want to review the training guide? + Tem certeza que quer rever o treinamento? + + + + Regulatory + Regulatório + + + + VIEW + VER + + + + Change Language + Mudar Língua + + + + CHANGE + MUDAR + + + + Select a language + Selecione uma linguagem + + + + Reboot + Reiniciar + + + + Power Off + Desligar + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + o openpilot requer que o dispositivo seja montado dentro de 4° esquerda ou direita e dentro de 5° para cima ou 8° para baixo. o openpilot está continuamente calibrando, a redefinição raramente é necessária. + + + + Your device is pointed %1° %2 and %3° %4. + Seu dispositivo está montado %1° %2 e %3° %4. + + + + down + baixo + + + + up + cima + + + + left + esquerda + + + + right + direita + + + + Are you sure you want to reboot? + Tem certeza que quer reiniciar? + + + + Disengage to Reboot + Desacione para Reiniciar + + + + Are you sure you want to power off? + Tem certeza que quer desligar? + + + + Disengage to Power Off + Desacione para Desligar + + + + DriveStats + + + Drives + Dirigidas + + + + Hours + Horas + + + + ALL TIME + TOTAL + + + + PAST WEEK + SEMANA PASSADA + + + + KM + KM + + + + Miles + Milhas + + + + DriverViewScene + + + camera starting + camera iniciando + + + + InputDialog + + + Cancel + Cancelar + + + + Need at least %n character(s)! + + Necessita no mínimo %n caractere! + Necessita no mínimo %n caracteres! + + + + + Installer + + + Installing... + Instalando... + + + + Receiving objects: + Recebendo objetos: + + + + Resolving deltas: + Resolvendo deltas: + + + + Updating files: + Atualizando arquivos: + + + + MapETA + + + eta + eta + + + + min + min + + + + hr + hr + + + + km + km + + + + mi + mi + + + + MapInstructions + + + km + km + + + + m + m + + + + mi + milha + + + + ft + pés + + + + MapPanel + + + Current Destination + Destino Atual + + + + CLEAR + LIMPAR + + + + Recent Destinations + Destinos Recentes + + + + Try the Navigation Beta + Experimente a Navegação Beta + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + Obtenha instruções passo a passo exibidas e muito mais com +uma assinatura prime Increva-se agora:https://connect.comma.ai + + + + No home +location set + Sem local +residência definido + + + + No work +location set + Sem local +trabalho definido + + + + no recent destinations + sem destinos recentes + + + + MapWindow + + + Map Loading + Carregando Mapa + + + + Waiting for GPS + Esperando por GPS + + + + MultiOptionDialog + + + Select + Selecione + + + + Cancel + Cancelar + + + + Networking + + + Advanced + Avançado + + + + Enter password + Insira a senha + + + + + for "%1" + para "%1" + + + + Wrong password + Senha incorreta + + + + NvgWindow + + + km/h + km/h + + + + mph + mph + + + + + MAX + LIMITE + + + + + SPEED + MAX + + + + + LIMIT + VELO + + + + OffroadHome + + + UPDATE + ATUALIZAÇÃO + + + + ALERTS + ALERTAS + + + + ALERT + ALERTA + + + + PairingPopup + + + Pair your device to your comma account + Pareie seu dispositivo a sua conta comma + + + + Go to https://connect.comma.ai on your phone + navegue até https://connect.comma.ai no seu telefone + + + + Click "add new device" and scan the QR code on the right + Clique "add new device" e escaneie o QR code a seguir + + + + Bookmark connect.comma.ai to your home screen to use it like an app + Salve connect.comma.ai como sua página inicial para utilizar com um app + + + + PrimeAdWidget + + + Upgrade Now + Atualizar Agora + + + + Become a comma prime member at connect.comma.ai + Torne-se um membro comma prime em connect.comma.ai + + + + PRIME FEATURES: + PRIME FEATURES: + + + + Remote access + Acesso remoto + + + + 1 year of storage + 1 ano de armazenamento + + + + Developer perks + Benefícios para desenvolvedor + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ INSCRITO + + + + comma prime + comma prime + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + PONTOS COMMA + + + + QObject + + + Reboot + Reiniciar + + + + Exit + Sair + + + + dashcam + dashcam + + + + openpilot + openpilot + + + + %n minute(s) ago + + há %n minuto + há %n minutos + + + + + %n hour(s) ago + + há %n hora + há %n horas + + + + + %n day(s) ago + + há %n dia + há %n dias + + + + + Reset + + + Reset failed. Reboot to try again. + Reset falhou. Reinicie para tentar novamente. + + + + Are you sure you want to reset your device? + Tem certeza que quer resetar seu dispositivo? + + + + Resetting device... + Resetando dispositivo... + + + + System Reset + Resetar Sistema + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + Solicitado reset do sistema. Confirme para apagar todo conteúdo e configurações. Aperte cancelar para continuar boot. + + + + Cancel + Cancelar + + + + Reboot + Reiniciar + + + + Confirm + Confirmar + + + + Unable to mount data partition. Press confirm to reset your device. + Não foi possível montar a partição de dados. Pressione confirmar para resetar seu dispositivo. + + + + RichTextDialog + + + Ok + Ok + + + + SettingsWindow + + + × + × + + + + Device + Dispositivo + + + + + Network + Rede + + + + Toggles + Ajustes + + + + Software + Software + + + + Navigation + Navegação + + + + Setup + + + WARNING: Low Voltage + ALERTA: Baixa Voltagem + + + + Power your device in a car with a harness or proceed at your own risk. + Ligue seu dispositivo em um carro com um chicote ou prossiga por sua conta e risco. + + + + Power off + Desligar + + + + + + Continue + Continuar + + + + Getting Started + Começando + + + + Before we get on the road, let’s finish installation and cover some details. + Antes de pegarmos a estrada, vamos terminar a instalação e cobrir alguns detalhes. + + + + Connect to Wi-Fi + Conectar ao Wi-Fi + + + + + Back + Voltar + + + + Continue without Wi-Fi + Continuar sem Wi-Fi + + + + Waiting for internet + Esperando pela internet + + + + Choose Software to Install + Escolher Software para Instalar + + + + Dashcam + Dashcam + + + + Custom Software + Sofware Customizado + + + + Enter URL + Preencher URL + + + + for Custom Software + para o Software Customizado + + + + Downloading... + Baixando... + + + + Download Failed + Download Falhou + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + Garanta que a URL inserida é valida, e uma boa conexão à internet. + + + + Reboot device + Reiniciar Dispositivo + + + + Start over + Inicializar + + + + SetupWidget + + + Finish Setup + Terminar Configuração + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + Pareie seu dispositivo com comma connect (connect.comma.ai) e reivindique sua oferta de comma prime. + + + + Pair device + Parear dispositivo + + + + Sidebar + + + + CONNECT + CONEXÃO + + + + OFFLINE + DESCONEC + + + + + ONLINE + CONECTADO + + + + ERROR + ERRO + + + + + + TEMP + TEMP + + + + HIGH + ALTA + + + + GOOD + BOA + + + + OK + OK + + + + VEHICLE + VEÍCULO + + + + NO + SEM + + + + PANDA + PANDA + + + + GPS + GPS + + + + SEARCH + PROCURA + + + + -- + -- + + + + Wi-Fi + Wi-Fi + + + + ETH + ETH + + + + 2G + 2G + + + + 3G + 3G + + + + LTE + LTE + + + + 5G + 5G + + + + SoftwarePanel + + + Git Branch + Ramo Git + + + + Git Commit + Commit Git + + + + OS Version + Versão do Sistema + + + + Version + Versão + + + + Last Update Check + Verificação da última atualização + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + A última vez que o openpilot verificou com sucesso uma atualização. O atualizador só funciona com o carro desligado. + + + + Check for Update + Verifique atualizações + + + + CHECKING + VERIFICANDO + + + + Switch Branch + Trocar Ramo + + + + ENTER + INSERIR + + + + + The new branch will be pulled the next time the updater runs. + O novo ramo será aplicado na próxima execução do atualizador. + + + + Enter branch name + Inserir o nome do ramo + + + + UNINSTALL + DESINSTALAR + + + + Uninstall %1 + Desintalando %1 + + + + Are you sure you want to uninstall? + Tem certeza que quer desinstalar? + + + + failed to fetch update + falha ao buscar atualização + + + + + CHECK + VERIFICAR + + + + SshControl + + + SSH Keys + Chave SSH + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub. + + + + + ADD + ADICIONAR + + + + Enter your GitHub username + Insira seu nome de usuário do GitHub + + + + LOADING + CARREGANDO + + + + REMOVE + REMOVER + + + + Username '%1' has no keys on GitHub + Usuário "%1” não possui chaves no GitHub + + + + Request timed out + A solicitação expirou + + + + Username '%1' doesn't exist on GitHub + Usuário '%1' não existe no GitHub + + + + SshToggle + + + Enable SSH + Habilitar SSH + + + + TermsPage + + + Terms & Conditions + Termos & Condições + + + + Decline + Declinar + + + + Scroll to accept + Role para aceitar + + + + Agree + Concordo + + + + TogglesPanel + + + Enable openpilot + Ativar openpilot + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + Use o sistema openpilot para controle de cruzeiro adaptativo e assistência ao motorista de manutenção de faixa. Sua atenção é necessária o tempo todo para usar esse recurso. A alteração desta configuração tem efeito quando o carro é desligado. + + + + Enable Lane Departure Warnings + Ativar Avisos de Saída de Faixa + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + Receba alertas para voltar para a pista se o seu veículo sair da faixa e a seta não tiver sido acionada previamente quando em velocidades superiores a 50 km/h. + + + + Use Metric System + Usar Sistema Métrico + + + + Display speed in km/h instead of mph. + Exibir velocidade em km/h invés de mph. + + + + Record and Upload Driver Camera + Gravar e Upload Câmera Motorista + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + Upload dados da câmera voltada para o motorista e ajude a melhorar o algoritmo de monitoramentor. + + + + Disengage On Accelerator Pedal + Desacionar Com Pedal Do Acelerador + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + Quando ativado, pressionar o pedal do acelerador desacionará o openpilot. + + + + Show ETA in 24h Format + Mostrar ETA em formato 24h + + + + Use 24h format instead of am/pm + Use o formato 24h em vez de am/pm + + + + Show Map on Left Side of UI + Exibir Mapa no Lado Esquerdo + + + + Show map on left side when in split screen view. + Exibir mapa do lado esquerdo quando a tela for dividida. + + + + openpilot Longitudinal Control + openpilot Controle Longitudinal + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilot desativará o radar do carro e assumirá o controle do acelerador e freios. Atenção: isso desativa AEB! + + + + Updater + + + Update Required + Atualização Necessária + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + Uma atualização do sistema operacional é necessária. Conecte seu dispositivo ao Wi-Fi para a experiência de atualização mais rápida. O tamanho do download é de aproximadamente 1GB. + + + + Connect to Wi-Fi + Conecte-se ao Wi-Fi + + + + Install + Instalar + + + + Back + Voltar + + + + Loading... + Carregando... + + + + Reboot + Reiniciar + + + + Update failed + Falha na atualização + + + + WifiUI + + + + Scanning for networks... + Procurando redes... + + + + CONNECTING... + CONECTANDO... + + + + FORGET + ESQUECER + + + + Forget Wi-Fi Network "%1"? + Esquecer Rede Wi-Fi "%1"? + + + diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts new file mode 100644 index 00000000000000..3277f537404ff0 --- /dev/null +++ b/selfdrive/ui/translations/main_th.ts @@ -0,0 +1,1303 @@ + + + + + AbstractAlert + + + Close + ปิด + + + + Snooze Update + เลื่อนการอัปเดต + + + + Reboot and Update + รีบูตและอัปเดต + + + + AdvancedNetworking + + + Back + ย้อนกลับ + + + + Enable Tethering + ปล่อยฮอตสปอต + + + + Tethering Password + รหัสผ่านฮอตสปอต + + + + + EDIT + แก้ไข + + + + Enter new tethering password + ป้อนรหัสผ่านฮอตสปอตใหม่ + + + + IP Address + หมายเลขไอพี + + + + Enable Roaming + เปิดใช้งานโรมมิ่ง + + + + APN Setting + ตั้งค่า APN + + + + Enter APN + ป้อนค่า APN + + + + leave blank for automatic configuration + เว้นว่างเพื่อตั้งค่าอัตโนมัติ + + + + ConfirmationDialog + + + + Ok + ตกลง + + + + Cancel + ยกเลิก + + + + DeclinePage + + + You must accept the Terms and Conditions in order to use openpilot. + คุณต้องยอมรับเงื่อนไขและข้อตกลง เพื่อใช้งาน openpilot + + + + Back + ย้อนกลับ + + + + Decline, uninstall %1 + ปฏิเสธ และถอนการติดตั้ง %1 + + + + DevicePanel + + + Dongle ID + Dongle ID + + + + N/A + ไม่มี + + + + Serial + ซีเรียล + + + + Driver Camera + กล้องฝั่งคนขับ + + + + PREVIEW + แสดงภาพ + + + + Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) + ดูภาพตัวอย่างกล้องที่หันเข้าหาคนขับเพื่อให้แน่ใจว่าการตรวจสอบคนขับมีทัศนวิสัยที่ดี (รถต้องดับเครื่องยนต์) + + + + Reset Calibration + รีเซ็ตการคาลิเบรท + + + + RESET + รีเซ็ต + + + + Are you sure you want to reset calibration? + คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตการคาลิเบรท? + + + + Review Training Guide + ทบทวนคู่มือการใช้งาน + + + + REVIEW + ทบทวน + + + + Review the rules, features, and limitations of openpilot + ตรวจสอบกฎ คุณสมบัติ และข้อจำกัดของ openpilot + + + + Are you sure you want to review the training guide? + คุณแน่ใจหรือไม่ว่าต้องการทบทวนคู่มือการใช้งาน? + + + + Regulatory + ระเบียบข้อบังคับ + + + + VIEW + ดู + + + + Change Language + เปลี่ยนภาษา + + + + CHANGE + เปลี่ยน + + + + Select a language + เลือกภาษา + + + + Reboot + รีบูต + + + + Power Off + ปิดเครื่อง + + + + openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. + openpilot กำหนดให้ติดตั้งอุปกรณ์ โดยสามารถเอียงด้านซ้ายหรือขวาไม่เกิน 4° และเอียงขึ้นด้านบนไม่เกิน 5° หรือเอียงลงด้านล่างไม่เกิน 8° openpilot ทำการคาลิเบรทอย่างต่อเนื่อง แทบจะไม่จำเป็นต้องทำการรีเซ็ตการคาลิเบรท + + + + Your device is pointed %1° %2 and %3° %4. + อุปกรณ์ของคุณเอียงไปทาง %2 %1° และ %4 %3° + + + + down + ด้านล่าง + + + + up + ด้านบน + + + + left + ด้านซ้าย + + + + right + ด้านขวา + + + + Are you sure you want to reboot? + คุณแน่ใจหรือไม่ว่าต้องการรีบูต? + + + + Disengage to Reboot + ยกเลิกระบบช่วยขับเพื่อรีบูต + + + + Are you sure you want to power off? + คุณแน่ใจหรือไม่ว่าต้องการปิดเครื่อง? + + + + Disengage to Power Off + ยกเลิกระบบช่วยขับเพื่อปิดเครื่อง + + + + DriveStats + + + Drives + การขับขี่ + + + + Hours + ชั่วโมง + + + + ALL TIME + ทั้งหมด + + + + PAST WEEK + สัปดาห์ที่ผ่านมา + + + + KM + กิโลเมตร + + + + Miles + ไมล์ + + + + DriverViewScene + + + camera starting + กำลังเปิดกล้อง + + + + InputDialog + + + Cancel + ยกเลิก + + + + Need at least %n character(s)! + + ต้องการอย่างน้อย %n ตัวอักษร! + + + + + Installer + + + Installing... + กำลังติดตั้ง... + + + + Receiving objects: + กำลังรับข้อมูล: + + + + Resolving deltas: + การแก้ไขเดลต้า: + + + + Updating files: + กำลังอัปเดตไฟล์: + + + + MapETA + + + eta + eta + + + + min + นาที + + + + hr + ชม. + + + + km + กม. + + + + mi + ไมล์ + + + + MapInstructions + + + km + กม. + + + + m + ม. + + + + mi + ไมล์ + + + + ft + ฟุต + + + + MapPanel + + + Current Destination + ปลายทางปัจจุบัน + + + + CLEAR + ล้างข้อมูล + + + + Recent Destinations + ปลายทางล่าสุด + + + + Try the Navigation Beta + ลองใช้ระบบนำทาง (เบต้า) + + + + Get turn-by-turn directions displayed and more with a comma +prime subscription. Sign up now: https://connect.comma.ai + รับการแสดงเส้นทางแบบเลี้ยวต่อเลี้ยว และอื่นๆ ด้วยการสมัครบริการ +comma prime สมัครเลย: https://connect.comma.ai + + + + No home +location set + ยังไม่ได้กำหนด +ตำแหน่งของบ้าน + + + + No work +location set + ยังไม่ได้กำหนด +ตำแหน่งของที่ทำงาน + + + + no recent destinations + ไม่พบปลายทางล่าสุด + + + + MapWindow + + + Map Loading + กำลังโหลดแผนที่ + + + + Waiting for GPS + กำลังรอสัญญาณ GPS + + + + MultiOptionDialog + + + Select + เลือก + + + + Cancel + ยกเลิก + + + + Networking + + + Advanced + ขั้นสูง + + + + Enter password + ใส่รหัสผ่าน + + + + + for "%1" + สำหรับ "%1" + + + + Wrong password + รหัสผ่านผิด + + + + NvgWindow + + + km/h + กม./ชม. + + + + mph + ไมล์/ชม. + + + + + MAX + สูงสุด + + + + + SPEED + ความเร็ว + + + + + LIMIT + จำกัด + + + + OffroadHome + + + UPDATE + อัปเดต + + + + ALERTS + การแจ้งเตือน + + + + ALERT + การแจ้งเตือน + + + + PairingPopup + + + Pair your device to your comma account + จับคู่อุปกรณ์ของคุณกับบัญชี comma ของคุณ + + + + Go to https://connect.comma.ai on your phone + ไปที่ https://connect.comma.ai ด้วยโทรศัพท์ของคุณ + + + + Click "add new device" and scan the QR code on the right + กดที่ "add new device" และสแกนคิวอาร์โค้ดทางด้านขวา + + + + Bookmark connect.comma.ai to your home screen to use it like an app + จดจำ connect.comma.ai โดยการเพิ่มไปยังหน้าจอโฮม เพื่อใช้งานเหมือนเป็นแอปพลิเคชัน + + + + PrimeAdWidget + + + Upgrade Now + อัพเกรดเดี๋ยวนี้ + + + + Become a comma prime member at connect.comma.ai + สมัครสมาชิก comma prime ได้ที่ connect.comma.ai + + + + PRIME FEATURES: + คุณสมบัติของ PRIME: + + + + Remote access + การเข้าถึงระยะไกล + + + + 1 year of storage + จัดเก็บข้อมูลนาน 1 ปี + + + + Developer perks + สิทธิพิเศษสำหรับนักพัฒนา + + + + PrimeUserWidget + + + ✓ SUBSCRIBED + ✓ สมัครสำเร็จ + + + + comma prime + comma prime + + + + CONNECT.COMMA.AI + CONNECT.COMMA.AI + + + + COMMA POINTS + คะแนน COMMA + + + + QObject + + + Reboot + รีบูต + + + + Exit + ปิด + + + + dashcam + กล้องติดรถยนต์ + + + + openpilot + openpilot + + + + %n minute(s) ago + + %n นาทีที่แล้ว + + + + + %n hour(s) ago + + %n ชั่วโมงที่แล้ว + + + + + %n day(s) ago + + %n วันที่แล้ว + + + + + Reset + + + Reset failed. Reboot to try again. + การรีเซ็ตล้มเหลว รีบูตเพื่อลองอีกครั้ง + + + + Are you sure you want to reset your device? + คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตอุปกรณ์? + + + + Resetting device... + กำลังรีเซ็ตอุปกรณ์... + + + + System Reset + รีเซ็ตระบบ + + + + System reset triggered. Press confirm to erase all content and settings. Press cancel to resume boot. + มีการสั่งรีเซ็ตระบบ กดยืนยันเพื่อลบข้อมูลและการตั้งค่าทั้งหมด กดยกเลิกเพื่อบูตเข้าระบบตามปกติ + + + + Cancel + ยกเลิก + + + + Reboot + รีบูต + + + + Confirm + ยืนยัน + + + + Unable to mount data partition. Press confirm to reset your device. + ไม่สามารถเมานต์พาร์ติชั่นข้อมูล กดยืนยันเพื่อรีเซ็ตอุปกรณ์ของคุณ + + + + RichTextDialog + + + Ok + ตกลง + + + + SettingsWindow + + + × + × + + + + Device + อุปกรณ์ + + + + + Network + เครือข่าย + + + + Toggles + ตัวเลือก + + + + Software + ซอฟต์แวร์ + + + + Navigation + การนำทาง + + + + Setup + + + WARNING: Low Voltage + คำเตือน: แรงดันไฟฟ้าต่ำ + + + + Power your device in a car with a harness or proceed at your own risk. + โปรดต่ออุปกรณ์ของคุณเข้ากับสายควบคุมในรถยนต์ หรือดำเนินการด้วยความเสี่ยงของคุณเอง + + + + Power off + ปิดเครื่อง + + + + + + Continue + ดำเนินการต่อ + + + + Getting Started + เริ่มกันเลย + + + + Before we get on the road, let’s finish installation and cover some details. + ก่อนออกเดินทาง เรามาทำการติดตั้งซอฟต์แวร์ และตรวจสอบการตั้งค่า + + + + Connect to Wi-Fi + เชื่อมต่อ Wi-Fi + + + + + Back + ย้อนกลับ + + + + Continue without Wi-Fi + ดำเนินการต่อโดยไม่ใช้ Wi-Fi + + + + Waiting for internet + กำลังรอสัญญาณอินเตอร์เน็ต + + + + Choose Software to Install + เลือกซอฟต์แวร์ที่จะติดตั้ง + + + + Dashcam + กล้องติดรถยนต์ + + + + Custom Software + ซอฟต์แวร์ที่กำหนดเอง + + + + Enter URL + ป้อน URL + + + + for Custom Software + สำหรับซอฟต์แวร์ที่กำหนดเอง + + + + Downloading... + กำลังดาวน์โหลด... + + + + Download Failed + ดาวน์โหลดล้มเหลว + + + + Ensure the entered URL is valid, and the device’s internet connection is good. + ตรวจสอบให้แน่ใจว่า URL ที่ป้อนนั้นถูกต้อง และอุปกรณ์เชื่อมต่ออินเทอร์เน็ตอยู่ + + + + Reboot device + รีบูตอุปกรณ์ + + + + Start over + เริ่มต้นใหม่ + + + + SetupWidget + + + Finish Setup + ตั้งค่าเสร็จสิ้น + + + + Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. + จับคู่อุปกรณ์ของคุณกับ comma connect (connect.comma.ai) และรับข้อเสนอ comma prime ของคุณ + + + + Pair device + จับคู่อุปกรณ์ + + + + Sidebar + + + + CONNECT + เชื่อมต่อ + + + + OFFLINE + ออฟไลน์ + + + + + ONLINE + ออนไลน์ + + + + ERROR + เกิดข้อผิดพลาด + + + + + + TEMP + อุณหภูมิ + + + + HIGH + สูง + + + + GOOD + ดี + + + + OK + พอใช้ + + + + VEHICLE + รถยนต์ + + + + NO + ไม่พบ + + + + PANDA + PANDA + + + + GPS + จีพีเอส + + + + SEARCH + ค้นหา + + + + -- + -- + + + + Wi-Fi + Wi-Fi + + + + ETH + ETH + + + + 2G + 2G + + + + 3G + 3G + + + + LTE + LTE + + + + 5G + 5G + + + + SoftwarePanel + + + Git Branch + Git Branch + + + + Git Commit + Git Commit + + + + OS Version + เวอร์ชันระบบปฏิบัติการ + + + + Version + เวอร์ชั่น + + + + Last Update Check + ตรวจสอบการอัปเดตล่าสุด + + + + The last time openpilot successfully checked for an update. The updater only runs while the car is off. + ครั้งสุดท้ายที่ openpilot ตรวจสอบการอัปเดตสำเร็จ ตัวอัปเดตจะทำงานในขณะที่รถดับเครื่องอยู่เท่านั้น + + + + Check for Update + ตรวจสอบการอัปเดต + + + + CHECKING + กำลังตรวจสอบ + + + + Switch Branch + เปลี่ยน Branch + + + + ENTER + เปลี่ยน + + + + + The new branch will be pulled the next time the updater runs. + Branch ใหม่จะถูกติดตั้งในครั้งต่อไปที่ตัวอัปเดตทำงาน + + + + Enter branch name + ใส่ชื่อ Branch + + + + Uninstall %1 + ถอนการติดตั้ง %1 + + + + UNINSTALL + ถอนการติดตั้ง + + + + Are you sure you want to uninstall? + คุณแน่ใจหรือไม่ว่าต้องการถอนการติดตั้ง? + + + + failed to fetch update + โหลดข้อมูลอัปเดตไม่สำเร็จ + + + + + CHECK + ตรวจสอบ + + + + SshControl + + + SSH Keys + คีย์ SSH + + + + Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. + คำเตือน: สิ่งนี้ให้สิทธิ์ SSH เข้าถึงคีย์สาธารณะทั้งหมดใน GitHub ของคุณ อย่าป้อนชื่อผู้ใช้ GitHub อื่นนอกเหนือจากของคุณเอง พนักงาน comma จะไม่ขอให้คุณเพิ่มชื่อผู้ใช้ GitHub ของพวกเขา + + + + + ADD + เพิ่ม + + + + Enter your GitHub username + ป้อนชื่อผู้ใช้ GitHub ของคุณ + + + + LOADING + กำลังโหลด + + + + REMOVE + ลบ + + + + Username '%1' has no keys on GitHub + ชื่อผู้ใช้ '%1' ไม่มีคีย์บน GitHub + + + + Request timed out + ตรวจสอบไม่สำเร็จ เนื่องจากใช้เวลามากเกินไป + + + + Username '%1' doesn't exist on GitHub + ไม่พบชื่อผู้ใช้ '%1' บน GitHub + + + + SshToggle + + + Enable SSH + เปิดใช้งาน SSH + + + + TermsPage + + + Terms & Conditions + ข้อตกลงและเงื่อนไข + + + + Decline + ปฏิเสธ + + + + Scroll to accept + เลื่อนเพื่อตอบรับข้อตกลง + + + + Agree + ยอมรับ + + + + TogglesPanel + + + Enable openpilot + เปิดใช้งาน openpilot + + + + Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. + ใช้ระบบ openpilot สำหรับระบบควบคุมความเร็วอัตโนมัติ และระบบช่วยควบคุมรถให้อยู่ในเลน คุณจำเป็นต้องให้ความสนใจตลอดเวลาที่ใช้คุณสมบัตินี้ การเปลี่ยนการตั้งค่านี้จะมีผลเมื่อคุณดับเครื่องยนต์ + + + + Enable Lane Departure Warnings + เปิดใช้งานการเตือนการออกนอกเลน + + + + Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). + รับการแจ้งเตือนให้เลี้ยวกลับเข้าเลนเมื่อรถของคุณตรวจพบการข้ามช่องจราจรโดยไม่เปิดสัญญาณไฟเลี้ยวในขณะขับขี่ที่ความเร็วเกิน 31 ไมล์ต่อชั่วโมง (50 กม./ชม) + + + + Use Metric System + ใช้ระบบเมตริก + + + + Display speed in km/h instead of mph. + แสดงความเร็วเป็น กม./ชม. แทน ไมล์/ชั่วโมง + + + + Record and Upload Driver Camera + บันทึกและอัปโหลดภาพจากกล้องคนขับ + + + + Upload data from the driver facing camera and help improve the driver monitoring algorithm. + อัปโหลดข้อมูลจากกล้องที่หันหน้าไปทางคนขับ และช่วยปรับปรุงอัลกอริธึมการตรวจสอบผู้ขับขี่ + + + + Disengage On Accelerator Pedal + ยกเลิกระบบช่วยขับเมื่อเหยียบคันเร่ง + + + + When enabled, pressing the accelerator pedal will disengage openpilot. + เมื่อเปิดใช้งาน การกดแป้นคันเร่งจะเป็นการยกเลิกระบบช่วยขับโดย openpilot + + + + Show ETA in 24h Format + แสดงเวลา ETA ในรูปแบบ 24 ชั่วโมง + + + + Use 24h format instead of am/pm + ใช้รูปแบบเวลา 24 ชั่วโมง แทน am/pm + + + + Show Map on Left Side of UI + แสดงแผนที่ที่ด้านซ้ายของหน้าจอ + + + + Show map on left side when in split screen view. + แสดงแผนที่ด้านซ้ายของหน้าจอเมื่ออยู่ในโหมดแบ่งหน้าจอ + + + + openpilot Longitudinal Control + openpilot การควบคุมการเร่งและลดความเร็ว + + + + openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB! + openpilot จะปิดการใช้งานเรดาร์ของรถ และจะเข้าควบคุมการเร่งและเบรก คำเตือน: สิ่งนี้จะปิดระบบ AEB! + + + + Updater + + + Update Required + จำเป็นต้องอัปเดต + + + + An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. + จำเป็นต้องมีการอัปเดตระบบปฏิบัติการ เชื่อมต่ออุปกรณ์ของคุณกับ Wi-Fi เพื่อประสบการณ์การอัปเดตที่เร็วที่สุด ขนาดดาวน์โหลดประมาณ 1GB + + + + Connect to Wi-Fi + เชื่อมต่อกับ Wi-Fi + + + + Install + ติดตั้ง + + + + Back + ย้อนกลับ + + + + Loading... + กำลังโหลด... + + + + Reboot + รีบูต + + + + Update failed + การอัปเดตล้มเหลว + + + + WifiUI + + + + Scanning for networks... + กำลังสแกนหาเครือข่าย... + + + + CONNECTING... + กำลังเชื่อมต่อ... + + + + FORGET + เลิกใช้ + + + + Forget Wi-Fi Network "%1"? + เลิกใช้เครือข่าย Wi-Fi "%1"? + + + diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index cb09e8c2effc93..a26cddc0c34859 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -874,71 +874,71 @@ location set Sidebar - - + + CONNECT CONNECT - + OFFLINE 离线 - - + + ONLINE 在线 - + ERROR 连接出错 - - - + + + TEMP 设备温度 - + HIGH 过热 - + GOOD 良好 - + OK 一般 - + VEHICLE 车辆连接 - + NO - + PANDA PANDA - + GPS GPS - + SEARCH 搜索中 @@ -1202,7 +1202,7 @@ location set - Show ETA in 24h format + Show ETA in 24h Format 以24小时格式显示预计到达时间 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 58e921b313562f..f4681f85d940c2 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -876,71 +876,71 @@ location set Sidebar - - + + CONNECT 雲端服務 - + OFFLINE 已離線 - - + + ONLINE 已連線 - + ERROR 錯誤 - - - + + + TEMP 溫度 - + HIGH 偏高 - + GOOD 正常 - + OK 一般 - + VEHICLE 車輛通訊 - + NO 未連線 - + PANDA 車輛通訊 - + GPS GPS - + SEARCH 車輛通訊 @@ -1204,7 +1204,7 @@ location set - Show ETA in 24h format + Show ETA in 24h Format 預計到達時間單位改用 24 小時制 diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index 2a56052559be95..198efca0bb3f8f 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -94,7 +94,6 @@ class CameraBuf { public: cl_command_queue q; FrameMetadata cur_frame_data; - VisionBuf *cur_rgb_buf; VisionBuf *cur_yuv_buf; VisionBuf *cur_camera_buf; std::unique_ptr camera_bufs; diff --git a/tools/joystick/joystickd.py b/tools/joystick/joystickd.py index 6226038d516ed9..35ecca351d1250 100755 --- a/tools/joystick/joystickd.py +++ b/tools/joystick/joystickd.py @@ -41,7 +41,7 @@ class Joystick: def __init__(self): # TODO: find a way to get this from API, perhaps "inputs" doesn't support it self.min_axis_value = {'ABS_Y': 0., 'ABS_RZ': 0.} - self.max_axis_value = {'ABS_Y': 1023., 'ABS_RZ': 255.} + self.max_axis_value = {'ABS_Y': 255., 'ABS_RZ': 255.} self.cancel_button = 'BTN_TRIGGER' self.axes_values = {'ABS_Y': 0., 'ABS_RZ': 0.} # gb, steer self.axes_order = ['ABS_Y', 'ABS_RZ'] diff --git a/tools/plotjuggler/layouts/max-torque-debug.xml b/tools/plotjuggler/layouts/max-torque-debug.xml index 05aa208e260808..6903ca8b1532b8 100644 --- a/tools/plotjuggler/layouts/max-torque-debug.xml +++ b/tools/plotjuggler/layouts/max-torque-debug.xml @@ -51,12 +51,6 @@ - - - - - - diff --git a/tools/plotjuggler/layouts/torque-controller.xml b/tools/plotjuggler/layouts/torque-controller.xml index 661bfe094de445..443255968ad31e 100644 --- a/tools/plotjuggler/layouts/torque-controller.xml +++ b/tools/plotjuggler/layouts/torque-controller.xml @@ -1,12 +1,12 @@ - + - + - + @@ -14,7 +14,7 @@ - + @@ -22,33 +22,26 @@ - + - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - @@ -64,19 +57,29 @@ - + + + return value * 3.6 + /carState/vEgo + + + + return value * 2.23694 + /carState/vEgo + + return (value * v1 ^ 2) - (v2 * 9.81) - /controlsState/curvature + /controlsState/desiredCurvature /carState/vEgo /liveParameters/roll - + return (value * v1 ^ 2) - (v2 * 9.81) - /controlsState/desiredCurvature + /controlsState/curvature /carState/vEgo /liveParameters/roll @@ -86,3 +89,4 @@ + diff --git a/tools/replay/camera.cc b/tools/replay/camera.cc index 87afe63a2af64e..1a577b40cefa00 100644 --- a/tools/replay/camera.cc +++ b/tools/replay/camera.cc @@ -37,7 +37,8 @@ void CameraServer::startVipcServer() { void CameraServer::cameraThread(Camera &cam) { auto read_frame = [&](FrameReader *fr, int frame_id) { VisionBuf *yuv_buf = vipc_server_->get_buffer(cam.stream_type); - bool ret = fr->get(frame_id, yuv_buf ? (uint8_t *)yuv_buf->addr : nullptr); + assert(yuv_buf); + bool ret = fr->get(frame_id, (uint8_t *)yuv_buf->addr); return ret ? yuv_buf : nullptr; }; diff --git a/tools/replay/consoleui.cc b/tools/replay/consoleui.cc index e4a3146a69c02c..6b419ca9d80908 100644 --- a/tools/replay/consoleui.cc +++ b/tools/replay/consoleui.cc @@ -18,6 +18,7 @@ const std::initializer_list> keyboard_shortc {"space", "Pause/Resume"}, {"e", "Next Engagement"}, {"d", "Next Disengagement"}, + {"t", "Next User Tag"} }, { {"enter", "Enter seek request"}, @@ -32,6 +33,7 @@ enum Color { Yellow, Green, Red, + Cyan, BrightWhite, Engaged, Disengaged, @@ -70,6 +72,7 @@ ConsoleUI::ConsoleUI(Replay *replay, QObject *parent) : replay(replay), sm({"car init_pair(Color::Debug, 246, COLOR_BLACK); // #949494 init_pair(Color::Yellow, 184, COLOR_BLACK); init_pair(Color::Red, COLOR_RED, COLOR_BLACK); + init_pair(Color::Cyan, COLOR_CYAN, COLOR_BLACK); init_pair(Color::BrightWhite, 15, COLOR_BLACK); init_pair(Color::Disengaged, COLOR_BLUE, COLOR_BLUE); init_pair(Color::Engaged, 28, 28); @@ -205,6 +208,7 @@ void ConsoleUI::displayTimelineDesc() { {Color::Green, " Info ", true}, {Color::Yellow, " Warning ", true}, {Color::Red, " Critical ", true}, + {Color::Cyan, " User Tag ", true}, }; for (auto [color, name, bold] : indicators) { add_str(w[Win::TimelineDesc], "__", color, bold); @@ -263,6 +267,8 @@ void ConsoleUI::updateTimeline() { if (type == TimelineType::Engaged) { mvwchgat(win, 1, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); mvwchgat(win, 2, start_pos, end_pos - start_pos + 1, A_COLOR, Color::Engaged, NULL); + } else if (type == TimelineType::UserFlag) { + mvwchgat(win, 3, start_pos, end_pos - start_pos + 1, ACS_S3, Color::Cyan, NULL); } else { auto color_id = Color::Green; if (type != TimelineType::AlertInfo) { @@ -336,6 +342,8 @@ void ConsoleUI::handleKey(char c) { replay->seekToFlag(FindFlag::nextEngagement); } else if (c == 'd') { replay->seekToFlag(FindFlag::nextDisEngagement); + } else if (c == 't') { + replay->seekToFlag(FindFlag::nextUserFlag); } else if (c == 'm') { replay->seekTo(+60, true); } else if (c == 'M') { diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index c886a7e1862ae3..4b983fff85969d 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -149,6 +149,9 @@ void Replay::buildTimeline() { timeline.push_back({toSeconds(alert_begin), toSeconds(e->mono_time), alert_type}); alert_begin = 0; } + } else if (e->which == cereal::Event::Which::USER_FLAG) { + std::lock_guard lk(timeline_lock); + timeline.push_back({toSeconds(e->mono_time), toSeconds(e->mono_time), TimelineType::UserFlag}); } } } @@ -163,6 +166,10 @@ std::optional Replay::find(FindFlag flag) { } else if (flag == FindFlag::nextDisEngagement && end_ts > cur_ts) { return end_ts; } + } else if (type == TimelineType::UserFlag) { + if (flag == FindFlag::nextUserFlag && start_ts > cur_ts) { + return start_ts; + } } } return std::nullopt; @@ -360,7 +367,7 @@ void Replay::stream() { setCurrentSegment(toSeconds(cur_mono_time_) / 60); // migration for pandaState -> pandaStates to keep UI working for old segments - if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D && + if (cur_which == cereal::Event::Which::PANDA_STATE_D_E_P_R_E_C_A_T_E_D && sockets_[cereal::Event::Which::PANDA_STATES] != nullptr) { MessageBuilder msg; auto ps = msg.initEvent().initPandaStates(1); diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 13269d3ec9a269..86d609683a5034 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -24,10 +24,11 @@ enum REPLAY_FLAGS { enum class FindFlag { nextEngagement, - nextDisEngagement + nextDisEngagement, + nextUserFlag, }; -enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical }; +enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; class Replay : public QObject { Q_OBJECT diff --git a/tools/sim/bridge.py b/tools/sim/bridge.py index fa4ce2b41d9742..f008b9e716cde9 100755 --- a/tools/sim/bridge.py +++ b/tools/sim/bridge.py @@ -179,7 +179,7 @@ def gps_callback(gps, vehicle_state): ] dat.gpsLocationExternal = { - "timestamp": int(time.time() * 1000), + "unixTimestampMillis": int(time.time() * 1000), "flags": 1, # valid fix "accuracy": 1.0, "verticalAccuracy": 1.0, diff --git a/tools/sim/tests/test_carla_integration.py b/tools/sim/tests/test_carla_integration.py index 43db783f4e38d4..f4673919674d29 100755 --- a/tools/sim/tests/test_carla_integration.py +++ b/tools/sim/tests/test_carla_integration.py @@ -77,7 +77,7 @@ def test_engage(self): while time.monotonic() < start_time + max_time_per_step: sm.update() - q.put("cruise_up") # Try engaging + q.put("cruise_down") # Try engaging if sm.all_alive() and sm['controlsState'].active: control_active += 1 diff --git a/tools/ssh/README.md b/tools/ssh/README.md index 29d33493282076..67b5271a42ac48 100644 --- a/tools/ssh/README.md +++ b/tools/ssh/README.md @@ -7,11 +7,11 @@ In order to SSH into your device, you'll need a GitHub account with SSH keys. Se * Enable SSH in your device's settings * Enter your GitHub username in the device's settings * Connect to your device - * Username: `comma` (comma three) + * Username: `comma` * Port: `22` or `8022` -Here's an example command for connecting to your device using its tethered connection: -`ssh root@192.168.43.1` +Here's an example command for connecting to your device using its tethered connection:
+`ssh comma@192.168.43.1` For doing development work on device, it's recommended to use [SSH agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding). @@ -34,7 +34,8 @@ Requires [comma SIM with comma prime](https://comma.ai/shop) activated with comm ## Recommended .ssh/config -With the below ssh configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`. For example, `ssh comma-ffffffffffffffff`. +With the below SSH configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`.
+For example: `ssh comma-ffffffffffffffff` ``` Host comma-* diff --git a/tools/ubuntu_setup.sh b/tools/ubuntu_setup.sh index 379df37adb6c84..863b853718fa56 100755 --- a/tools/ubuntu_setup.sh +++ b/tools/ubuntu_setup.sh @@ -108,7 +108,11 @@ if [ -f "/etc/os-release" ]; then if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi - install_ubuntu_focal_requirements + if [ "$UBUNTU_CODENAME" = "jammy" ]; then + install_ubuntu_jammy_requirements + else + install_ubuntu_focal_requirements + fi esac else echo "No /etc/os-release in the system" diff --git a/update_requirements.sh b/update_requirements.sh index 94b14496f151a9..719a28c3599ead 100755 --- a/update_requirements.sh +++ b/update_requirements.sh @@ -4,11 +4,26 @@ set -e DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" cd $DIR +RC_FILE="${HOME}/.$(basename ${SHELL})rc" +if [ "$(uname)" == "Darwin" ] && [ $SHELL == "/bin/bash" ]; then + RC_FILE="$HOME/.bash_profile" +fi + if ! command -v "pyenv" > /dev/null 2>&1; then echo "pyenv install ..." curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash - export PATH=$HOME/.pyenv/bin:$HOME/.pyenv/shims:$PATH + + echo -e "\n. ~/.pyenvrc" >> $RC_FILE + cat < "${HOME}/.pyenvrc" +if [ -z "\$PYENV_ROOT" ]; then + export PATH=\$HOME/.pyenv/bin:\$HOME/.pyenv/shims:\$PATH + export PYENV_ROOT="\$HOME/.pyenv" + eval "\$(pyenv init -)" + eval "\$(pyenv virtualenv-init -)" +fi +EOF fi +source $RC_FILE export MAKEFLAGS="-j$(nproc)"