Merge branch 'master' into pq-upstream

pull/24768/head
Jason Young 3 years ago
commit b4257fbec5
  1. 2
      .github/ISSUE_TEMPLATE/car_bug_report.yml
  2. 3
      .github/workflows/prebuilt.yaml
  3. 3
      .github/workflows/release.yaml
  4. 18
      .github/workflows/selfdrive_tests.yaml
  5. 40
      .github/workflows/tools_tests.yaml
  6. 4
      .gitignore
  7. 2
      .pre-commit-config.yaml
  8. 9
      .pylintrc
  9. 7
      Dockerfile.openpilot
  10. 58
      Jenkinsfile
  11. 8
      Pipfile
  12. 561
      Pipfile.lock
  13. 13
      README.md
  14. 17
      RELEASES.md
  15. 27
      SConstruct
  16. 2
      cereal
  17. 2
      common/api/__init__.py
  18. 2
      common/basedir.py
  19. 8
      common/modeldata.h
  20. 4
      common/params.cc
  21. 2
      common/realtime.py
  22. 5
      common/swaglog.cc
  23. 2
      common/tests/test_swaglog.cc
  24. 183
      docs/CARS.md
  25. 8
      docs/c_docs.rst
  26. 6
      docs/conf.py
  27. 2
      laika_repo
  28. 6
      launch_chffrplus.sh
  29. 2
      opendbc
  30. 2
      panda
  31. 2
      rednose_repo
  32. 92
      release/files_common
  33. 8
      release/files_tici
  34. 2
      scripts/disable-powersave.py
  35. BIN
      selfdrive/assets/fonts/opensans_bold.ttf
  36. BIN
      selfdrive/assets/fonts/opensans_regular.ttf
  37. BIN
      selfdrive/assets/fonts/opensans_semibold.ttf
  38. 8
      selfdrive/athena/athenad.py
  39. 4
      selfdrive/athena/manage_athenad.py
  40. 4
      selfdrive/athena/registration.py
  41. 9
      selfdrive/athena/tests/test_athenad.py
  42. 2
      selfdrive/boardd/boardd.cc
  43. 2
      selfdrive/boardd/main.cc
  44. 4
      selfdrive/boardd/pandad.py
  45. 2
      selfdrive/boardd/tests/boardd_old.py
  46. 2
      selfdrive/boardd/tests/test_boardd_loopback.py
  47. 30
      selfdrive/car/CARS_template.md
  48. 4
      selfdrive/car/body/carcontroller.py
  49. 1
      selfdrive/car/body/interface.py
  50. 14
      selfdrive/car/car_helpers.py
  51. 4
      selfdrive/car/chrysler/carcontroller.py
  52. 108
      selfdrive/car/chrysler/carstate.py
  53. 9
      selfdrive/car/chrysler/chryslercan.py
  54. 59
      selfdrive/car/chrysler/interface.py
  55. 9
      selfdrive/car/chrysler/values.py
  56. 2
      selfdrive/car/disable_ecu.py
  57. 4
      selfdrive/car/docs.py
  58. 69
      selfdrive/car/docs_definitions.py
  59. 90
      selfdrive/car/ecu_addrs.py
  60. 15
      selfdrive/car/ford/carcontroller.py
  61. 5
      selfdrive/car/ford/interface.py
  62. 6
      selfdrive/car/ford/radar_interface.py
  63. 4
      selfdrive/car/ford/values.py
  64. 46
      selfdrive/car/fw_versions.py
  65. 26
      selfdrive/car/gm/carcontroller.py
  66. 18
      selfdrive/car/gm/carstate.py
  67. 53
      selfdrive/car/gm/interface.py
  68. 33
      selfdrive/car/gm/values.py
  69. 31
      selfdrive/car/honda/carcontroller.py
  70. 99
      selfdrive/car/honda/carstate.py
  71. 55
      selfdrive/car/honda/hondacan.py
  72. 22
      selfdrive/car/honda/interface.py
  73. 63
      selfdrive/car/honda/values.py
  74. 6
      selfdrive/car/hyundai/carcontroller.py
  75. 8
      selfdrive/car/hyundai/carstate.py
  76. 38
      selfdrive/car/hyundai/interface.py
  77. 55
      selfdrive/car/hyundai/values.py
  78. 43
      selfdrive/car/interfaces.py
  79. 24
      selfdrive/car/isotp_parallel_query.py
  80. 30
      selfdrive/car/mazda/carcontroller.py
  81. 1
      selfdrive/car/mazda/carstate.py
  82. 5
      selfdrive/car/mazda/interface.py
  83. 6
      selfdrive/car/mazda/values.py
  84. 2
      selfdrive/car/mock/interface.py
  85. 33
      selfdrive/car/nissan/carcontroller.py
  86. 12
      selfdrive/car/nissan/interface.py
  87. 21
      selfdrive/car/subaru/carcontroller.py
  88. 13
      selfdrive/car/subaru/interface.py
  89. 4
      selfdrive/car/subaru/values.py
  90. 4
      selfdrive/car/tesla/interface.py
  91. 2
      selfdrive/car/tesla/values.py
  92. 4
      selfdrive/car/tests/routes.py
  93. 7
      selfdrive/car/tests/test_car_interfaces.py
  94. 19
      selfdrive/car/tests/test_docs.py
  95. 2
      selfdrive/car/tests/test_models.py
  96. 29
      selfdrive/car/torque_data/override.yaml
  97. 96
      selfdrive/car/torque_data/params.yaml
  98. 75
      selfdrive/car/torque_data/substitute.yaml
  99. 41
      selfdrive/car/toyota/interface.py
  100. 8
      selfdrive/car/toyota/tunes.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,6 +1,6 @@
name: Car bug report name: Car bug report
description: For issues with a particular car make or model description: For issues with a particular car make or model
labels: ["car bug"] labels: ["car", "bug"]
body: body:
- type: markdown - type: markdown

@ -25,11 +25,12 @@ jobs:
IMAGE_NAME: openpilot-prebuilt IMAGE_NAME: openpilot-prebuilt
steps: steps:
- name: Wait for green check mark - name: Wait for green check mark
uses: lewagon/wait-on-check-action@v0.2 uses: commaai/wait-on-check-action@f16fc3bb6cd4886520b4e9328db1d42104d5cadc
with: with:
ref: master ref: master
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build prebuilt' running-workflow-name: 'build prebuilt'
check-regexp: ^((?!.*(build master-ci).*).)*$
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true

@ -12,11 +12,12 @@ jobs:
if: github.repository == 'commaai/openpilot' if: github.repository == 'commaai/openpilot'
steps: steps:
- name: Wait for green check mark - name: Wait for green check mark
uses: lewagon/wait-on-check-action@v0.2 uses: commaai/wait-on-check-action@f16fc3bb6cd4886520b4e9328db1d42104d5cadc
with: with:
ref: master ref: master
wait-interval: 30 wait-interval: 30
running-workflow-name: 'build master-ci' running-workflow-name: 'build master-ci'
check-regexp: ^((?!.*(build prebuilt).*).)*$
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true submodules: true

@ -20,8 +20,7 @@ env:
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 $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 $BASE_IMAGE /bin/sh -c
BUILD_CL: | BUILD_CL: |
docker pull $(grep -iohP '(?<=^from)\s+\S+' Dockerfile.openpilot_base_cl) || true docker pull $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest || true
docker pull $DOCKER_REGISTRY/$BASE_IMAGE:latest || true
docker build --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl . docker build --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
RUN_CL: 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 $CL_BASE_IMAGE /bin/sh -c RUN_CL: 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 $CL_BASE_IMAGE /bin/sh -c
@ -95,7 +94,7 @@ jobs:
run: | run: |
${{ env.RUN }} "scons -j$(nproc) --extras --test && \ ${{ env.RUN }} "scons -j$(nproc) --extras --test && \
rm -rf /tmp/scons_cache/* && \ rm -rf /tmp/scons_cache/* && \
scons -j$(nproc) --cache-populate" scons -j$(nproc) --extras --test --cache-populate"
#build_mac: #build_mac:
# name: build macos # name: build macos
@ -201,12 +200,15 @@ jobs:
submodules: true submodules: true
- name: Build Docker image - name: Build Docker image
run: eval "$BUILD" run: eval "$BUILD"
- name: Push to container registry
run: |
$DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
- name: Build CL Docker image - name: Build CL Docker image
run: eval "$BUILD_CL" run: eval "$BUILD_CL"
- name: Push to container registry - name: Push to container registry
run: | run: |
$DOCKER_LOGIN $DOCKER_LOGIN
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest
static_analysis: static_analysis:
@ -297,18 +299,22 @@ jobs:
$UNIT_TEST selfdrive/loggerd && \ $UNIT_TEST selfdrive/loggerd && \
$UNIT_TEST selfdrive/car && \ $UNIT_TEST selfdrive/car && \
$UNIT_TEST selfdrive/locationd && \ $UNIT_TEST selfdrive/locationd && \
selfdrive/locationd/test/_test_locationd_lib.py && \
$UNIT_TEST selfdrive/athena && \ $UNIT_TEST selfdrive/athena && \
$UNIT_TEST selfdrive/thermald && \ $UNIT_TEST selfdrive/thermald && \
$UNIT_TEST selfdrive/hardware/tici && \ $UNIT_TEST selfdrive/hardware/tici && \
$UNIT_TEST selfdrive/modeld && \ $UNIT_TEST selfdrive/modeld && \
$UNIT_TEST tools/lib/tests && \ $UNIT_TEST tools/lib/tests && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
./selfdrive/ui/tests/test_translations.py && \
./common/tests/test_util && \ ./common/tests/test_util && \
./common/tests/test_swaglog && \ ./common/tests/test_swaglog && \
./selfdrive/boardd/tests/test_boardd_usbprotocol && \ ./selfdrive/boardd/tests/test_boardd_usbprotocol && \
./selfdrive/loggerd/tests/test_logger &&\ ./selfdrive/loggerd/tests/test_logger &&\
./system/proclogd/tests/test_proclog && \ ./system/proclogd/tests/test_proclog && \
./selfdrive/ui/replay/tests/test_replay && \ ./tools/replay/tests/test_replay && \
./selfdrive/camerad/test/ae_gray_test && \ ./system/camerad/test/ae_gray_test && \
coverage xml" coverage xml"
- name: "Upload coverage to Codecov" - name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2 uses: codecov/codecov-action@v2

@ -21,6 +21,46 @@ env:
GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/comma_download_cache:/tmp/comma_download_cache $BASE_IMAGE /bin/sh -c
jobs: 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: plotjuggler:
name: plotjuggler name: plotjuggler
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

4
.gitignore vendored

@ -49,8 +49,8 @@ selfdrive/loggerd/loggerd
selfdrive/loggerd/bootlog selfdrive/loggerd/bootlog
selfdrive/sensord/_gpsd selfdrive/sensord/_gpsd
selfdrive/sensord/_sensord selfdrive/sensord/_sensord
selfdrive/camerad/camerad system/camerad/camerad
selfdrive/camerad/test/ae_gray_test system/camerad/test/ae_gray_test
selfdrive/modeld/_modeld selfdrive/modeld/_modeld
selfdrive/modeld/_dmonitoringmodeld selfdrive/modeld/_dmonitoringmodeld
/src/ /src/

@ -20,7 +20,7 @@ repos:
hooks: hooks:
- id: mypy - id: mypy
exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)/' exclude: '^(pyextra/)|(cereal/)|(rednose/)|(panda/)|(laika/)|(opendbc/)|(laika_repo/)|(rednose_repo/)/'
additional_dependencies: ['lxml', 'numpy', 'types-atomicwrites', 'types-pycurl', 'types-requests', 'types-certifi'] additional_dependencies: ['types-PyYAML', 'lxml', 'numpy', 'types-atomicwrites', 'types-pycurl', 'types-requests', 'types-certifi']
args: args:
- --warn-redundant-casts - --warn-redundant-casts
- --warn-return-any - --warn-return-any

@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may # A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may # be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code # run arbitrary code
extension-pkg-whitelist=scipy cereal.messaging.messaging_pyx extension-pkg-whitelist=scipy,cereal.messaging.messaging_pyx,PyQt5,av
# Add files or directories to the blacklist. They should be base names, not # Add files or directories to the blacklist. They should be base names, not
# paths. # paths.
@ -250,13 +250,6 @@ max-line-length=100
# Maximum number of lines in a module # Maximum number of lines in a module
max-module-lines=1000 max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
# Allow the body of a class to be on the same line as the declaration if body # Allow the body of a class to be on the same line as the declaration if body
# contains single statement. # contains single statement.
single-line-class-stmt=no single-line-class-stmt=no

@ -8,11 +8,6 @@ ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
RUN mkdir -p ${OPENPILOT_PATH} RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH} WORKDIR ${OPENPILOT_PATH}
COPY Pipfile Pipfile.lock $OPENPILOT_PATH
RUN pip install --no-cache-dir pipenv==2021.5.29 pip==21.3.1 && \
pipenv install --system --deploy --dev --clear && \
pip uninstall -y pipenv
COPY SConstruct ${OPENPILOT_PATH} COPY SConstruct ${OPENPILOT_PATH}
COPY ./pyextra ${OPENPILOT_PATH}/pyextra COPY ./pyextra ${OPENPILOT_PATH}/pyextra
@ -30,4 +25,4 @@ COPY ./panda ${OPENPILOT_PATH}/panda
COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system COPY ./system ${OPENPILOT_PATH}/system
RUN scons -j$(nproc) RUN scons --cache-readonly -j$(nproc)

58
Jenkinsfile vendored

@ -10,6 +10,7 @@ export TEST_DIR=${env.TEST_DIR}
export SOURCE_DIR=${env.SOURCE_DIR} export SOURCE_DIR=${env.SOURCE_DIR}
export GIT_BRANCH=${env.GIT_BRANCH} export GIT_BRANCH=${env.GIT_BRANCH}
export GIT_COMMIT=${env.GIT_COMMIT} export GIT_COMMIT=${env.GIT_COMMIT}
export AZURE_TOKEN='${env.AZURE_TOKEN}'
source ~/.bash_profile source ~/.bash_profile
if [ -f /TICI ]; then if [ -f /TICI ]; then
@ -42,8 +43,10 @@ def phone_steps(String device_type, steps) {
pipeline { pipeline {
agent none agent none
environment { environment {
CI = "1"
TEST_DIR = "/data/openpilot" TEST_DIR = "/data/openpilot"
SOURCE_DIR = "/data/openpilot_source/" SOURCE_DIR = "/data/openpilot_source/"
AZURE_TOKEN = credentials('azure_token')
} }
options { options {
timeout(time: 4, unit: 'HOURS') timeout(time: 4, unit: 'HOURS')
@ -74,13 +77,37 @@ pipeline {
} }
} }
stages {
stage('On-device Tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
stages {
stage('parallel tests') {
parallel { parallel {
stage('simulator') {
agent {
dockerfile {
filename 'Dockerfile.sim_nvidia'
dir 'tools/sim'
args '--user=root'
}
}
steps {
sh "git config --global --add safe.directory ${WORKSPACE}"
sh "git lfs pull"
lock(resource: "", label: "simulator", inversePrecedence: true, quantity: 1) {
sh "${WORKSPACE}/tools/sim/build_container.sh"
sh "DETACH=1 ${WORKSPACE}/tools/sim/start_carla.sh"
sh "${WORKSPACE}/tools/sim/start_openpilot_docker.sh"
}
}
post {
always {
sh "docker kill carla_sim || true"
sh "rm -rf ${WORKSPACE}/* || true"
sh "rm -rf .* || true"
}
}
}
stage('build') { stage('build') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
environment { environment {
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}" R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
} }
@ -96,10 +123,11 @@ pipeline {
} }
stage('HW + Unit Tests') { stage('HW + Unit Tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici2", [ phone_steps("tici2", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
["test power draw", "python selfdrive/hardware/tici/test_power_draw.py"], ["test power draw", "python system/hardware/tici/test_power_draw.py"],
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"], ["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"], ["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"],
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"], ["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python selfdrive/loggerd/tests/test_encoder.py"],
@ -109,16 +137,18 @@ pipeline {
} }
stage('camerad') { stage('camerad') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici-party", [ phone_steps("tici-party", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
["test camerad", "python selfdrive/camerad/test/test_camerad.py"], ["test camerad", "python system/camerad/test/test_camerad.py"],
["test exposure", "python selfdrive/camerad/test/test_exposure.py"], ["test exposure", "python system/camerad/test/test_exposure.py"],
]) ])
} }
} }
stage('replay') { stage('replay') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici3", [ phone_steps("tici3", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
@ -126,20 +156,8 @@ pipeline {
]) ])
} }
} }
}
}
} }
post {
always {
cleanWs()
}
}
}
}
} }
} }
} }

@ -11,7 +11,7 @@ coverage = "*"
dictdiffer = "*" dictdiffer = "*"
fastcluster = "*" fastcluster = "*"
hexdump = "*" hexdump = "*"
hypothesis = "*" hypothesis = "==6.46.7"
inputs = "*" inputs = "*"
lru-dict = "*" lru-dict = "*"
markdown-it-py = "*" markdown-it-py = "*"
@ -39,7 +39,8 @@ breathe = "*"
subprocess32 = "*" subprocess32 = "*"
tenacity = "*" tenacity = "*"
mpld3 = "*" mpld3 = "*"
carla = {version = "==0.9.12", markers="platform_system != 'Darwin'"} carla = {version = "==0.9.13", markers="platform_system != 'Darwin'"}
ft4222 = "*"
[packages] [packages]
atomicwrites = "*" atomicwrites = "*"
@ -58,6 +59,7 @@ json-rpc = "*"
libusb1 = "*" libusb1 = "*"
nose = "*" nose = "*"
numpy = "*" numpy = "*"
protobuf = "==3.20.1"
onnx = "*" onnx = "*"
onnxruntime-gpu = {version = "*", markers="platform_system != 'Darwin'"} onnxruntime-gpu = {version = "*", markers="platform_system != 'Darwin'"}
pillow = "*" pillow = "*"
@ -84,6 +86,8 @@ urllib3 = "*"
utm = "*" utm = "*"
websocket_client = "*" websocket_client = "*"
hatanaka = "==2.4" hatanaka = "==2.4"
PyQt5 = "==5.15.4"
PyQt5-sip = "==12.9.0"
[requires] [requires]
python_version = "3.8" python_version = "3.8"

561
Pipfile.lock generated

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "50d3486820d05997c1e084e70362fa080e0a294faa01c7f1d43219e44f94a80b" "sha256": "d2629168f477b3a14f68f26e3b63ea9797b20599c066b2aa23a027bcee2ca40a"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -18,11 +18,11 @@
"default": { "default": {
"astroid": { "astroid": {
"hashes": [ "hashes": [
"sha256:14ffbb4f6aa2cf474a0834014005487f7ecd8924996083ab411e7fa0b508ce0b", "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6",
"sha256:f4e4ec5294c4b07ac38bab9ca5ddd3914d4bf46f9006eb5c0ae755755061044e" "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"
], ],
"markers": "python_full_version >= '3.6.2'", "markers": "python_full_version >= '3.6.2'",
"version": "==2.11.5" "version": "==2.11.6"
}, },
"atomicwrites": { "atomicwrites": {
"hashes": [ "hashes": [
@ -146,7 +146,7 @@
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df" "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
], ],
"markers": "python_version >= '3'", "markers": "python_full_version >= '3.5.0'",
"version": "==2.0.12" "version": "==2.0.12"
}, },
"click": { "click": {
@ -347,7 +347,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
], ],
"markers": "python_version >= '3'", "markers": "python_full_version >= '3.5.0'",
"version": "==3.3" "version": "==3.3"
}, },
"importlib-metadata": { "importlib-metadata": {
@ -689,21 +689,35 @@
"hashes": [ "hashes": [
"sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf", "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf",
"sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f", "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f",
"sha256:0d4719e724472e296062ba8e82a36d64693fcfdb550d9dff98af70ca79eafe3d",
"sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f", "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f",
"sha256:2b35602cb65d53c168c104469e714bf68670335044c38eee3c899d6a8af03ffc",
"sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7", "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7",
"sha256:32fff501b6df3050936d1839b80ea5899bf34db24792d223d7640611f67de15a",
"sha256:34400fd76f85bdae9a2e9c1444ea4699c0280962423eff4418765deceebd81b5",
"sha256:3767c64593a49c7ac0accd08ed39ce42744405f0989d468f0097a17496fdbe84",
"sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996", "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996",
"sha256:3f2ed842e8ca43b790cb4a101bcf577226e0ded98a6a6ba2d5e12095a08dc4da",
"sha256:52c1e44e25f2949be7ffa7c66acbfea940b0945dd416920231f7cb30ea5ac6db",
"sha256:5d9b5c8270461706973c3871c6fbdd236b51dfe9dab652f1fb6a36aa88287e47",
"sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067", "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067",
"sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c", "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c",
"sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7", "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7",
"sha256:72d357cc4d834cc85bd957e8b8e1f4b64c2eac9ca1a942efeb8eb2e723fca852",
"sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9", "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9",
"sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c", "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c",
"sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739", "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739",
"sha256:79cd8d0a269b714f6b32641f86928c718e8d234466919b3f552bfb069dbb159b",
"sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91", "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91",
"sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c", "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c",
"sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153", "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153",
"sha256:a4c0c6f2f95a559e59a0258d3e4b186f340cbdc5adec5ce1bc06d01972527c88",
"sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9", "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9",
"sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388", "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388",
"sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e", "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e",
"sha256:b309fda192850ac4184ca1777aab9655564bc8d10a9cc98f10e1c8bf11295c22",
"sha256:b3d7d4b4945fe3c001403b6c24442901a5e58c0a3059290f5a63523ed4435f82",
"sha256:c8829092c5aeb61619161269b2f8a2e36fd7cb26abbd9282d3bc453f02769146",
"sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab", "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab",
"sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde", "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde",
"sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531", "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531",
@ -712,7 +726,7 @@
"sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20", "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20",
"sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3" "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"
], ],
"markers": "python_version >= '3.7'", "index": "pypi",
"version": "==3.20.1" "version": "==3.20.1"
}, },
"psutil": { "psutil": {
@ -841,11 +855,11 @@
}, },
"pylint": { "pylint": {
"hashes": [ "hashes": [
"sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731", "sha256:549261e0762c3466cc001024c4419c08252cb8c8d40f5c2c6966fea690e7fe2a",
"sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526" "sha256:bb71e6d169506de585edea997e48d9ff20c0dc0e2fbc1d166bad6b640120326b"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.13.9" "version": "==2.14.1"
}, },
"pyopencl": { "pyopencl": {
"hashes": [ "hashes": [
@ -890,6 +904,53 @@
"index": "pypi", "index": "pypi",
"version": "==2022.1.5" "version": "==2022.1.5"
}, },
"pyqt5": {
"hashes": [
"sha256:213bebd51821ed89b4d5b35bb10dbe67564228b3568f463a351a08e8b1677025",
"sha256:2a69597e0dd11caabe75fae133feca66387819fc9bc050f547e5551bce97e5be",
"sha256:883a549382fc22d29a0568f3ef20b38c8e7ab633a59498ac4eb63a3bf36d3fd3",
"sha256:8c0848ba790a895801d5bfd171da31cad3e551dbcc4e59677a3b622de2ceca98",
"sha256:a88526a271e846e44779bb9ad7a738c6d3c4a9d01e15a128ecfc6dd4696393b7"
],
"index": "pypi",
"version": "==5.15.4"
},
"pyqt5-qt5": {
"hashes": [
"sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a",
"sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962",
"sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154",
"sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"
],
"version": "==5.15.2"
},
"pyqt5-sip": {
"hashes": [
"sha256:055581c6fed44ba4302b70eeb82e979ff70400037358908f251cd85cbb3dbd93",
"sha256:0fc9aefacf502696710b36cdc9fa2a61487f55ee883dbcf2c2a6477e261546f7",
"sha256:42274a501ab4806d2c31659170db14c282b8313d2255458064666d9e70d96206",
"sha256:4347bd81d30c8e3181e553b3734f91658cfbdd8f1a19f254777f906870974e6d",
"sha256:485972daff2fb0311013f471998f8ec8262ea381bded244f9d14edaad5f54271",
"sha256:4f8e05fe01d54275877c59018d8e82dcdd0bc5696053a8b830eecea3ce806121",
"sha256:69a3ad4259172e2b1aa9060de211efac39ddd734a517b1924d9c6c0cc4f55f96",
"sha256:6a8701892a01a5a2a4720872361197cc80fdd5f49c8482d488ddf38c9c84f055",
"sha256:6d5bca2fc222d58e8093ee8a81a6e3437067bb22bc3f86d06ec8be721e15e90a",
"sha256:83c3220b1ca36eb8623ba2eb3766637b19eb0ce9f42336ad8253656d32750c0a",
"sha256:a25b9843c7da6a1608f310879c38e6434331aab1dc2fe6cb65c14f1ecf33780e",
"sha256:ac57d796c78117eb39edd1d1d1aea90354651efac9d3590aac67fa4983f99f1f",
"sha256:b09f4cd36a4831229fb77c424d89635fa937d97765ec90685e2f257e56a2685a",
"sha256:c446971c360a0a1030282a69375a08c78e8a61d568bfd6dab3dcc5cf8817f644",
"sha256:c5216403d4d8d857ec4a61f631d3945e44fa248aa2415e9ee9369ab7c8a4d0c7",
"sha256:d3e4489d7c2b0ece9d203ae66e573939f7f60d4d29e089c9f11daa17cfeaae32",
"sha256:d59af63120d1475b2bf94fe8062610720a9be1e8940ea146c7f42bb449d49067",
"sha256:d85002238b5180bce4b245c13d6face848faa1a7a9e5c6e292025004f2fd619a",
"sha256:d8b2bdff7bbf45bc975c113a03b14fd669dc0c73e1327f02706666a7dd51a197",
"sha256:dd05c768c2b55ffe56a9d49ce6cc77cdf3d53dbfad935258a9e347cbfd9a5850",
"sha256:fc43f2d7c438517ee33e929e8ae77132749c15909afab6aeece5fcf4147ffdb5"
],
"index": "pypi",
"version": "==12.9.0"
},
"pyserial": { "pyserial": {
"hashes": [ "hashes": [
"sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb",
@ -954,74 +1015,74 @@
}, },
"pyzmq": { "pyzmq": {
"hashes": [ "hashes": [
"sha256:011a45c846ec69a3671ed15893b74b6ad608800c89ac6d0f0411e2137c6b313d", "sha256:057176dd3f5ccf5aad4abd662d76b6a39bbf799baaf2f39cd4fdaf2eab326e43",
"sha256:011f26841dd56ed87e464c98023dbbd4c0b3ab8802a045de3ea83e0187eb8145", "sha256:05ec90a8da618f2398f9d1aa20b18a9ef332992c6ac23e8c866099faad6ef0d6",
"sha256:0258563bf69f6ca305204354f171e0627a9bf8fe78c9d4f63a5e2447035cbb4b", "sha256:154de02b15422af28b53d29a02de72121ba503634955017255573fc1f995143d",
"sha256:07d2008e51718fba60641e5d1a0646b222b7929f16f6e7cf0834b8439f42c9e8", "sha256:16b832adb5d8716f46051da5533c480250bf126984ce86804db6137a3a7f931b",
"sha256:0a787f7870cba38d655c68ea7ae14bb6c3e9e19bb618d0c2412513321eeaeb80", "sha256:1df26aa854bdd3a8341bf199064dd6aa6e240f2eaa3c9fa8d217e5d8b868c73e",
"sha256:0a89b9860d2171bcf674648dc8186db9cf3b773ad3c0610a2c7bf189cf3560b6", "sha256:28f9164fb2658b7b414fa0894c75b1a9c61375774cdc1bdb7298beb042a2cd87",
"sha256:0de8a7e13ffacfe33c89acc0d7bfa2f5bde94e3f74b7f1e4d43c97ce17864d77", "sha256:2951c29b8649f3672af9dca8ff61d86310d3664d9629788b1c66422fb13b1239",
"sha256:12a53f5c13edf12547ce495afebdd5ab11c1b67ea078a941b21e13161783741a", "sha256:2b08774057ae7ce8a2eb4e7d54db05358234440706ce43a85814500c5d7bd22e",
"sha256:12eac2294d48ee27d1eaef7e214acedb394e4c95e3a1f6e4467099b82d58ef73", "sha256:2e2ac40f7a91c740ec68d6db07ae19ea9259c959333c68bee56ab2c799a67d66",
"sha256:176be6c348dbec04e8e0d41e810743b7084b73e50954a6fedeeafc65d7fa9290", "sha256:312e56799410c34797417a4060a8bd37d4db1f06d1ec0c54f7c8fd81e0d90376",
"sha256:1d480d48253f61ff90115b8069ed32f51a0907eb19101c4a5ae0b9a5973e40ad", "sha256:38f778a74e3889392e949326cfd0e9b2eb37dcbb2980d98fad2c51703d523db2",
"sha256:21792f4d0fcc5040978ee211c033e915d8b6608ea8a5b33fe197a04f0d43e991", "sha256:3955dd5bbbe02f454655296ee36a66c334c7102a29b8458223d168c0380edfd5",
"sha256:277b3ebc684b369a57a186a9acf629c1b01247eb04d1105536ef2dae5f61168a", "sha256:425ba851a6f9892bde1da2024d82e2fe6796bd77e3391fb96665c50fe9d4c6a5",
"sha256:2f227150148e7c3db7ecd8a58500439979f556e15455841a30b6d121755b14bc", "sha256:48bbc2db041ab28eeee4a3e8ada0ed336640946dd5a8e53dbd3805f9dbdcf0dc",
"sha256:34b143751e9b2b89cf9b656081f1b2842a563c4c9ffc8465531875daf546e772", "sha256:4fbcd657cda75574fd1315a4c44bd322bc2e219039fb09f146bbe6f8aef039e9",
"sha256:3f3807e81bf51d4c63eb12a21920614e0e840645418e9f2e3b5ffdd5991b3415", "sha256:523ba7fd4d8fe75ad09c1e574a648892b75a97d0cfc8005727681053ac19555b",
"sha256:3fa7126d532effee452c0ab395ab3cbef1c06fd6870ab7e681f812ba9e685cfa", "sha256:53b2c1326c2e484d450932d2be739f064b7cb572faabec38386098a28516a529",
"sha256:434044eec7f9df08fc5ca5c9bdd1a4bb08663679d43ebe7b9849713956f4d85f", "sha256:540d7146c3cdc9bbffab039ea067f494eba24d1abe5bd33eb9f963c01e3305d4",
"sha256:4d861ae20040afc17adef33053c328667da78d4d3676b2936788fd031665e3a8", "sha256:563d4281c4dbdf647d93114420151d33f895afc4c46b7115a67a0aa5347e6624",
"sha256:536491ad640448f14d8aa2dc497c354a348f216eb23513bf5aa0ac40e2b02577", "sha256:67a049bcf967a39993858beed873ed3405536019820922d4efacfe35ab3da51a",
"sha256:5619f6598d6fd30778053ae2daa48a7c54029816648b908270b751411fd52e74", "sha256:67ec63ae3c9c1fa2e077fcb42e77035e2121a04f987464bdf9945a28535d30ad",
"sha256:591b455546d34bb96aa453dd9666bddb8c81314e23dbf2606f9614acf7e73d9f", "sha256:68e22c5d3be451e87d47f956b397a7823bfbde2176341bc902fba30f96831d7e",
"sha256:5a13171268f05d127e31b4c369b753733f67dbb0d765901ef625a115feb5c7de", "sha256:6ab4b6108e69f63c917cd7ef7217c5727955b1ac90600e44a13ed5312019a014",
"sha256:5eaf7e0841d3d8d1d92838c8b56f98cb9bf35b14bcbe4efa281e4812ef4be728", "sha256:6bd7f18bd4cf51ea8d7e54825902cf36f9d2f35cc51ef618373988d5398b8dd0",
"sha256:6c09e6e5c4baf0959287943dc8170624d739ae555d334e896a94d9de01c7bb21", "sha256:6cd53e861bccc0bdc4620f68fb4a91d5bcfe9f4213cf8e200fa498044d33a6dc",
"sha256:6e2093a97bf3f6008a4be6b5bae8ae3fc409f18373593bef19dd7b381ab8030c", "sha256:6d346e551fa64b89d57a4ac74b9bc66703413f02f50093e089e861999ec5cccc",
"sha256:7b518ad9cdbaaeb1a9da3444797698871ae2eeae34ff9a656d5150d37e1e42a1", "sha256:6ff8708fabc9f9bc2949f457d39b4088c9656c4c9ac15fbbbbaafce8f6d07833",
"sha256:7ca7d77f24644298cbe53bc279eb7ca05f3b8637473d392f0c9f34b37f08b49a", "sha256:7626e8384275a7dea6f3d1f749fb5e00299042e9c895fc3dbe24cb154909c242",
"sha256:7eca5902ff41575d9a26f91fc750018b7eb129600ea600fe69ce852fbdfab4e2", "sha256:7e7346b2b33dcd4a2171dd8a9870ae283eec8f6231dcbcf237a0f41e74751a50",
"sha256:8951830d6a00636b3af478091f9668ecc486f1dad01b975527957fd1d8c31bfd", "sha256:81623c67cb71b93b5f7e06c9107f3781738ae86866db830c950223d87af2a235",
"sha256:8c234aefeef034c5d6de452e2af5173a95ea06315b685db703091e6f937a6e60", "sha256:83f1c76068faf62c32a36dd62dc4db642c2027bbbd960f8f6345b59e9d4dc472",
"sha256:9273f6d1da1018822f41630fb0f3fe208e8e70e5d5e780795326900cfa22d8b6", "sha256:8679bb1dd723ecbea03b1f96c98972815775fd8ec756c440a14f289c436c472e",
"sha256:9622d9560a6fd8d589816cdcec6946642cb4e070b3f68be1d3779b52cf240f73", "sha256:86fb683cb9a9c0bb7476988b7957393ecdd22777d87d804442c66e62c99197f9",
"sha256:9ef2d1476cea927ba33a29f59aa128ce3b174e81083cbd091dd3149af741c85d", "sha256:8757c62f7960cd26122f7aaaf86eda1e016fa85734c3777b8054dd334d7dea4d",
"sha256:9feb7ccd426ff2158ce79f4c87a8a1600ed4f77e65e2fffda2b42638b2bc73e4", "sha256:894be7d17228e7328cc188096c0162697211ec91761f6812fff12790cbe11c66",
"sha256:a0b8528aefceb787f41ad429f3210a3c6b52e99f85413416e3d0c9e6d035f8ac", "sha256:8a0f240bf43c29be1bd82d77e602a61c798e9de02e5f8bb7bb414cb814f43236",
"sha256:a2e4d70d34112997a32c8193fae2579aec854745f8730031e5d84cc579fd98ff", "sha256:8c3abf7eab5b76ae162c4fbb16d514a947fc57fd995b64e5ea8ef8ba3b888a69",
"sha256:a37f0ec88e220326803084208d80229218b309d728954ab747ab21cca33424aa", "sha256:93332c6972e4c91522c4810e907f3aea067424338071161b39cacded022559df",
"sha256:a45f5c0477d12df05ef2e2922b49b7c0ae9d0f4ff9b6bb0d666558df0ef37122", "sha256:97d6c676dc97d593625d9fc48154f2ffeabb619a1e6fe8d2a5b53f97e3e9bdee",
"sha256:a64b9cce166396df5f33894074d6515778d48c63aae5ee1abd86d8bbc5a711d8", "sha256:99dd85f0ca1db8d17a01a25c2bbb7784d25a2d39497c6beddbe96bff74194e04",
"sha256:a89285fedbeca483a855a77285453e21e4fc86ef0944bc018ef4b3033aa04ad2", "sha256:9c7fb691fb07ec7ab99fd173bb0e7e0248d31bf83d484a87b917a342f63812c9",
"sha256:a8f40604437ec8010f77f7053fd135ccb202d6ca18329903831087cab8dbdab1", "sha256:b3bc3cf200aab74f3d758586ac50295214eda496ac6a6636e0c881c5958d9123",
"sha256:b2a4af5e6fa85ee1743c725b46579f8de0b97024eb5ae1a0b5c5711adc436665", "sha256:bba54f97578943f48f621b4a7afb8eb022370da26a88b88ccc9fee9f3ef7ce45",
"sha256:b97dc1273f16f85a38cff6668a07b636ef14e30591039efbfd21f5f91efae964", "sha256:bd2a13a0f8367e50347cbac87ae230ae1953935443240238f956bf10668bead6",
"sha256:bdd008629293a0d4f00b516841ac0df89f17a64bc2d83bcfa48212d3f3b3ca1a", "sha256:cbc1184349ca6e5112898aa7fc3efa1b1bbae24ab1edc774cfd09cbfd3b091d7",
"sha256:be3425dfdb9c46dc62d490fc1a6142a5f3dc6605ebb9048ae675056ef621413c", "sha256:cd82cca9c489e441574804dbda2dd8e114cf3be7935b03de11dade2c9478aea6",
"sha256:c2394bb857607494c3750b5040f852a1ad7831d7a7907b6106f0af2c70860cef", "sha256:ce8ba5ed8b0a7a203922d61cff45ee6001a41a9359f04f00d055a4e988755569",
"sha256:cb45b7ea577283b547b907a3389d62ca2eaddaf725fbb79cd360802440fa9c91", "sha256:cfee22e072a382b92ee0709dbb8203dabd52d54258051e770d9d2a81b162530b",
"sha256:cd3f563b98e2a8730c93bdc550f119ae766b2d3da1f0d6a3c7735b59adfa1642", "sha256:d977df6f7c4109ed1d96ffb6795f6af77114be606ae4556efbfc9cac725db65d",
"sha256:cda55ff0a7566405fb29ca38db1829fecb4c041b8dc3f91754f337bb7b27cbd8", "sha256:da72a384a1d7e87490ca71182f3ab469ed21d847adc16b70c34faac5a3b12801",
"sha256:df0b05fa4321b090abe5601dea9b1c8933c06f496588ccb397a0b1f9dfe32ebe", "sha256:ddf4ad1d651e6c9234945061e1a31fe27a4be0dea21c498b87b186fadf8f5919",
"sha256:e464e7b1be2216eba54b47256c15bf307ae4a656aa0f73becea7b3e7283c5ac2", "sha256:eb0ae5dfda83bbce660179d7b41c1c38fd833a54d2e6d9b258c644f3b75ef94d",
"sha256:e730d490b1421e52b43b1b9f5e1f8c3973499206e188f29b582577531e11033b", "sha256:f4c7d370badc60ac94a554bc571a46d03e39d8aacfba8006b334512e184aed59",
"sha256:e7ae3e520bd182a0cbfff3cc69dda3a2c26f69847e81bd3f090ed04471fc1282", "sha256:f6c378b435a26fda8996579c0e324b108d2ca0d01b4661503a75634e5155559f",
"sha256:e9631c6a339843e4f95efb80ff9a1bfaaf3d611ba9677a7a5cc61ffb923b4e06", "sha256:f6c9d30888503f2f5f87d6d41f016301352dd98da4a861bd10663c3a2d99d3b5",
"sha256:f3daabbe42ca31712e29d906dfa4bf1890341d2fd5178de118bc9977a8d2b23b", "sha256:fab8a7877275060f7b303e1f91c218069a2814a616b6a5ee2d8a3737deb15915",
"sha256:fe8807d67456e7cf0e9a33b85e0d05bb9d2977dbdb23977e4cc2b843633618fd" "sha256:fc32e7d7f98cac3d8d5153ed2cb583158ae3d446a6efb8e28ccb1c54a09f4169"
], ],
"index": "pypi", "index": "pypi",
"version": "==23.0.0" "version": "==23.1.0"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61", "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f",
"sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d" "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.27.1" "version": "==2.28.0"
}, },
"scons": { "scons": {
"hashes": [ "hashes": [
@ -1118,11 +1179,11 @@
}, },
"setuptools": { "setuptools": {
"hashes": [ "hashes": [
"sha256:68e45d17c9281ba25dc0104eadd2647172b3472d9e01f911efa57965e8d51a36", "sha256:5a844ad6e190dccc67d6d7411d119c5152ce01f7c76be4d8a1eaa314501bba77",
"sha256:a43bdedf853c670e5fed28e5623403bad2f73cf02f9a2774e91def6bda8265a7" "sha256:bf8a748ac98b09d32c9a64a995a6b25921c96cc5743c1efa82763ba80ff54e91"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==62.3.2" "version": "==62.4.0"
}, },
"six": { "six": {
"hashes": [ "hashes": [
@ -1134,11 +1195,11 @@
}, },
"smbus2": { "smbus2": {
"hashes": [ "hashes": [
"sha256:6276eb599b76c4e74372f2582d2282f03b4398f0da16bc996608e4f21557ca9b", "sha256:50f3c78e436b42a9583948be06961a8104cf020ebad5edfaaf2657528bef0818",
"sha256:8b1e70cda011b6fb3caf8377a1084f73a5aa99392b78529f140b0a3f06468f6c" "sha256:634541ed794068a822fe7499f1577468b9d4641b68dd9bfb6d0eb7270f4d2a32"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.4.1" "version": "==0.4.2"
}, },
"sympy": { "sympy": {
"hashes": [ "hashes": [
@ -1150,11 +1211,11 @@
}, },
"timezonefinder": { "timezonefinder": {
"hashes": [ "hashes": [
"sha256:90228f7bcf60388868ac95d3d6a8c9f710c766990909cb89dc39a893da7132ad", "sha256:0471463083b5fef7a656c7e31a7376fa1941bf6a41a0750c6e0ca151e43646e7",
"sha256:ce16831bc6349a82ca25ffc0e4c89df5bc5df29b286e5201701bce852f8e49f2" "sha256:2e8e958814f21fa809b16e98bd2f99082b69a0f88b864a4a1f2944d5a7e61827"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.0.0" "version": "==6.0.1"
}, },
"tomli": { "tomli": {
"hashes": [ "hashes": [
@ -1164,6 +1225,14 @@
"markers": "python_version < '3.11'", "markers": "python_version < '3.11'",
"version": "==2.0.1" "version": "==2.0.1"
}, },
"tomlkit": {
"hashes": [
"sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1",
"sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"
],
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==0.11.0"
},
"tqdm": { "tqdm": {
"hashes": [ "hashes": [
"sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d", "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
@ -1374,11 +1443,11 @@
}, },
"babel": { "babel": {
"hashes": [ "hashes": [
"sha256:3f349e85ad3154559ac4930c3918247d319f21910d5ce4b25d439ed8693b98d2", "sha256:7aed055f0c04c9e7f51a2f75261e41e1c804efa724cb65b60a970dd4448d469d",
"sha256:98aeaca086133efb3e1e2aad0396987490c8425929ddbcfe0550184fdc54cd13" "sha256:81a3beca4d0cd40a9cfb9e2adb2cf39261c2f959b92e7a74750befe5d79afd7b"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.10.1" "version": "==2.10.2"
}, },
"bcrypt": { "bcrypt": {
"hashes": [ "hashes": [
@ -1407,17 +1476,17 @@
}, },
"carla": { "carla": {
"hashes": [ "hashes": [
"sha256:1ed11b56c781cd15cbd540cacfb59ad348e0a021d898cfd0ff89a585f144da0b", "sha256:1210cce213e968a644effd4e2e48458a072481459d073424b05725056ba3d77d",
"sha256:20c1e1b72034175824d89b2d86b09ae72b4aca09ea25874dc6251f239297251d", "sha256:339fcb1e392f3ade1be82b7258de19c533e2efae111e954a6eb174efb296903d",
"sha256:6d1122c24af4f6375dc6858fbb0309b61c219b101d8c5a540def4c36c4563fe1", "sha256:5f065825ce812343bf27a80a19d647b3200b31b44a9e80cea0340e3bd20cdf81",
"sha256:9c19ebf6cbbc535bde4baf9e18c72ab349657b34c4202b9751541e4c0d20b3cc", "sha256:954ca34d5bdd4516ceca353db907fee8cec6630d6b31a732b17dd1554e0f0f94",
"sha256:a69f6d84b59e2f805b2a417de98f977fe9efe0cfa733da8d75e20d28892da915", "sha256:a64ee78fe91137fa7d4828c7fc06d5824bd7312e29e4ea4f31a5d74dd28bff40",
"sha256:c3ae0dce3f1354b6311fee21a365947b0ff169249993a913904f676046d2d69f", "sha256:a95d2d4218ea388c863c66b7c2ab3fe49ffefe53999305cfcb6a8107042f79af",
"sha256:dd392a267e14b785a8f65dafef86e05a92201253e9fb4a01e1e262834f20bed2" "sha256:d2bfaea2d6824a2d758cbe813856c69420494f5c97d2a2dfb45653ccf976f1ce"
], ],
"index": "pypi", "index": "pypi",
"markers": "platform_system != 'Darwin'", "markers": "platform_system != 'Darwin'",
"version": "==0.9.12" "version": "==0.9.13"
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
@ -1496,62 +1565,62 @@
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df" "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
], ],
"markers": "python_version >= '3'", "markers": "python_full_version >= '3.5.0'",
"version": "==2.0.12" "version": "==2.0.12"
}, },
"control": { "control": {
"hashes": [ "hashes": [
"sha256:8c9084bf386eafcf5d74008f780fae6dec68d243d18a380c866ac10a3549f8d3" "sha256:0891d2d32d6006ac1faa4e238ed8223ca342a4721d202dfeccae24fb02563183"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.9.1" "version": "==0.9.2"
}, },
"coverage": { "coverage": {
"hashes": [ "hashes": [
"sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a", "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749",
"sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6", "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982",
"sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383", "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3",
"sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f", "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9",
"sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f", "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428",
"sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f", "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e",
"sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c", "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c",
"sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018", "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9",
"sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720", "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264",
"sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3", "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605",
"sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf", "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397",
"sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211", "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d",
"sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39", "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c",
"sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95", "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815",
"sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41", "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068",
"sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c", "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b",
"sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166", "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4",
"sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49", "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4",
"sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce", "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3",
"sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088", "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84",
"sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6", "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83",
"sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426", "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4",
"sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df", "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8",
"sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632", "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb",
"sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3", "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d",
"sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08", "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df",
"sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65", "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6",
"sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea", "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b",
"sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701", "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72",
"sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5", "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13",
"sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311", "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df",
"sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7", "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc",
"sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d", "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6",
"sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61", "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28",
"sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c", "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b",
"sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a", "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4",
"sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055", "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad",
"sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740", "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46",
"sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45", "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3",
"sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052", "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9",
"sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f" "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.4" "version": "==6.4.1"
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
@ -1665,11 +1734,11 @@
}, },
"filelock": { "filelock": {
"hashes": [ "hashes": [
"sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20", "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404",
"sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6" "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==3.7.0" "version": "==3.7.1"
}, },
"fonttools": { "fonttools": {
"hashes": [ "hashes": [
@ -1679,6 +1748,33 @@
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==4.33.3" "version": "==4.33.3"
}, },
"ft4222": {
"hashes": [
"sha256:1489b08e4042cb2b24894495c1c8514fa115122e9f8a739f665f0e4d8c53d3f4",
"sha256:1c71913f9fb862634fb77eb6efeea38b2b839e09f739aee5864b9549bcf1c4a9",
"sha256:1f9be0961bd7f3e0c1729c9692f602244e93898610611602ce1fa16059ac5bfa",
"sha256:208cde748fc2bf4a753e217ee2844e75d7d1b6d370a8937a2055f9de8906f933",
"sha256:372be3d7d04c0f6dfa62ff66b3d4be5c39d57c258ab562d9eb0d7cdd3a90ed0f",
"sha256:3ebf9380315c6af8f9f3bc5c5c7a5ae06498349c14a7b2e65d5067b20462d21b",
"sha256:422740efac86f3fdd971bb9a94d9974e56cfe062284a2be1a85db96dd0e77e5e",
"sha256:4414ef82a6321c5205ab0e3dab2bf072f14b4dd3262e5f392560adf107210d41",
"sha256:459894dcca7db71a0d58e6f3abb4c9dd67654c92ad9d934f1e32dc08bed87645",
"sha256:4b7e4c8b5e4a8fe769127d3db99b7b410468ffe2718a07f7b317e7dc1ad9ca4f",
"sha256:68e6b357ea2287fe0e8dac287efbac2f0ce9632d65e828d422d3a0604555c174",
"sha256:800a26a4a8fc854f41e4de41e45b6deafdeb78ae5eff48818f710bb2d94a8c11",
"sha256:8790e96b4c3f8d1fb768f689f1c437a59b20889e4726ed7457ff3d188f0a3274",
"sha256:9369b55393b2e1f58f8b8516bcf9cd6fd34c05e77377c4cc48be39a49cad7be5",
"sha256:a6236f05b8948cd9c113c7159e3fdde202a0f73dce6440da078fd2fc9e411123",
"sha256:b7f58cd16213a5c92503c530b301f071d67283c80c1e8cb43d6f3ec5c186df35",
"sha256:cc368b06b92529a11add37d14196d2e2aaae19c6ffc51b9457513a0d05ceae1a",
"sha256:d479c037b417ff289727112f1d4725563b78448d3765f457c8bf6884e4d83abd",
"sha256:dce00d513be811f738954c5593c52b533a02331e8a26983280dea3f4d864962f",
"sha256:eadcbc1dd20ed6d7d07dc53354ea137bcea30fb0889d74bedcd189cdb4f61c84",
"sha256:ec984bd7e9300e5f2e50823dcd5874d54aa3e7503e69afe7eb3b4cea77071084"
],
"index": "pypi",
"version": "==1.4.1"
},
"hexdump": { "hexdump": {
"hashes": [ "hashes": [
"sha256:d781a43b0c16ace3f9366aade73e8ad3a7bd5137d58f0b45ab2d3f54876f20db" "sha256:d781a43b0c16ace3f9366aade73e8ad3a7bd5137d58f0b45ab2d3f54876f20db"
@ -1689,6 +1785,8 @@
"hypothesis": { "hypothesis": {
"hashes": [ "hashes": [
"sha256:2696cdb9005946bf1d2b215cc91d3fc01625e3342eb8743ddd04b667b2f1882b", "sha256:2696cdb9005946bf1d2b215cc91d3fc01625e3342eb8743ddd04b667b2f1882b",
"sha256:4ad26c5d434171ffc02aba569dd52255573d615554c062bc30734dbe6f318c61",
"sha256:69978811f1d9c19710c7d2bf8233dc43c80efa964251b72efbe8274044e073b4",
"sha256:967009fa561b3a3f8363a73d71923357271c37dc7fa27b30c2d21a1b6092b240" "sha256:967009fa561b3a3f8363a73d71923357271c37dc7fa27b30c2d21a1b6092b240"
], ],
"index": "pypi", "index": "pypi",
@ -1707,7 +1805,7 @@
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
], ],
"markers": "python_version >= '3'", "markers": "python_full_version >= '3.5.0'",
"version": "==3.3" "version": "==3.3"
}, },
"imagesize": { "imagesize": {
@ -1751,52 +1849,52 @@
}, },
"kiwisolver": { "kiwisolver": {
"hashes": [ "hashes": [
"sha256:0b7f50a1a25361da3440f07c58cd1d79957c2244209e4f166990e770256b6b0b", "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7",
"sha256:0c380bb5ae20d829c1a5473cfcae64267b73aaa4060adc091f6df1743784aae0", "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb",
"sha256:0d98dca86f77b851350c250f0149aa5852b36572514d20feeadd3c6b1efe38d0", "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a",
"sha256:0e45e780a74416ef2f173189ef4387e44b5494f45e290bcb1f03735faa6779bf", "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18",
"sha256:0e8afdf533b613122e4bbaf3c1e42c2a5e9e2d1dd3a0a017749a7658757cb377", "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694",
"sha256:1008346a7741620ab9cc6c96e8ad9b46f7a74ce839dbb8805ddf6b119d5fc6c2", "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca",
"sha256:1d1078ba770d6165abed3d9a1be1f9e79b61515de1dd00d942fa53bba79f01ae", "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051",
"sha256:1dcade8f6fe12a2bb4efe2cbe22116556e3b6899728d3b2a0d3b367db323eacc", "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09",
"sha256:240009fdf4fa87844f805e23f48995537a8cb8f8c361e35fda6b5ac97fcb906f", "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c",
"sha256:240c2d51d098395c012ddbcb9bd7b3ba5de412a1d11840698859f51d0e643c4f", "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e",
"sha256:262c248c60f22c2b547683ad521e8a3db5909c71f679b93876921549107a0c24", "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae",
"sha256:2e6cda72db409eefad6b021e8a4f964965a629f577812afc7860c69df7bdb84a", "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a",
"sha256:3c032c41ae4c3a321b43a3650e6ecc7406b99ff3e5279f24c9b310f41bc98479", "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9",
"sha256:42f6ef9b640deb6f7d438e0a371aedd8bef6ddfde30683491b2e6f568b4e884e", "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24",
"sha256:484f2a5f0307bc944bc79db235f41048bae4106ffa764168a068d88b644b305d", "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5",
"sha256:69b2d6c12f2ad5f55104a36a356192cfb680c049fe5e7c1f6620fc37f119cdc2", "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7",
"sha256:6e395ece147f0692ca7cdb05a028d31b83b72c369f7b4a2c1798f4b96af1e3d8", "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56",
"sha256:6ece2e12e4b57bc5646b354f436416cd2a6f090c1dadcd92b0ca4542190d7190", "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b",
"sha256:71469b5845b9876b8d3d252e201bef6f47bf7456804d2fbe9a1d6e19e78a1e65", "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67",
"sha256:7f606d91b8a8816be476513a77fd30abe66227039bd6f8b406c348cb0247dcc9", "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a",
"sha256:7f88c4b8e449908eeddb3bbd4242bd4dc2c7a15a7aa44bb33df893203f02dc2d", "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d",
"sha256:81237957b15469ea9151ec8ca08ce05656090ffabc476a752ef5ad7e2644c526", "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7",
"sha256:89b57c2984f4464840e4b768affeff6b6809c6150d1166938ade3e22fbe22db8", "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be",
"sha256:8a830a03970c462d1a2311c90e05679da56d3bd8e78a4ba9985cb78ef7836c9f", "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046",
"sha256:8ae5a071185f1a93777c79a9a1e67ac46544d4607f18d07131eece08d415083a", "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3",
"sha256:8b6086aa6936865962b2cee0e7aaecf01ab6778ce099288354a7229b4d9f1408", "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7",
"sha256:8ec2e55bf31b43aabe32089125dca3b46fdfe9f50afbf0756ae11e14c97b80ca", "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8",
"sha256:8ff3033e43e7ca1389ee59fb7ecb8303abb8713c008a1da49b00869e92e3dd7c", "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86",
"sha256:91eb4916271655dfe3a952249cb37a5c00b6ba68b4417ee15af9ba549b5ba61d", "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7",
"sha256:9d2bb56309fb75a811d81ed55fbe2208aa77a3a09ff5f546ca95e7bb5fac6eff", "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0",
"sha256:a4e8f072db1d6fb7a7cc05a6dbef8442c93001f4bb604f1081d8c2db3ca97159", "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b",
"sha256:b1605c7c38cc6a85212dfd6a641f3905a33412e49f7c003f35f9ac6d71f67720", "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567",
"sha256:b3e251e5c38ac623c5d786adb21477f018712f8c6fa54781bd38aa1c60b60fc2", "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720",
"sha256:b978afdb913ca953cf128d57181da2e8798e8b6153be866ae2a9c446c6162f40", "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e",
"sha256:be9a650890fb60393e60aacb65878c4a38bb334720aa5ecb1c13d0dac54dd73b", "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136",
"sha256:c222f91a45da9e01a9bc4f760727ae49050f8e8345c4ff6525495f7a164c8973", "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069",
"sha256:c839bf28e45d7ddad4ae8f986928dbf5a6d42ff79760d54ec8ada8fb263e097c", "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498",
"sha256:cbb5eb4a2ea1ffec26268d49766cafa8f957fe5c1b41ad00733763fae77f9436", "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab",
"sha256:e348f1904a4fab4153407f7ccc27e43b2a139752e8acf12e6640ba683093dd96", "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2",
"sha256:e677cc3626287f343de751e11b1e8a5b915a6ac897e8aecdbc996cd34de753a0", "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6",
"sha256:f74f2a13af201559e3d32b9ddfc303c94ae63d63d7f4326d06ce6fe67e7a8255", "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658",
"sha256:fa4d97d7d2b2c082e67907c0b8d9f31b85aa5d3ba0d33096b7116f03f8061261", "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a",
"sha256:ffbdb9a96c536f0405895b5e21ee39ec579cb0ed97bdbd169ae2b55f41d73219" "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==1.4.2" "version": "==1.4.3"
}, },
"lru-dict": { "lru-dict": {
"hashes": [ "hashes": [
@ -1918,41 +2016,40 @@
}, },
"mpld3": { "mpld3": {
"hashes": [ "hashes": [
"sha256:3626d37da7ac11d0fce4dd5ceb37d04ba618bf951129bf6ca3f94bf48da87d6f", "sha256:1a167dbef836dd7c66d8aa71c06a32d50bffa18725f304d93cb74fdb3545043b",
"sha256:b1290f6cca2d9515e32ee21a8b0c18b2ea361cefce58ba0f4df881e9eeb3f64c", "sha256:41938e65de4ba41a1b53d92e7c8609e7172e09b33ef5db42bb6f73701106c8b7"
"sha256:c589db8b661aee25c93e198e2e18ed47b9a96951de41d96241345acec5f819ab"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.5.7" "version": "==0.5.8"
}, },
"mypy": { "mypy": {
"hashes": [ "hashes": [
"sha256:0112752a6ff07230f9ec2f71b0d3d4e088a910fdce454fdb6553e83ed0eced7d", "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5",
"sha256:0384d9f3af49837baa92f559d3fa673e6d2652a16550a9ee07fc08c736f5e6f8", "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66",
"sha256:1b333cfbca1762ff15808a0ef4f71b5d3eed8528b23ea1c3fb50543c867d68de", "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e",
"sha256:1fdeb0a0f64f2a874a4c1f5271f06e40e1e9779bf55f9567f149466fc7a55038", "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56",
"sha256:4c653e4846f287051599ed8f4b3c044b80e540e88feec76b11044ddc5612ffed", "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e",
"sha256:563514c7dc504698fb66bb1cf897657a173a496406f1866afae73ab5b3cdb334", "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d",
"sha256:5b231afd6a6e951381b9ef09a1223b1feabe13625388db48a8690f8daa9b71ff", "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813",
"sha256:5ce6a09042b6da16d773d2110e44f169683d8cc8687e79ec6d1181a72cb028d2", "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932",
"sha256:5e7647df0f8fc947388e6251d728189cfadb3b1e558407f93254e35abc026e22", "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569",
"sha256:6003de687c13196e8a1243a5e4bcce617d79b88f83ee6625437e335d89dfebe2", "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b",
"sha256:61504b9a5ae166ba5ecfed9e93357fd51aa693d3d434b582a925338a2ff57fd2", "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0",
"sha256:77423570c04aca807508a492037abbd72b12a1fb25a385847d191cd50b2c9605", "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648",
"sha256:a4d9898f46446bfb6405383b57b96737dcfd0a7f25b748e78ef3e8c576bba3cb", "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6",
"sha256:a952b8bc0ae278fc6316e6384f67bb9a396eb30aced6ad034d3a76120ebcc519", "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950",
"sha256:b5b5bd0ffb11b4aba2bb6d31b8643902c48f990cc92fda4e21afac658044f0c0", "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15",
"sha256:ca75ecf2783395ca3016a5e455cb322ba26b6d33b4b413fcdedfc632e67941dc", "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723",
"sha256:cf9c261958a769a3bd38c3e133801ebcd284ffb734ea12d01457cb09eacf7d7b", "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a",
"sha256:dd4d670eee9610bf61c25c940e9ade2d0ed05eb44227275cce88701fee014b1f", "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3",
"sha256:e19736af56947addedce4674c0971e5dceef1b5ec7d667fe86bcd2b07f8f9075", "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6",
"sha256:eaea21d150fb26d7b4856766e7addcf929119dd19fc832b22e71d942835201ef", "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24",
"sha256:eaff8156016487c1af5ffa5304c3e3fd183edcb412f3e9c72db349faf3f6e0eb", "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b",
"sha256:ee0a36edd332ed2c5208565ae6e3a7afc0eabb53f5327e281f2ef03a6bc7687a", "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d",
"sha256:ef7beb2a3582eb7a9f37beaf38a28acfd801988cde688760aea9e6cc4832b10b" "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.950" "version": "==0.961"
}, },
"mypy-extensions": { "mypy-extensions": {
"hashes": [ "hashes": [
@ -1963,11 +2060,11 @@
}, },
"myst-parser": { "myst-parser": {
"hashes": [ "hashes": [
"sha256:1635ce3c18965a528d6de980f989ff64d6a1effb482e1f611b1bfb79e38f3d98", "sha256:4965e51918837c13bf1c6f6fe2c6bddddf193148360fbdaefe743a4981358f6a",
"sha256:4c076d649e066f9f5c7c661bae2658be1ca06e76b002bb97f02a09398707686c" "sha256:739a4d96773a8e55a2cacd3941ce46a446ee23dcd6b37e06f73f551ad7821d86"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.17.2" "version": "==0.18.0"
}, },
"natsort": { "natsort": {
"hashes": [ "hashes": [
@ -2014,16 +2111,16 @@
}, },
"opencv-python-headless": { "opencv-python-headless": {
"hashes": [ "hashes": [
"sha256:3f330468c29882dbbec5af25695c5e575572c6b855cb0f9fe53e14116fd46bfc", "sha256:21e70f8b0c04098cdf466d27184fe6c3820aaef944a22548db95099959c95889",
"sha256:4bdf982574bf2fefc5f82c86df7cb42e56ad627874c7c0f4d94ecf4ae8885304", "sha256:2c032c373e447c3fc2a670bca20e2918a1205a6e72854df60425fd3f82c78c32",
"sha256:567a54c1919bcf5b3d20a9830e3c511e57134de8def286ce137c3544a892f98c", "sha256:3bacd806cce1f1988e58f3d6f761538e0215d6621d316de94c009dc0acbd6ad3",
"sha256:62e31878641a8f96e773118d1eea9f34bdda87c9990a0faab04ebaafb5ae015c", "sha256:d5291d7e10aa2c19cab6fd86f0d61af8617290ecd2d7ffcb051e446868d04cc5",
"sha256:a60e9ff48854ec37be391e19dd634883cc26c2f0f814e5325b3deca33420912c", "sha256:e6c069bc963d7e8fcec21b3e33e594d35948badd63eccb2e80f88b0a12102c03",
"sha256:c3c2dda44d601757a508b07d628537c49f31223ad4edd0f747a70d4c852a7b98", "sha256:eec6281054346103d6af93f173b7c6a206beb2663d3adc04aa3ddc66e85093df",
"sha256:ca4f013fa958f60fb2327fe87e6127c1ac0ab536890b1d4b00847f417b7af1ba" "sha256:ffbf26fcd697af996408440a93bc69c49c05a36845771f984156dfbeaa95d497"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.5.5.64" "version": "==4.6.0.66"
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
@ -2329,11 +2426,11 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61", "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f",
"sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d" "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.27.1" "version": "==2.28.0"
}, },
"reverse-geocoder": { "reverse-geocoder": {
"hashes": [ "hashes": [

@ -35,16 +35,17 @@ What is openpilot?
</table> </table>
Running in a car Running on a dedicated device in a car
------ ------
To use openpilot in a car, you need four things To use openpilot in a car, you need four things
* This software. It's free and available right here. * 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. * 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 supported device to run this software: a [comma three](https://comma.ai/shop/products/three), or if you like to experiment, a [Ubuntu computer with webcams](https://github.com/commaai/openpilot/tree/master/tools/webcam). * A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car.
* A way to connect to your car. With a comma three, you need only a [car harness](https://comma.ai/shop/products/car-harness). With a PC, you also need a [black panda](https://comma.ai/shop/products/panda).
We have detailed instructions for [how to install the device in a car](https://comma.ai/setup). We have detailed instructions for [how to mount the device in a car](https://comma.ai/setup).
Running on PC Running on PC
------ ------
@ -55,6 +56,7 @@ With openpilot's tools you can plot logs, replay drives and watch the full-res c
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).
Community and Contributing Community and Contributing
------ ------
@ -119,6 +121,7 @@ Directory Structure
├── modeld # Driving and monitoring model runners ├── modeld # Driving and monitoring model runners
├── proclogd # Logs information from proc ├── proclogd # Logs information from proc
├── sensord # IMU interface code ├── sensord # IMU interface code
├── navd # Turn-by-turn navigation
├── test # Unit tests, system tests, and a car simulator ├── test # Unit tests, system tests, and a car simulator
└── ui # The UI └── ui # The UI

@ -1,11 +1,24 @@
Version 0.8.15 (2022-XX-XX) Version 0.8.15 (2022-07-XX)
======================== ========================
* New driving model
* New lateral controller based on physical wheel torque model * New lateral controller based on physical wheel torque model
* Much smoother control, consistent across the speed range * Much smoother control, consistent across the speed range
* Effective feedforward that uses road roll * Effective feedforward that uses road roll
* Simplified tuning, all car-specific parameters can be derived from data * Simplified tuning, all car-specific parameters can be derived from data
* Significantly improved control on TSS-P Prius
* New driver monitoring model
* takes a larger input frame
* outputs a driver state for both driver and passenger
* automatically determines which side the driver is on (soon)
* Reduced power usage: device runs cooler and fan spins less
* Minor UI updates
* New font
* Refreshed max speed design
* Speed limits shown while navigating
* More consistent camera view perspective across cars
* AGNOS 5 * AGNOS 5
* Honda Civic 2022 support
* Hyundai Tucson 2021 support thanks to bluesforte!
* Lexus NX Hybrid 2020 support thanks to AlexandreSato!
Version 0.8.14 (2022-06-01) Version 0.8.14 (2022-06-01)
======================== ========================

@ -94,7 +94,7 @@ if arch == "larch64":
"/usr/lib/aarch64-linux-gnu" "/usr/lib/aarch64-linux-gnu"
] ]
cpppath += [ cpppath += [
"#selfdrive/camerad/include", "#system/camerad/include",
] ]
cflags = ["-DQCOM2", "-mcpu=cortex-a57"] cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"] cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
@ -356,22 +356,27 @@ Export('cereal', 'messaging', 'visionipc')
# Build rednose library and ekf models # Build rednose library and ekf models
rednose_deps = [
"#selfdrive/locationd/models/constants.py",
"#selfdrive/locationd/models/gnss_helpers.py",
]
rednose_config = { rednose_config = {
'generated_folder': '#selfdrive/locationd/models/generated', 'generated_folder': '#selfdrive/locationd/models/generated',
'to_build': { 'to_build': {
'live': ('#selfdrive/locationd/models/live_kf.py', True, ['live_kf_constants.h']), 'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, [], rednose_deps),
'car': ('#selfdrive/locationd/models/car_kf.py', True, []), 'live': ('#selfdrive/locationd/models/live_kf.py', True, ['live_kf_constants.h'], rednose_deps),
'car': ('#selfdrive/locationd/models/car_kf.py', True, [], rednose_deps),
}, },
} }
if arch != "larch64": if arch != "larch64":
rednose_config['to_build'].update({ rednose_config['to_build'].update({
'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, []), 'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, [], rednose_deps),
'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, []), 'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, [], []),
'pos_computer_4': ('#rednose/helpers/lst_sq_computer.py', False, []), 'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, [], []),
'pos_computer_5': ('#rednose/helpers/lst_sq_computer.py', False, []), 'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, [], []),
'feature_handler_5': ('#rednose/helpers/feature_handler.py', False, []), 'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, [], rednose_deps),
'lane': ('#xx/pipeline/lib/ekf/lane_kf.py', True, []),
}) })
Export('rednose_config') Export('rednose_config')
@ -379,6 +384,7 @@ SConscript(['rednose/SConscript'])
# Build system services # Build system services
SConscript([ SConscript([
'system/camerad/SConscript',
'system/clocksd/SConscript', 'system/clocksd/SConscript',
'system/proclogd/SConscript', 'system/proclogd/SConscript',
]) ])
@ -396,7 +402,6 @@ SConscript(['third_party/SConscript'])
SConscript(['common/kalman/SConscript']) SConscript(['common/kalman/SConscript'])
SConscript(['common/transformations/SConscript']) SConscript(['common/transformations/SConscript'])
SConscript(['selfdrive/camerad/SConscript'])
SConscript(['selfdrive/modeld/SConscript']) SConscript(['selfdrive/modeld/SConscript'])
SConscript(['selfdrive/controls/lib/cluster/SConscript']) SConscript(['selfdrive/controls/lib/cluster/SConscript'])
@ -411,6 +416,8 @@ SConscript(['selfdrive/locationd/SConscript'])
SConscript(['selfdrive/sensord/SConscript']) SConscript(['selfdrive/sensord/SConscript'])
SConscript(['selfdrive/ui/SConscript']) SConscript(['selfdrive/ui/SConscript'])
SConscript(['tools/replay/SConscript'])
if GetOption('test'): if GetOption('test'):
SConscript('panda/tests/safety/SConscript') SConscript('panda/tests/safety/SConscript')

@ -1 +1 @@
Subproject commit 7cbb4f1c8cb7cfc798a058c611801442b40feb52 Subproject commit df08568318da97ed6f87747caee0a5b2c30086c4

@ -3,7 +3,7 @@ import os
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
from common.basedir import PERSIST from common.basedir import PERSIST
from selfdrive.version import get_version from system.version import get_version
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')

@ -1,7 +1,7 @@
import os import os
from pathlib import Path from pathlib import Path
from selfdrive.hardware import PC from system.hardware import PC
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")) BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))

@ -2,7 +2,7 @@
#include <array> #include <array>
#include "common/mat.h" #include "common/mat.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
const int TRAJECTORY_SIZE = 33; const int TRAJECTORY_SIZE = 33;
const int LAT_MPC_N = 16; const int LAT_MPC_N = 16;
@ -24,12 +24,6 @@ constexpr auto T_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(10.0);
constexpr auto X_IDXS = build_idxs<double, TRAJECTORY_SIZE>(192.0); constexpr auto X_IDXS = build_idxs<double, TRAJECTORY_SIZE>(192.0);
constexpr auto X_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(192.0); constexpr auto X_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(192.0);
namespace tici_dm_crop {
const int x_offset = -72;
const int y_offset = -144;
const int width = 954;
};
const mat3 fcam_intrinsic_matrix = (mat3){{2648.0, 0.0, 1928.0 / 2, const mat3 fcam_intrinsic_matrix = (mat3){{2648.0, 0.0, 1928.0 / 2,
0.0, 2648.0, 1208.0 / 2, 0.0, 2648.0, 1208.0 / 2,
0.0, 0.0, 1.0}}; 0.0, 0.0, 1.0}};

@ -8,7 +8,7 @@
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
namespace { namespace {
@ -94,6 +94,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CompletedTrainingVersion", PERSISTENT}, {"CompletedTrainingVersion", PERSISTENT},
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DashcamOverride", PERSISTENT},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT}, {"DisablePowerDown", PERSISTENT},
{"DisableRadar_Allow", PERSISTENT}, {"DisableRadar_Allow", PERSISTENT},
@ -127,6 +128,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START}, {"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
{"IsUpdateAvailable", CLEAR_ON_MANAGER_START}, {"IsUpdateAvailable", CLEAR_ON_MANAGER_START},
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"LaikadEphemeris", PERSISTENT | DONT_LOG},
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START}, {"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
{"LastGPSPosition", PERSISTENT}, {"LastGPSPosition", PERSISTENT},
{"LastManagerExitReason", CLEAR_ON_MANAGER_START}, {"LastManagerExitReason", CLEAR_ON_MANAGER_START},

@ -8,7 +8,7 @@ from typing import Optional, List, Union
from setproctitle import getproctitle # pylint: disable=no-name-in-module from setproctitle import getproctitle # pylint: disable=no-name-in-module
from common.clock import sec_since_boot # pylint: disable=no-name-in-module, import-error from common.clock import sec_since_boot # pylint: disable=no-name-in-module, import-error
from selfdrive.hardware import PC from system.hardware import PC
# time step for each process # time step for each process

@ -15,7 +15,7 @@
#include "common/util.h" #include "common/util.h"
#include "common/version.h" #include "common/version.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
class SwaglogState : public LogState { class SwaglogState : public LogState {
public: public:
@ -66,8 +66,9 @@ static void log(int levelnum, const char* filename, int lineno, const char* func
char levelnum_c = levelnum; char levelnum_c = levelnum;
zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK); zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK);
} }
static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func, static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func,
char* msg_buf, json11::Json::object msg_j={}) { char* msg_buf, const json11::Json::object &msg_j={}) {
std::lock_guard lk(s.lock); std::lock_guard lk(s.lock);
if (!s.initialized) s.initialize(); if (!s.initialized) s.initialize();

@ -7,7 +7,7 @@
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
#include "common/version.h" #include "common/version.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
const char *SWAGLOG_ADDR = "ipc:///tmp/logmessage"; const char *SWAGLOG_ADDR = "ipc:///tmp/logmessage";
std::string daemon_name = "testy"; std::string daemon_name = "testy";

@ -25,7 +25,8 @@ How We Rate The Cars
- <a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a> - No steering control below certain speeds. - <a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a> - No steering control below certain speeds.
### Steering Torque ### Steering Torque
- <a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a> - Car has enough steering torque for comfortable highway driving. - <a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a> - Car has enough steering torque to take tighter turns.
- <a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a> - Car has enough steering torque for comfortable highway driving.
- <a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a> - Limited ability to make turns. - <a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a> - Limited ability to make turns.
### Actively Maintained ### Actively Maintained
@ -34,7 +35,7 @@ How We Rate The Cars
**All supported cars can move between the tiers as support changes.** **All supported cars can move between the tiers as support changes.**
## Gold Cars # Gold - 31 cars
|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained|
|---|---|---|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|
@ -51,10 +52,15 @@ How We Rate The Cars
|Lexus|ES 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|ES 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|ES Hybrid 2019-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|ES Hybrid 2019-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|NX 2020|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|NX 2020|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|NX Hybrid 2020|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RX 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|UX Hybrid 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|UX Hybrid 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2022|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon Hybrid 2022|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Camry 2021-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Camry 2021-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Camry Hybrid 2021-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Camry Hybrid 2021-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Corolla 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Corolla 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Corolla Cross 2020-21 (Non-US only)|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Corolla Hatchback 2019-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Corolla Hatchback 2019-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Corolla Hybrid 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Corolla Hybrid 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Highlander 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Highlander 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
@ -65,11 +71,15 @@ How We Rate The Cars
|Toyota|RAV4 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|RAV4 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 Hybrid 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|RAV4 Hybrid 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
## Silver Cars # Silver - 67 cars
|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained|
|---|---|---|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|
|Audi|Q2 2018|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Audi|A3 2014-19|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|RS3 2018|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|S3 2015-17|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Genesis|G70 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Genesis|G70 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Genesis|G80 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Genesis|G80 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Elantra 2021-22|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Elantra 2021-22|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
@ -87,7 +97,7 @@ How We Rate The Cars
|Kia|Ceed 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Ceed 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Forte 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Forte 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Forte 2019-21|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Forte 2019-21|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|K5 2021-22|SCC + LFA|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|K5 2021-22|SCC|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Niro Hybrid 2021|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Niro Hybrid 2021|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Niro Hybrid 2022|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Niro Hybrid 2022|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Optima 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Optima 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
@ -95,63 +105,67 @@ How We Rate The Cars
|Kia|Sorento 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Sorento 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Sorento 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Sorento 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Stinger 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Stinger 2018|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|CT Hybrid 2017-18|LSS|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|ES Hybrid 2017-18|LSS|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|ES Hybrid 2017-18|LSS|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|NX 2018-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|NX 2018-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|NX Hybrid 2018-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|NX Hybrid 2018-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RX 2020-22|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|RX Hybrid 2020-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RX Hybrid 2020-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Nissan|Altima 2019-20|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Mazda|CX-5 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Nissan|Leaf 2018-22|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|Rogue 2018-20|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|X-Trail 2017|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|SEAT|Ateca 2018|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |SEAT|Ateca 2018|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|SEAT|Leon 2014-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |SEAT|Leon 2014-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Forester 2019-21|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Ascent 2019-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Kamiq 2021[<sup>6</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Crosstrek 2020-21|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Karoq 2019|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Forester 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Kodiaq 2018-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Impreza 2020-21|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Octavia 2015, 2018-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Alphard 2019-20|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Octavia RS 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Alphard Hybrid 2021|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Scala 2020|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Superb 2015-18|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Alphard 2019-20|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Alphard Hybrid 2021|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2022|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon Hybrid 2022|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Camry 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Camry 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Camry Hybrid 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Camry Hybrid 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>[<sup>4</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Highlander 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Highlander Hybrid 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Prius 2016-20|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Prius Prime 2017-20|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|RAV4 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 Hybrid 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 Hybrid 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|RAV4 Hybrid 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Arteon 2018, 2021[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Atlas 2018-19, 2022[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Passat 2015-19[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|e-Golf 2014, 2018-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf 2015-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf Alltrack 2017-18|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf GTE 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf GTI 2018-21|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf R 2016-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf SportsVan 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf SportWagen 2015|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Passat 2015-19[<sup>6</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Polo 2020|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Polo 2020|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|T-Cross 2021[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|T-Roc 2021[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Taos 2022[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Touran 2017|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
## Bronze Cars # Bronze - 78 cars
|Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained| |Make|Model|Supported Package|openpilot ACC|Stop and Go|Steer to 0|Steering Torque|Actively Maintained|
|---|---|---|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|
|Acura|ILX 2016-19|AcuraWatch Plus|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Acura|ILX 2016-19|AcuraWatch Plus|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Acura|RDX 2016-18|AcuraWatch Plus|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Acura|RDX 2016-18|AcuraWatch Plus|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Acura|RDX 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Acura|RDX 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|A3 2014-19|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Audi|Q2 2018|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|A3 Sportback e-tron 2017-18|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Audi|Q3 2020-21|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|Q3 2020-21|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|ACC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|
|Audi|RS3 2018|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Chrysler|Pacifica 2017-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Audi|S3 2015-17|ACC + Lane Assist|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Chrysler|Pacifica 2019-20|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Cadillac|Escalade ESV 2016[<sup>1</sup>](#footnotes)|ACC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>| |Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chevrolet|Volt 2017-18[<sup>1</sup>](#footnotes)|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Chrysler|Pacifica Hybrid 2019-22|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chrysler|Pacifica 2017-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chrysler|Pacifica 2020|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Chrysler|Pacifica Hybrid 2019-21|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Genesis|G90 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>| |Genesis|G90 2018|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|
|GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>| |GMC|Acadia 2018[<sup>1</sup>](#footnotes)|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|
|Honda|Accord 2018-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Accord 2018-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Accord Hybrid 2018-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Accord Hybrid 2018-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Civic 2016-18|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Civic 2016-18|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Civic 2019-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>[<sup>2</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Civic 2019-20|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>[<sup>2</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Civic 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Civic Hatchback 2017-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Civic Hatchback 2017-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Civic Hatchback 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|CR-V 2015-16|Touring|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|CR-V 2015-16|Touring|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|CR-V 2017-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|CR-V 2017-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|CR-V Hybrid 2017-19|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|CR-V Hybrid 2017-19|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
@ -165,61 +179,52 @@ How We Rate The Cars
|Honda|Passport 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Passport 2019-21|All|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Pilot 2016-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Pilot 2016-21|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Honda|Ridgeline 2017-22|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Honda|Ridgeline 2017-22|Honda Sensing|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Elantra 2017-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Elantra 2017-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Genesis 2015-16|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Genesis 2015-16|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Ioniq Electric 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Ioniq Electric 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Ioniq Hybrid 2017-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Ioniq Hybrid 2017-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Ioniq Plug-in Hybrid 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Ioniq Plug-in Hybrid 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Sonata 2018-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Sonata 2018-19|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Hyundai|Veloster 2019-20|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Tucson 2021|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Hyundai|Veloster 2019-20|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Jeep|Grand Cherokee 2019-20|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Jeep|Grand Cherokee 2019-20|Adaptive Cruise|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Niro Plug-in Hybrid 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Niro Plug-in Hybrid 2019|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Kia|Optima 2017|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Kia|Optima 2017|SCC + LKAS|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|CT Hybrid 2017-18|LSS|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|IS 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|IS 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|RC 2020|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RC 2020|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|RX 2016-18|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RX 2016-18|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Lexus|RX Hybrid 2016-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Lexus|RX Hybrid 2016-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Mazda|CX-5 2022|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Mazda|CX-9 2021|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Mazda|CX-9 2021|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|Altima 2019-20|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|Leaf 2018-22|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|Rogue 2018-20|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Nissan|X-Trail 2017|ProPILOT|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Ascent 2019-20|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Crosstrek 2018-19|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Crosstrek 2018-19|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Crosstrek 2020-21|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Impreza 2017-19|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Subaru|Impreza 2017-19|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Subaru|Impreza 2020-21|EyeSight|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Kamiq 2021[<sup>5</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Karoq 2019|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2019-21|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Kodiaq 2018-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon Hybrid 2019-21|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Octavia 2015, 2018-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|C-HR 2017-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Octavia RS 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|C-HR Hybrid 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Škoda|Scala 2020|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Škoda|Superb 2015-18|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon 2019-21|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Avalon Hybrid 2019-21|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|C-HR 2017-21|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|C-HR Hybrid 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Corolla 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Corolla 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Highlander 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Prius v 2017|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Highlander Hybrid 2017-19|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|RAV4 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Prius 2016-20|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>5</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Toyota|Sienna 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Prius Prime 2017-20|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>5</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Arteon 2018, 2021[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Prius v 2017|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>5</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|California 2021[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Caravelle 2020[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|RAV4 Hybrid 2016-18|TSS-P|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Jetta 2018-21|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Toyota|Sienna 2018-20|All|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>[<sup>3</sup>](#footnotes)|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Jetta GLI 2021|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Atlas 2018-19, 2022[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|T-Cross 2021[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|California 2021[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|T-Roc 2021[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Caravelle 2020[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Taos 2022[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|e-Golf 2014, 2018-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Tiguan 2019-22[<sup>7</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf 2015-20|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>| |Volkswagen|Touran 2017|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-half.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf Alltrack 2017-18|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf GTE 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf GTI 2018-21|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf R 2016-19|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf SportsVan 2016|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Golf SportWagen 2015|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Jetta 2018-21|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Jetta GLI 2021|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Passat 2017, 2021[<sup>8</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
|Volkswagen|Tiguan 2019-22[<sup>9</sup>](#footnotes)|Driver Assistance|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-empty.svg" width="22" /></a>|<a href="##"><img valign="top" src="assets/icon-star-full.svg" width="22" /></a>|
<a id="footnotes"></a> <a id="footnotes"></a>
@ -227,11 +232,9 @@ How We Rate The Cars
<sup>2</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br /> <sup>2</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
<sup>3</sup>When disconnecting the Driver Support Unit (DSU), openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). <b><i> NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br /> <sup>3</sup>When disconnecting the Driver Support Unit (DSU), openpilot Adaptive Cruise Control (ACC) will replace stock Adaptive Cruise Control (ACC). <b><i> NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
<sup>4</sup>28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br /> <sup>4</sup>28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
<sup>5</sup>An inaccurate steering wheel angle sensor makes precise control difficult. <br /> <sup>5</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
<sup>6</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br /> <sup>6</sup>Not including the USA/China market Passat, which is based on the (currently) unsupported PQ35/NMS platform. <br />
<sup>7</sup>European B8 Passat on MQB platform, not including USA/China/Mideast. <br /> <sup>7</sup>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. <br />
<sup>8</sup>USA/China/Mideast NMS Passat made on the PQ46 platform, not including Europe. <br />
<sup>9</sup>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. <br />
## Community Maintained Cars ## Community Maintained Cars
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).

@ -28,11 +28,11 @@ selfdrive
camerad camerad
^^^^^^^ ^^^^^^^
.. autodoxygenindex:: .. autodoxygenindex::
:project: selfdrive_camerad_cameras :project: system_camerad_cameras
.. autodoxygenindex:: .. autodoxygenindex::
:project: selfdrive_camerad_transforms :project: system_camerad_transforms
.. autodoxygenindex:: .. autodoxygenindex::
:project: selfdrive_camerad_imgproc :project: system_camerad_imgproc
locationd locationd
^^^^^^^^^ ^^^^^^^^^
@ -54,7 +54,7 @@ soundd
replay replay
"""""" """"""
.. autodoxygenindex:: .. autodoxygenindex::
:project: selfdrive_ui_replay :project: tools_replay
qt qt
"" ""

@ -14,10 +14,12 @@
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
# #
import os import os
from os.path import exists
import sys import sys
from selfdrive.version import get_version from os.path import exists
from common.basedir import BASEDIR from common.basedir import BASEDIR
from system.version import get_version
sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))

@ -1 +1 @@
Subproject commit ba6ed3277cadfdc8697206784afbd7f9a223798b Subproject commit 6e87f536dbe8cf80040f724c89798e66ca17cf9d

@ -22,12 +22,12 @@ function agnos_init {
# Check if AGNOS update is required # Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
AGNOS_PY="$DIR/selfdrive/hardware/tici/agnos.py" AGNOS_PY="$DIR/system/hardware/tici/agnos.py"
MANIFEST="$DIR/selfdrive/hardware/tici/agnos.json" MANIFEST="$DIR/system/hardware/tici/agnos.json"
if $AGNOS_PY --verify $MANIFEST; then if $AGNOS_PY --verify $MANIFEST; then
sudo reboot sudo reboot
fi fi
$DIR/selfdrive/hardware/tici/updater $AGNOS_PY $MANIFEST $DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
fi fi
} }

@ -1 +1 @@
Subproject commit fac85f721b8cee25aa9d256edd5274ba2d6df110 Subproject commit 02571d450094e8ad52f5a67366cfc1b9d03134f1

@ -1 +1 @@
Subproject commit c077fc2347514ca892cd996286caadb9d8bd68bb Subproject commit a2f199c5874b232d4281613626ccb9fbef2afadd

@ -1 +1 @@
Subproject commit 7663289f1e68860f53dc34337ef080dde69a2586 Subproject commit 225dbacbaac312f85eaaee0b97a3acc31f9c6b47

@ -60,18 +60,20 @@ release/*
tools/lib/* tools/lib/*
tools/joystick/* tools/joystick/*
tools/replay/*.cc
selfdrive/version.py tools/replay/*.h
selfdrive/__init__.py selfdrive/__init__.py
selfdrive/sentry.py selfdrive/sentry.py
selfdrive/swaglog.py
selfdrive/logmessaged.py
selfdrive/tombstoned.py selfdrive/tombstoned.py
selfdrive/updated.py selfdrive/updated.py
selfdrive/rtshield.py selfdrive/rtshield.py
selfdrive/statsd.py selfdrive/statsd.py
system/logmessaged.py
system/swaglog.py
system/version.py
selfdrive/athena/__init__.py selfdrive/athena/__init__.py
selfdrive/athena/athenad.py selfdrive/athena/athenad.py
selfdrive/athena/manage_athenad.py selfdrive/athena/manage_athenad.py
@ -101,9 +103,13 @@ selfdrive/car/interfaces.py
selfdrive/car/vin.py selfdrive/car/vin.py
selfdrive/car/disable_ecu.py selfdrive/car/disable_ecu.py
selfdrive/car/fw_versions.py selfdrive/car/fw_versions.py
selfdrive/car/ecu_addrs.py
selfdrive/car/isotp_parallel_query.py selfdrive/car/isotp_parallel_query.py
selfdrive/car/tests/__init__.py selfdrive/car/tests/__init__.py
selfdrive/car/tests/test_car_interfaces.py selfdrive/car/tests/test_car_interfaces.py
selfdrive/car/torque_data/params.yaml
selfdrive/car/torque_data/substitute.yaml
selfdrive/car/torque_data/override.yaml
selfdrive/car/body/*.py selfdrive/car/body/*.py
selfdrive/car/chrysler/*.py selfdrive/car/chrysler/*.py
@ -181,21 +187,22 @@ selfdrive/controls/lib/longitudinal_mpc_lib/.gitignore
selfdrive/controls/lib/lateral_mpc_lib/* selfdrive/controls/lib/lateral_mpc_lib/*
selfdrive/controls/lib/longitudinal_mpc_lib/* selfdrive/controls/lib/longitudinal_mpc_lib/*
selfdrive/hardware/__init__.py selfdrive/hardware
selfdrive/hardware/base.h system/hardware/__init__.py
selfdrive/hardware/base.py system/hardware/base.h
selfdrive/hardware/hw.h system/hardware/base.py
selfdrive/hardware/tici/__init__.py system/hardware/hw.h
selfdrive/hardware/tici/hardware.h system/hardware/tici/__init__.py
selfdrive/hardware/tici/hardware.py system/hardware/tici/hardware.h
selfdrive/hardware/tici/pins.py system/hardware/tici/hardware.py
selfdrive/hardware/tici/agnos.py system/hardware/tici/pins.py
selfdrive/hardware/tici/agnos.json system/hardware/tici/agnos.py
selfdrive/hardware/tici/amplifier.py system/hardware/tici/agnos.json
selfdrive/hardware/tici/updater system/hardware/tici/amplifier.py
selfdrive/hardware/tici/iwlist.py system/hardware/tici/updater
selfdrive/hardware/pc/__init__.py system/hardware/tici/iwlist.py
selfdrive/hardware/pc/hardware.py system/hardware/pc/__init__.py
system/hardware/pc/hardware.py
selfdrive/locationd/__init__.py selfdrive/locationd/__init__.py
selfdrive/locationd/.gitignore selfdrive/locationd/.gitignore
@ -208,15 +215,20 @@ selfdrive/locationd/generated/ubx.h
selfdrive/locationd/generated/gps.cpp selfdrive/locationd/generated/gps.cpp
selfdrive/locationd/generated/gps.h selfdrive/locationd/generated/gps.h
selfdrive/locationd/laikad.py
selfdrive/locationd/laikad_helpers.py
selfdrive/locationd/locationd.cc
selfdrive/locationd/locationd.h selfdrive/locationd/locationd.h
selfdrive/locationd/locationd.cc selfdrive/locationd/locationd.cc
selfdrive/locationd/paramsd.py selfdrive/locationd/paramsd.py
selfdrive/locationd/models/.gitignore selfdrive/locationd/models/.gitignore
selfdrive/locationd/models/live_kf.py
selfdrive/locationd/models/car_kf.py selfdrive/locationd/models/car_kf.py
selfdrive/locationd/models/constants.py selfdrive/locationd/models/gnss_kf.py
selfdrive/locationd/models/live_kf.py
selfdrive/locationd/models/live_kf.h selfdrive/locationd/models/live_kf.h
selfdrive/locationd/models/live_kf.cc selfdrive/locationd/models/live_kf.cc
selfdrive/locationd/models/constants.py
selfdrive/locationd/models/gnss_helpers.py
selfdrive/locationd/calibrationd.py selfdrive/locationd/calibrationd.py
@ -277,6 +289,7 @@ selfdrive/ui/soundd/*.cc
selfdrive/ui/soundd/*.h selfdrive/ui/soundd/*.h
selfdrive/ui/soundd/soundd selfdrive/ui/soundd/soundd
selfdrive/ui/soundd/.gitignore selfdrive/ui/soundd/.gitignore
selfdrive/ui/translations/*
selfdrive/ui/qt/*.cc selfdrive/ui/qt/*.cc
selfdrive/ui/qt/*.h selfdrive/ui/qt/*.h
@ -285,31 +298,27 @@ selfdrive/ui/qt/offroad/*.h
selfdrive/ui/qt/offroad/*.qml selfdrive/ui/qt/offroad/*.qml
selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.cc
selfdrive/ui/qt/widgets/*.h selfdrive/ui/qt/widgets/*.h
selfdrive/ui/replay/*.cc
selfdrive/ui/replay/*.h
selfdrive/ui/qt/maps/*.cc selfdrive/ui/qt/maps/*.cc
selfdrive/ui/qt/maps/*.h selfdrive/ui/qt/maps/*.h
selfdrive/camerad/SConscript system/camerad/SConscript
selfdrive/camerad/main.cc system/camerad/main.cc
selfdrive/camerad/snapshot/* system/camerad/snapshot/*
selfdrive/camerad/include/* system/camerad/include/*
selfdrive/camerad/cameras/camera_common.h system/camerad/cameras/camera_common.h
selfdrive/camerad/cameras/camera_common.cc system/camerad/cameras/camera_common.cc
selfdrive/camerad/cameras/sensor2_i2c.h system/camerad/cameras/sensor2_i2c.h
selfdrive/camerad/transforms/rgb_to_yuv.cc system/camerad/transforms/rgb_to_yuv.cc
selfdrive/camerad/transforms/rgb_to_yuv.h system/camerad/transforms/rgb_to_yuv.h
selfdrive/camerad/transforms/rgb_to_yuv.cl system/camerad/transforms/rgb_to_yuv.cl
selfdrive/camerad/transforms/rgb_to_yuv_test.cc system/camerad/transforms/rgb_to_yuv_test.cc
selfdrive/camerad/imgproc/conv.cl system/camerad/imgproc/conv.cl
selfdrive/camerad/imgproc/pool.cl system/camerad/imgproc/pool.cl
selfdrive/camerad/imgproc/utils.cc system/camerad/imgproc/utils.cc
selfdrive/camerad/imgproc/utils.h system/camerad/imgproc/utils.h
selfdrive/manager/__init__.py selfdrive/manager/__init__.py
selfdrive/manager/build.py selfdrive/manager/build.py
@ -407,6 +416,7 @@ pyextra/.gitignore
pyextra/acados_template/** pyextra/acados_template/**
rednose/** rednose/**
laika/**
cereal/.gitignore cereal/.gitignore
cereal/__init__.py cereal/__init__.py
@ -472,7 +482,6 @@ opendbc/gm_global_a_powertrain_generated.dbc
opendbc/gm_global_a_object.dbc opendbc/gm_global_a_object.dbc
opendbc/gm_global_a_chassis.dbc opendbc/gm_global_a_chassis.dbc
opendbc/ford_fusion_2018_adas.dbc
opendbc/ford_lincoln_base_pt.dbc opendbc/ford_lincoln_base_pt.dbc
opendbc/honda_accord_2018_can_generated.dbc opendbc/honda_accord_2018_can_generated.dbc
@ -490,6 +499,7 @@ opendbc/honda_odyssey_exl_2018_generated.dbc
opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc opendbc/honda_odyssey_extreme_edition_2018_china_can_generated.dbc
opendbc/honda_insight_ex_2019_can_generated.dbc opendbc/honda_insight_ex_2019_can_generated.dbc
opendbc/acura_ilx_2016_nidec.dbc opendbc/acura_ilx_2016_nidec.dbc
opendbc/honda_civic_ex_2022_can_generated.dbc
opendbc/kia_ev6.dbc opendbc/kia_ev6.dbc
opendbc/hyundai_kia_generic.dbc opendbc/hyundai_kia_generic.dbc

@ -2,14 +2,14 @@ third_party/snpe/larch64**
third_party/snpe/aarch64-ubuntu-gcc7.5/* third_party/snpe/aarch64-ubuntu-gcc7.5/*
third_party/mapbox-gl-native-qt/include/* third_party/mapbox-gl-native-qt/include/*
selfdrive/timezoned.py system/timezoned.py
selfdrive/assets/navigation/* selfdrive/assets/navigation/*
selfdrive/assets/training_wide/* selfdrive/assets/training_wide/*
selfdrive/camerad/cameras/camera_qcom2.cc system/camerad/cameras/camera_qcom2.cc
selfdrive/camerad/cameras/camera_qcom2.h system/camerad/cameras/camera_qcom2.h
selfdrive/camerad/cameras/real_debayer.cl system/camerad/cameras/real_debayer.cl
selfdrive/ui/qt/spinner_larch64 selfdrive/ui/qt/spinner_larch64
selfdrive/ui/qt/text_larch64 selfdrive/ui/qt/text_larch64

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from selfdrive.hardware import HARDWARE from system.hardware import HARDWARE
if __name__ == "__main__": if __name__ == "__main__":
HARDWARE.set_power_save(False) HARDWARE.set_power_save(False)

@ -32,12 +32,12 @@ from common.basedir import PERSIST
from common.file_helpers import CallbackReader from common.file_helpers import CallbackReader
from common.params import Params from common.params import Params
from common.realtime import sec_since_boot, set_core_affinity from common.realtime import sec_since_boot, set_core_affinity
from selfdrive.hardware import HARDWARE, PC, AGNOS from system.hardware import HARDWARE, PC, AGNOS
from selfdrive.loggerd.config import ROOT from selfdrive.loggerd.config import ROOT
from selfdrive.loggerd.xattr_cache import getxattr, setxattr from selfdrive.loggerd.xattr_cache import getxattr, setxattr
from selfdrive.statsd import STATS_DIR from selfdrive.statsd import STATS_DIR
from selfdrive.swaglog import SWAGLOG_DIR, cloudlog from system.swaglog import SWAGLOG_DIR, cloudlog
from selfdrive.version import get_commit, get_origin, get_short_branch, get_version from system.version import get_commit, get_origin, get_short_branch, get_version
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai') ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4")) HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
@ -493,7 +493,7 @@ def getNetworks():
@dispatcher.add_method @dispatcher.add_method
def takeSnapshot(): def takeSnapshot():
from selfdrive.camerad.snapshot.snapshot import jpeg_write, snapshot from system.camerad.snapshot.snapshot import jpeg_write, snapshot
ret = snapshot() ret = snapshot()
if ret is not None: if ret is not None:
def b64jpeg(x): def b64jpeg(x):

@ -5,8 +5,8 @@ from multiprocessing import Process
from common.params import Params from common.params import Params
from selfdrive.manager.process import launcher from selfdrive.manager.process import launcher
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
from selfdrive.version import get_version, is_dirty from system.version import get_version, is_dirty
ATHENA_MGR_PID_PARAM = "AthenadPid" ATHENA_MGR_PID_PARAM = "AthenadPid"

@ -11,8 +11,8 @@ from common.params import Params
from common.spinner import Spinner from common.spinner import Spinner
from common.basedir import PERSIST from common.basedir import PERSIST
from selfdrive.controls.lib.alertmanager import set_offroad_alert from selfdrive.controls.lib.alertmanager import set_offroad_alert
from selfdrive.hardware import HARDWARE, PC from system.hardware import HARDWARE, PC
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
UNREGISTERED_DONGLE_ID = "UnregisteredDevice" UNREGISTERED_DONGLE_ID = "UnregisteredDevice"

@ -16,7 +16,7 @@ from unittest import mock
from websocket import ABNF from websocket import ABNF
from websocket._exceptions import WebSocketConnectionClosedException from websocket._exceptions import WebSocketConnectionClosedException
from selfdrive import swaglog from system import swaglog
from selfdrive.athena import athenad from selfdrive.athena import athenad
from selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher from selfdrive.athena.athenad import MAX_RETRY_COUNT, dispatcher
from selfdrive.athena.tests.helpers import MockWebsocket, MockParams, MockApi, EchoSocket, with_http_server from selfdrive.athena.tests.helpers import MockWebsocket, MockParams, MockApi, EchoSocket, with_http_server
@ -120,6 +120,13 @@ class TestAthenadMethods(unittest.TestCase):
self.assertTrue(resp, 'list empty!') self.assertTrue(resp, 'list empty!')
self.assertCountEqual(resp, expected) self.assertCountEqual(resp, expected)
def test_strip_bz2_extension(self):
fn = os.path.join(athenad.ROOT, 'qlog.bz2')
Path(fn).touch()
if fn.endswith('.bz2'):
self.assertEqual(athenad.strip_bz2_extension(fn), fn[:-4])
@with_http_server @with_http_server
def test_do_upload(self, host): def test_do_upload(self, host):
fn = os.path.join(athenad.ROOT, 'qlog.bz2') fn = os.path.join(athenad.ROOT, 'qlog.bz2')

@ -27,7 +27,7 @@
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/timing.h" #include "common/timing.h"
#include "common/util.h" #include "common/util.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/boardd/pigeon.h" #include "selfdrive/boardd/pigeon.h"

@ -3,7 +3,7 @@
#include "selfdrive/boardd/boardd.h" #include "selfdrive/boardd/boardd.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
#include "selfdrive/hardware/hw.h" #include "system/hardware/hw.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
LOGW("starting boardd"); LOGW("starting boardd");

@ -10,8 +10,8 @@ from functools import cmp_to_key
from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU from panda import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, Panda, PandaDFU
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.params import Params from common.params import Params
from selfdrive.hardware import HARDWARE from system.hardware import HARDWARE
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
def get_expected_signature(panda: Panda) -> bytes: def get_expected_signature(panda: Panda) -> bytes:

@ -13,7 +13,7 @@ import time
import cereal.messaging as messaging import cereal.messaging as messaging
from common.realtime import Ratekeeper from common.realtime import Ratekeeper
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
from selfdrive.boardd.boardd import can_capnp_to_can_list from selfdrive.boardd.boardd import can_capnp_to_can_list
from cereal import car from cereal import car

@ -12,7 +12,7 @@ from common.spinner import Spinner
from common.timeout import Timeout from common.timeout import Timeout
from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.car import make_can_msg from selfdrive.car import make_can_msg
from selfdrive.hardware import TICI from system.hardware import TICI
from selfdrive.test.helpers import phone_only, with_processes from selfdrive.test.helpers import phone_only, with_processes

@ -14,31 +14,19 @@ Cars are organized into three tiers:
How We Rate The Cars How We Rate The Cars
--- ---
### openpilot Adaptive Cruise Control (ACC) {% for star_row in star_descriptions.values() %}
- {{star_icon.format(Star.FULL.value)}} - openpilot is able to control the gas and brakes. {% for name, stars in star_row.items() %}
- {{star_icon.format(Star.HALF.value)}} - openpilot is able to control the gas and brakes with some restrictions. ### {{name}}
- {{star_icon.format(Star.EMPTY.value)}} - The gas and brakes are controlled by the car's stock Adaptive Cruise Control (ACC) system. {% for star, description in stars %}
- {{star_icon.format(star)}} - {{description}}
### Stop and Go {% endfor %}
- {{star_icon.format(Star.FULL.value)}} - Adaptive Cruise Control (ACC) operates down to 0 mph.
- {{star_icon.format(Star.EMPTY.value)}} - Adaptive Cruise Control (ACC) available only above certain speeds. See your car's manual for the minimum speed.
### Steer to 0
- {{star_icon.format(Star.FULL.value)}} - openpilot can control the steering wheel down to 0 mph.
- {{star_icon.format(Star.EMPTY.value)}} - No steering control below certain speeds.
### Steering Torque
- {{star_icon.format(Star.FULL.value)}} - Car has enough steering torque for comfortable highway driving.
- {{star_icon.format(Star.EMPTY.value)}} - Limited ability to make turns.
### Actively Maintained
- {{star_icon.format(Star.FULL.value)}} - Mainline software support, harness hardware sold by comma, lots of users, primary development target.
- {{star_icon.format(Star.EMPTY.value)}} - Low user count, community maintained, harness hardware not sold by comma.
{% endfor %}
{% endfor %}
**All supported cars can move between the tiers as support changes.** **All supported cars can move between the tiers as support changes.**
{% for tier, cars in tiers.items() %} {% for tier, cars in tiers.items() %}
## {{tier.name.title()}} Cars # {{tier.name.title()}} - {{cars | length}} cars
|{{Column | map(attribute='value') | join('|')}}| |{{Column | map(attribute='value') | join('|')}}|
|---|---|---|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|

@ -1,8 +1,8 @@
import numpy as np import numpy as np
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car.body import bodycan
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car.body import bodycan
from selfdrive.car.body.values import SPEED_FROM_RPM from selfdrive.car.body.values import SPEED_FROM_RPM
from selfdrive.controls.lib.pid import PIDController from selfdrive.controls.lib.pid import PIDController
@ -14,7 +14,7 @@ MAX_POS_INTEGRATOR = 0.2 # meters
MAX_TURN_INTEGRATOR = 0.1 # meters MAX_TURN_INTEGRATOR = 0.1 # meters
class CarController(): class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.frame = 0 self.frame = 0
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)

@ -19,7 +19,6 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = -math.inf ret.minSteerSpeed = -math.inf
ret.maxLateralAccel = math.inf # TODO: set to a reasonable value ret.maxLateralAccel = math.inf # TODO: set to a reasonable value
ret.steerRatio = 0.5 ret.steerRatio = 0.5
ret.steerRateCost = 0.5
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0. ret.steerActuatorDelay = 0.

@ -4,12 +4,12 @@ from typing import Dict, List
from cereal import car from cereal import car
from common.params import Params from common.params import Params
from common.basedir import BASEDIR from common.basedir import BASEDIR
from selfdrive.version import is_comma_remote, is_tested_branch from system.version import is_comma_remote, is_tested_branch
from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, VIN_UNKNOWN from selfdrive.car.vin import get_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car, get_present_ecus
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
import cereal.messaging as messaging import cereal.messaging as messaging
from selfdrive.car import gen_empty_fingerprint from selfdrive.car import gen_empty_fingerprint
@ -79,6 +79,7 @@ interfaces = load_interfaces(interface_names)
def fingerprint(logcan, sendcan): def fingerprint(logcan, sendcan):
fixed_fingerprint = os.environ.get('FINGERPRINT', "") fixed_fingerprint = os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
ecu_responses = set()
if not fixed_fingerprint and not skip_fw_query: if not fixed_fingerprint and not skip_fw_query:
# Vin query only reliably works thorugh OBDII # Vin query only reliably works thorugh OBDII
@ -97,6 +98,7 @@ def fingerprint(logcan, sendcan):
else: else:
cloudlog.warning("Getting VIN & FW versions") cloudlog.warning("Getting VIN & FW versions")
_, vin = get_vin(logcan, sendcan, bus) _, vin = get_vin(logcan, sendcan, bus)
ecu_responses = get_present_ecus(logcan, sendcan)
car_fw = get_fw_versions(logcan, sendcan) car_fw = get_fw_versions(logcan, sendcan)
exact_fw_match, fw_candidates = match_fw_to_car(car_fw) exact_fw_match, fw_candidates = match_fw_to_car(car_fw)
@ -113,7 +115,7 @@ def fingerprint(logcan, sendcan):
finger = gen_empty_fingerprint() finger = gen_empty_fingerprint()
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1 candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
frame = 0 frame = 0
frame_fingerprint = 25 # 0.25s frame_fingerprint = 100 # 1s
car_fingerprint = None car_fingerprint = None
done = False done = False
@ -163,8 +165,8 @@ def fingerprint(logcan, sendcan):
car_fingerprint = fixed_fingerprint car_fingerprint = fixed_fingerprint
source = car.CarParams.FingerprintSource.fixed source = car.CarParams.FingerprintSource.fixed
cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, cloudlog.event("fingerprinted", car_fingerprint=car_fingerprint, source=source, fuzzy=not exact_match,
source=source, fuzzy=not exact_match, fw_count=len(car_fw)) fw_count=len(car_fw), ecu_responses=ecu_responses, error=True)
return car_fingerprint, finger, vin, car_fw, source, exact_match return car_fingerprint, finger, vin, car_fw, source, exact_match

@ -1,7 +1,7 @@
from cereal import car from cereal import car
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_toyota_steer_torque_limits from selfdrive.car import apply_toyota_steer_torque_limits
from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_wheel_buttons from selfdrive.car.chrysler.chryslercan import create_lkas_hud, create_lkas_command, create_cruise_buttons
from selfdrive.car.chrysler.values import CAR, CarControllerParams from selfdrive.car.chrysler.values import CAR, CarControllerParams
@ -49,7 +49,7 @@ class CarController:
# *** control msgs *** # *** control msgs ***
if CC.cruiseControl.cancel: if CC.cruiseControl.cancel:
can_sends.append(create_wheel_buttons(self.packer, CS.button_counter + 1, cancel=True)) can_sends.append(create_cruise_buttons(self.packer, CS.button_counter + 1, cancel=True))
# LKAS_HEARTBIT is forwarded by Panda so no need to send it here. # LKAS_HEARTBIT is forwarded by Panda so no need to send it here.
# frame is 100Hz (0.01s period) # frame is 100Hz (0.01s period)

@ -16,26 +16,29 @@ class CarState(CarStateBase):
ret = car.CarState.new_message() ret = car.CarState.new_message()
self.frame = int(cp.vl["EPS_STATUS"]["COUNTER"]) self.frame = int(cp.vl["EPS_2"]["COUNTER"])
ret.doorOpen = any([cp.vl["DOORS"]["DOOR_OPEN_FL"], ret.doorOpen = any([cp.vl["BCM_1"]["DOOR_OPEN_FL"],
cp.vl["DOORS"]["DOOR_OPEN_FR"], cp.vl["BCM_1"]["DOOR_OPEN_FR"],
cp.vl["DOORS"]["DOOR_OPEN_RL"], cp.vl["BCM_1"]["DOOR_OPEN_RL"],
cp.vl["DOORS"]["DOOR_OPEN_RR"]]) cp.vl["BCM_1"]["DOOR_OPEN_RR"]])
ret.seatbeltUnlatched = cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_UNLATCHED"] == 1 ret.seatbeltUnlatched = cp.vl["ORC_1"]["SEATBELT_DRIVER_UNLATCHED"] == 1
ret.brakePressed = cp.vl["BRAKE_2"]["BRAKE_PRESSED_2"] == 5 # human-only # brake pedal
ret.brake = 0 ret.brake = 0
ret.gas = cp.vl["ACCEL_GAS_134"]["ACCEL_134"] ret.brakePressed = cp.vl["ESP_1"]['Brake_Pedal_State'] == 1 # Physical brake pedal switch
# gas pedal
ret.gas = cp.vl["ECM_5"]["Accelerator_Position"]
ret.gasPressed = ret.gas > 1e-5 ret.gasPressed = ret.gas > 1e-5
ret.espDisabled = (cp.vl["TRACTION_BUTTON"]["TRACTION_OFF"] == 1) ret.espDisabled = (cp.vl["TRACTION_BUTTON"]["TRACTION_OFF"] == 1)
ret.wheelSpeeds = self.get_wheel_speeds( ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"], cp.vl["ESP_6"]["WHEEL_SPEED_FL"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"], cp.vl["ESP_6"]["WHEEL_SPEED_FR"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"], cp.vl["ESP_6"]["WHEEL_SPEED_RL"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"], cp.vl["ESP_6"]["WHEEL_SPEED_RR"],
unit=1, unit=1,
) )
ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2. ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2.
@ -44,21 +47,21 @@ class CarState(CarStateBase):
ret.leftBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 1 ret.leftBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 1
ret.rightBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 2 ret.rightBlinker = cp.vl["STEERING_LEVERS"]["TURN_SIGNALS"] == 2
ret.steeringAngleDeg = cp.vl["STEERING"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEERING"]["STEERING_RATE"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["GEAR"]["PRNDL"], None)) ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(cp.vl["GEAR"]["PRNDL"], None))
ret.cruiseState.enabled = cp.vl["ACC_2"]["ACC_STATUS_2"] == 7 # ACC is green. ret.cruiseState.available = cp.vl["DAS_3"]["ACC_AVAILABLE"] == 1 # ACC is white
ret.cruiseState.available = ret.cruiseState.enabled # FIXME: for now same as enabled ret.cruiseState.enabled = cp.vl["DAS_3"]["ACC_ACTIVE"] == 1 # ACC is green
ret.cruiseState.speed = cp.vl["DASHBOARD"]["ACC_SPEED_CONFIG_KPH"] * CV.KPH_TO_MS ret.cruiseState.speed = cp.vl["DAS_4"]["ACC_SPEED_CONFIG_KPH"] * CV.KPH_TO_MS
# CRUISE_STATE is a three bit msg, 0 is off, 1 and 2 are Non-ACC mode, 3 and 4 are ACC mode, find if there are other states too # CRUISE_STATE is a three bit msg, 0 is off, 1 and 2 are Non-ACC mode, 3 and 4 are ACC mode, find if there are other states too
ret.cruiseState.nonAdaptive = cp.vl["DASHBOARD"]["CRUISE_STATE"] in (1, 2) ret.cruiseState.nonAdaptive = cp.vl["DAS_4"]["CRUISE_STATE"] in (1, 2)
ret.accFaulted = cp.vl["ACC_2"]["ACC_FAULTED"] != 0 ret.accFaulted = cp.vl["DAS_3"]["ACC_FAULTED"] != 0
ret.steeringTorque = cp.vl["EPS_STATUS"]["TORQUE_DRIVER"] ret.steeringAngleDeg = cp.vl["STEERING"]["STEER_ANGLE"]
ret.steeringTorqueEps = cp.vl["EPS_STATUS"]["TORQUE_MOTOR"] ret.steeringRateDeg = cp.vl["STEERING"]["STEERING_RATE"]
ret.steeringTorque = cp.vl["EPS_2"]["COLUMN_TORQUE"]
ret.steeringTorqueEps = cp.vl["EPS_2"]["EPS_TORQUE_MOTOR"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
steer_state = cp.vl["EPS_STATUS"]["LKAS_STATE"] steer_state = cp.vl["EPS_2"]["LKAS_STATE"]
ret.steerFaultPermanent = steer_state == 4 or (steer_state == 0 and ret.vEgo > self.CP.minSteerSpeed) ret.steerFaultPermanent = steer_state == 4 or (steer_state == 0 and ret.vEgo > self.CP.minSteerSpeed)
ret.genericToggle = bool(cp.vl["STEERING_LEVERS"]["HIGH_BEAM_FLASH"]) ret.genericToggle = bool(cp.vl["STEERING_LEVERS"]["HIGH_BEAM_FLASH"])
@ -70,7 +73,7 @@ class CarState(CarStateBase):
self.lkas_counter = cp_cam.vl["LKAS_COMMAND"]["COUNTER"] self.lkas_counter = cp_cam.vl["LKAS_COMMAND"]["COUNTER"]
self.lkas_car_model = cp_cam.vl["LKAS_HUD"]["CAR_MODEL"] self.lkas_car_model = cp_cam.vl["LKAS_HUD"]["CAR_MODEL"]
self.lkas_status_ok = cp_cam.vl["LKAS_HEARTBIT"]["LKAS_STATUS_OK"] self.lkas_status_ok = cp_cam.vl["LKAS_HEARTBIT"]["LKAS_STATUS_OK"]
self.button_counter = cp.vl["WHEEL_BUTTONS"]["COUNTER"] self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"]
return ret return ret
@ -79,50 +82,51 @@ class CarState(CarStateBase):
signals = [ signals = [
# sig_name, sig_address # sig_name, sig_address
("PRNDL", "GEAR"), ("PRNDL", "GEAR"),
("DOOR_OPEN_FL", "DOORS"), ("DOOR_OPEN_FL", "BCM_1"),
("DOOR_OPEN_FR", "DOORS"), ("DOOR_OPEN_FR", "BCM_1"),
("DOOR_OPEN_RL", "DOORS"), ("DOOR_OPEN_RL", "BCM_1"),
("DOOR_OPEN_RR", "DOORS"), ("DOOR_OPEN_RR", "BCM_1"),
("BRAKE_PRESSED_2", "BRAKE_2"), ("Brake_Pedal_State", "ESP_1"),
("ACCEL_134", "ACCEL_GAS_134"), ("Accelerator_Position", "ECM_5"),
("SPEED_LEFT", "SPEED_1"), ("SPEED_LEFT", "SPEED_1"),
("SPEED_RIGHT", "SPEED_1"), ("SPEED_RIGHT", "SPEED_1"),
("WHEEL_SPEED_FL", "WHEEL_SPEEDS"), ("WHEEL_SPEED_FL", "ESP_6"),
("WHEEL_SPEED_RR", "WHEEL_SPEEDS"), ("WHEEL_SPEED_RR", "ESP_6"),
("WHEEL_SPEED_RL", "WHEEL_SPEEDS"), ("WHEEL_SPEED_RL", "ESP_6"),
("WHEEL_SPEED_FR", "WHEEL_SPEEDS"), ("WHEEL_SPEED_FR", "ESP_6"),
("STEER_ANGLE", "STEERING"), ("STEER_ANGLE", "STEERING"),
("STEERING_RATE", "STEERING"), ("STEERING_RATE", "STEERING"),
("TURN_SIGNALS", "STEERING_LEVERS"), ("TURN_SIGNALS", "STEERING_LEVERS"),
("ACC_STATUS_2", "ACC_2"), ("ACC_AVAILABLE", "DAS_3"),
("ACC_FAULTED", "ACC_2"), ("ACC_ACTIVE", "DAS_3"),
("ACC_FAULTED", "DAS_3"),
("HIGH_BEAM_FLASH", "STEERING_LEVERS"), ("HIGH_BEAM_FLASH", "STEERING_LEVERS"),
("ACC_SPEED_CONFIG_KPH", "DASHBOARD"), ("ACC_SPEED_CONFIG_KPH", "DAS_4"),
("CRUISE_STATE", "DASHBOARD"), ("CRUISE_STATE", "DAS_4"),
("TORQUE_DRIVER", "EPS_STATUS"), ("COLUMN_TORQUE", "EPS_2"),
("TORQUE_MOTOR", "EPS_STATUS"), ("EPS_TORQUE_MOTOR", "EPS_2"),
("LKAS_STATE", "EPS_STATUS"), ("LKAS_STATE", "EPS_2"),
("COUNTER", "EPS_STATUS",), ("COUNTER", "EPS_2",),
("TRACTION_OFF", "TRACTION_BUTTON"), ("TRACTION_OFF", "TRACTION_BUTTON"),
("SEATBELT_DRIVER_UNLATCHED", "SEATBELT_STATUS"), ("SEATBELT_DRIVER_UNLATCHED", "ORC_1"),
("COUNTER", "WHEEL_BUTTONS"), ("COUNTER", "CRUISE_BUTTONS"),
] ]
checks = [ checks = [
# sig_address, frequency # sig_address, frequency
("BRAKE_2", 50), ("ESP_1", 50),
("EPS_STATUS", 100), ("EPS_2", 100),
("SPEED_1", 100), ("SPEED_1", 100),
("WHEEL_SPEEDS", 50), ("ESP_6", 50),
("STEERING", 100), ("STEERING", 100),
("ACC_2", 50), ("DAS_3", 50),
("GEAR", 50), ("GEAR", 50),
("ACCEL_GAS_134", 50), ("ECM_5", 50),
("WHEEL_BUTTONS", 50), ("CRUISE_BUTTONS", 50),
("DASHBOARD", 15), ("DAS_4", 15),
("STEERING_LEVERS", 10), ("STEERING_LEVERS", 10),
("SEATBELT_STATUS", 2), ("ORC_1", 2),
("DOORS", 1), ("BCM_1", 1),
("TRACTION_BUTTON", 1), ("TRACTION_BUTTON", 1),
] ]

@ -48,10 +48,9 @@ def create_lkas_command(packer, apply_steer, moving_fast, frame):
return packer.make_can_msg("LKAS_COMMAND", 0, values) return packer.make_can_msg("LKAS_COMMAND", 0, values)
def create_wheel_buttons(packer, frame, cancel=False): def create_cruise_buttons(packer, frame, cancel=False):
# WHEEL_BUTTONS (571) Message sent to cancel ACC.
values = { values = {
"ACC_CANCEL": cancel, "ACC_Cancel": cancel,
"COUNTER": frame % 0x10 "COUNTER": frame % 0x10,
} }
return packer.make_can_msg("WHEEL_BUTTONS", 0, values) return packer.make_can_msg("CRUISE_BUTTONS", 0, values)

@ -12,37 +12,38 @@ class CarInterface(CarInterfaceBase):
ret.carName = "chrysler" ret.carName = "chrysler"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.chrysler)]
# Speed conversion: 20, 45 mph
ret.wheelbase = 3.089 # in meters for Pacifica Hybrid 2017
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
ret.mass = 2242. + STD_CARGO_KG # kg curb weight Pacifica Hybrid 2017
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
ret.lateralTuning.pid.kf = 0.00006 # full torque for 10 deg at 80mph means 0.00007818594
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRateCost = 0.7
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
# set max lateral acceleration
if candidate in (CAR.PACIFICA_2018, CAR.PACIFICA_2019_HYBRID, CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019):
ret.maxLateralAccel = 1.6
if candidate in (CAR.PACIFICA_2018_HYBRID,):
ret.maxLateralAccel = 1.4
if candidate in (CAR.PACIFICA_2017_HYBRID,):
ret.maxLateralAccel = 1.2
if candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019):
ret.wheelbase = 2.91 # in meters
ret.steerRatio = 12.7
ret.steerActuatorDelay = 0.2 # in seconds
ret.centerToFront = ret.wheelbase * 0.44
ret.minSteerSpeed = 3.8 # m/s ret.minSteerSpeed = 3.8 # m/s
if candidate in (CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020, CAR.JEEP_CHEROKEE_2019): 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. # TODO: allow 2019 cars to steer down to 13 m/s if already engaged.
ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged. ret.minSteerSpeed = 17.5 # m/s 17 on the way up, 13 on the way down once engaged.
# Chrysler
if candidate in (CAR.PACIFICA_2017_HYBRID, CAR.PACIFICA_2018, CAR.PACIFICA_2018_HYBRID, CAR.PACIFICA_2019_HYBRID, CAR.PACIFICA_2020):
ret.mass = 2242. + STD_CARGO_KG
ret.wheelbase = 3.089
ret.steerRatio = 16.2 # Pacifica Hybrid 2017
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
ret.lateralTuning.pid.kf = 0.00006
# Jeep
elif candidate in (CAR.JEEP_CHEROKEE, CAR.JEEP_CHEROKEE_2019):
ret.mass = 1778 + STD_CARGO_KG
ret.wheelbase = 2.71
ret.steerRatio = 16.7
ret.steerActuatorDelay = 0.2
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kiBP = [[9., 20.], [9., 20.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.15, 0.30], [0.03, 0.05]]
ret.lateralTuning.pid.kf = 0.00006
else:
raise ValueError(f"Unsupported car: {candidate}")
ret.centerToFront = ret.wheelbase * 0.44
# starting with reasonable value for civic and scaling by mass and wheelbase # starting with reasonable value for civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
@ -54,7 +55,6 @@ class CarInterface(CarInterfaceBase):
return ret return ret
# returns a car.CarState
def _update(self, c): def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam) ret = self.CS.update(self.cp, self.cp_cam)
@ -63,14 +63,17 @@ class CarInterface(CarInterfaceBase):
# events # events
events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low]) events = self.create_common_events(ret, extra_gears=[car.CarState.GearShifter.low])
if ret.vEgo < self.CP.minSteerSpeed: # Low speed steer alert hysteresis logic
if self.CP.minSteerSpeed > 0. and ret.vEgo < (self.CP.minSteerSpeed + 1.):
self.low_speed_alert = True
elif ret.vEgo > (self.CP.minSteerSpeed + 2.):
self.low_speed_alert = False
if self.low_speed_alert:
events.add(car.CarEvent.EventName.belowSteerSpeed) events.add(car.CarEvent.EventName.belowSteerSpeed)
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
# pass in a car.CarControl
# to be called @ 100hz
def apply(self, c): def apply(self, c):
return self.CC.update(c, self.CS) return self.CC.update(c, self.CS)

@ -16,11 +16,14 @@ class CarControllerParams:
class CAR: class CAR:
# Chrysler
PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017" PACIFICA_2017_HYBRID = "CHRYSLER PACIFICA HYBRID 2017"
PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018" PACIFICA_2018_HYBRID = "CHRYSLER PACIFICA HYBRID 2018"
PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019" PACIFICA_2019_HYBRID = "CHRYSLER PACIFICA HYBRID 2019"
PACIFICA_2018 = "CHRYSLER PACIFICA 2018" # includes 2017 Pacifica PACIFICA_2018 = "CHRYSLER PACIFICA 2018" # includes 2017 Pacifica
PACIFICA_2020 = "CHRYSLER PACIFICA 2020" PACIFICA_2020 = "CHRYSLER PACIFICA 2020"
# Jeep
JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk JEEP_CHEROKEE = "JEEP GRAND CHEROKEE V6 2018" # includes 2017 Trailhawk
JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk JEEP_CHEROKEE_2019 = "JEEP GRAND CHEROKEE 2019" # includes 2020 Trailhawk
@ -34,9 +37,9 @@ class ChryslerCarInfo(CarInfo):
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"), CAR.PACIFICA_2017_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2017-18"),
CAR.PACIFICA_2018_HYBRID: None, # same platforms CAR.PACIFICA_2018_HYBRID: None, # same platforms
CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-21"), CAR.PACIFICA_2019_HYBRID: ChryslerCarInfo("Chrysler Pacifica Hybrid 2019-22"),
CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"), CAR.PACIFICA_2018: ChryslerCarInfo("Chrysler Pacifica 2017-18"),
CAR.PACIFICA_2020: ChryslerCarInfo("Chrysler Pacifica 2020"), CAR.PACIFICA_2020: ChryslerCarInfo("Chrysler Pacifica 2019-20"),
CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), 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-20", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-20", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"),
} }
@ -85,7 +88,7 @@ FINGERPRINTS = {
}, },
# Based on "8190c7275a24557b|2020-02-24--09-57-23" # Based on "8190c7275a24557b|2020-02-24--09-57-23"
{ {
168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 650: 8, 653: 8, 654: 8, 655: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 683: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 711: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 793: 8, 794: 8, 795: 8, 796: 8, 797: 8, 798: 8, 799: 8, 800: 8, 801: 8, 802: 8, 803: 8, 804: 8, 805: 8, 807: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 886: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1568: 8, 1570: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2015: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8 168: 8, 257: 5, 258: 8, 264: 8, 268: 8, 270: 8, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 291: 8, 292: 8, 294: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 368: 8, 376: 3, 384: 8, 388: 4, 448: 6, 456: 4, 464: 8, 469: 8, 480: 8, 500: 8, 501: 8, 512: 8, 514: 8, 515: 7, 516: 7, 517: 7, 518: 7, 520: 8, 524: 8, 526: 6, 528: 8, 532: 8, 542: 8, 544: 8, 557: 8, 559: 8, 560: 8, 564: 8, 571: 3, 579: 8, 584: 8, 608: 8, 624: 8, 625: 8, 632: 8, 639: 8, 640: 1, 650: 8, 653: 8, 654: 8, 655: 8, 656: 4, 658: 6, 660: 8, 669: 3, 671: 8, 672: 8, 678: 8, 680: 8, 683: 8, 701: 8, 703: 8, 704: 8, 705: 8, 706: 8, 709: 8, 710: 8, 711: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 764: 8, 766: 8, 770: 8, 773: 8, 779: 8, 782: 8, 784: 8, 792: 8, 793: 8, 794: 8, 795: 8, 796: 8, 797: 8, 798: 8, 799: 8, 800: 8, 801: 8, 802: 8, 803: 8, 804: 8, 805: 8, 807: 8, 808: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 832: 8, 838: 2, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 878: 8, 882: 8, 886: 8, 897: 8, 906: 8, 908: 8, 924: 8, 926: 3, 929: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 958: 8, 959: 8, 962: 8, 969: 4, 973: 8, 974: 5, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1082: 8, 1083: 8, 1098: 8, 1100: 8, 1216: 8, 1218: 8, 1220: 8, 1225: 8, 1235: 8, 1242: 8, 1246: 8, 1250: 8, 1251: 8, 1252: 8, 1258: 8, 1259: 8, 1260: 8, 1262: 8, 1284: 8, 1536: 8, 1568: 8, 1570: 8, 1856: 8, 1858: 8, 1860: 8, 1863: 8, 1865: 8, 1875: 8, 1882: 8, 1886: 8, 1890: 8, 1891: 8, 1892: 8, 1898: 8, 1899: 8, 1900: 8, 1902: 8, 2015: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2023: 8, 2024: 8, 2026: 8, 2027: 8, 2028: 8, 2031: 8
}], }],
CAR.JEEP_CHEROKEE: [{ CAR.JEEP_CHEROKEE: [{
55: 8, 168: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 975: 8, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1543: 8, 1562: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8 55: 8, 168: 8, 181: 8, 256: 4, 257: 5, 258: 8, 264: 8, 268: 8, 272: 6, 273: 6, 274: 2, 280: 8, 284: 8, 288: 7, 290: 6, 292: 8, 300: 8, 308: 8, 320: 8, 324: 8, 331: 8, 332: 8, 344: 8, 352: 8, 362: 8, 368: 8, 376: 3, 384: 8, 388: 4, 416: 7, 448: 6, 456: 4, 464: 8, 500: 8, 501: 8, 512: 8, 514: 8, 520: 8, 532: 8, 544: 8, 557: 8, 559: 8, 560: 4, 564: 4, 571: 3, 579: 8, 584: 8, 608: 8, 618: 8, 624: 8, 625: 8, 632: 8, 639: 8, 656: 4, 658: 6, 660: 8, 671: 8, 672: 8, 676: 8, 678: 8, 680: 8, 683: 8, 684: 8, 703: 8, 705: 8, 706: 8, 709: 8, 710: 8, 719: 8, 720: 6, 729: 5, 736: 8, 737: 8, 738: 8, 746: 5, 752: 2, 754: 8, 760: 8, 761: 8, 764: 8, 766: 8, 773: 8, 776: 8, 779: 8, 782: 8, 783: 8, 784: 8, 785: 8, 788: 3, 792: 8, 799: 8, 800: 8, 804: 8, 806: 2, 808: 8, 810: 8, 816: 8, 817: 8, 820: 8, 825: 2, 826: 8, 831: 6, 832: 8, 838: 2, 840: 8, 844: 5, 847: 1, 848: 8, 853: 8, 856: 4, 860: 6, 863: 8, 874: 2, 882: 8, 897: 8, 906: 8, 924: 8, 937: 8, 938: 8, 939: 8, 940: 8, 941: 8, 942: 8, 943: 8, 947: 8, 948: 8, 956: 8, 968: 8, 969: 4, 970: 8, 973: 8, 974: 5, 975: 8, 976: 8, 977: 4, 979: 8, 980: 8, 981: 8, 982: 8, 983: 8, 984: 8, 992: 8, 993: 7, 995: 8, 996: 8, 1000: 8, 1001: 8, 1002: 8, 1003: 8, 1008: 8, 1009: 8, 1010: 8, 1011: 8, 1012: 8, 1013: 8, 1014: 8, 1015: 8, 1024: 8, 1025: 8, 1026: 8, 1031: 8, 1033: 8, 1050: 8, 1059: 8, 1062: 8, 1098: 8, 1100: 8, 1543: 8, 1562: 8, 2015: 8, 2016: 8, 2017: 8, 2024: 8, 2025: 8

@ -1,5 +1,5 @@
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
EXT_DIAG_REQUEST = b'\x10\x03' EXT_DIAG_REQUEST = b'\x10\x03'
EXT_DIAG_RESPONSE = b'\x50\x03' EXT_DIAG_RESPONSE = b'\x50\x03'

@ -7,7 +7,7 @@ from natsort import natsorted
from typing import Dict, List from typing import Dict, List
from common.basedir import BASEDIR from common.basedir import BASEDIR
from selfdrive.car.docs_definitions import CarInfo, Column, Star, Tier from selfdrive.car.docs_definitions import STAR_DESCRIPTIONS, CarInfo, Column, Star, Tier
from selfdrive.car.car_helpers import interfaces, get_interface_attr 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 from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR as HKG_RADAR_START_ADDR
from selfdrive.car.tests.routes import non_tested_cars from selfdrive.car.tests.routes import non_tested_cars
@ -65,7 +65,7 @@ def generate_cars_md(all_car_info: List[CarInfo], template_fn: str) -> str:
footnotes = [fn.value.text for fn in ALL_FOOTNOTES] footnotes = [fn.value.text for fn in ALL_FOOTNOTES]
cars_md: str = template.render(tiers=sort_by_tier(all_car_info), all_car_info=all_car_info, cars_md: str = template.render(tiers=sort_by_tier(all_car_info), all_car_info=all_car_info,
footnotes=footnotes, Star=Star, Column=Column) footnotes=footnotes, Star=Star, Column=Column, star_descriptions=STAR_DESCRIPTIONS)
return cars_md return cars_md

@ -6,7 +6,9 @@ from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Dict, List, Optional, Union, no_type_check from typing import Dict, List, Optional, Union, no_type_check
STEERING_TORQUE_THRESHOLD = 2.0 # m/s^2 TACO_TORQUE_THRESHOLD = 2.5 # m/s^2
GREAT_TORQUE_THRESHOLD = 1.4 # m/s^2
GOOD_TORQUE_THRESHOLD = 1.0 # m/s^2
class Tier(Enum): class Tier(Enum):
@ -53,7 +55,6 @@ class CarInfo:
footnotes: Optional[List[Enum]] = None footnotes: Optional[List[Enum]] = None
min_steer_speed: Optional[float] = None min_steer_speed: Optional[float] = None
min_enable_speed: Optional[float] = None min_enable_speed: Optional[float] = None
good_torque: bool = False
harness: Optional[Enum] = None harness: Optional[Enum] = None
def init(self, CP: car.CarParams, non_tested_cars: List[str], all_footnotes: Dict[Enum, int]): def init(self, CP: car.CarParams, non_tested_cars: List[str], all_footnotes: Dict[Enum, int]):
@ -70,11 +71,6 @@ class CarInfo:
if self.min_enable_speed is not None: if self.min_enable_speed is not None:
min_enable_speed = self.min_enable_speed min_enable_speed = self.min_enable_speed
# TODO: remove hardcoded good torque and just use maxLateralAccel
good_torque = self.good_torque
if not math.isnan(CP.maxLateralAccel):
good_torque = CP.maxLateralAccel >= STEERING_TORQUE_THRESHOLD
self.car_name = CP.carName self.car_name = CP.carName
self.make, self.model = self.name.split(' ', 1) self.make, self.model = self.name.split(' ', 1)
self.row = { self.row = {
@ -82,21 +78,28 @@ class CarInfo:
Column.MODEL: self.model, Column.MODEL: self.model,
Column.PACKAGE: self.package, Column.PACKAGE: self.package,
# StarColumns # StarColumns
Column.LONGITUDINAL: CP.openpilotLongitudinalControl and not CP.radarOffCan, Column.LONGITUDINAL: Star.FULL if CP.openpilotLongitudinalControl and not CP.radarOffCan else Star.EMPTY,
Column.FSR_LONGITUDINAL: min_enable_speed <= 0., Column.FSR_LONGITUDINAL: Star.FULL if min_enable_speed <= 0. else Star.EMPTY,
Column.FSR_STEERING: min_steer_speed <= 0., Column.FSR_STEERING: Star.FULL if min_steer_speed <= 0. else Star.EMPTY,
Column.STEERING_TORQUE: good_torque, # Column.STEERING_TORQUE set below
Column.MAINTAINED: CP.carFingerprint not in non_tested_cars and self.harness is not Harness.none, Column.MAINTAINED: Star.FULL if CP.carFingerprint not in non_tested_cars and self.harness is not Harness.none else Star.EMPTY,
} }
# Set steering torque star from max lateral acceleration
if not math.isnan(CP.maxLateralAccel):
if CP.maxLateralAccel >= GREAT_TORQUE_THRESHOLD:
self.row[Column.STEERING_TORQUE] = Star.FULL
elif CP.maxLateralAccel >= GOOD_TORQUE_THRESHOLD:
self.row[Column.STEERING_TORQUE] = Star.HALF
else:
self.row[Column.STEERING_TORQUE] = Star.EMPTY
if CP.notCar: if CP.notCar:
for col in StarColumns: for col in StarColumns:
self.row[col] = True self.row[col] = Star.FULL
self.all_footnotes = all_footnotes self.all_footnotes = all_footnotes
for column in StarColumns: for column in StarColumns:
self.row[column] = Star.FULL if self.row[column] else Star.EMPTY
# Demote if footnote specifies a star # Demote if footnote specifies a star
footnote = get_footnote(self.footnotes, column) footnote = get_footnote(self.footnotes, column)
if footnote is not None and footnote.value.star is not None: if footnote is not None and footnote.value.star is not None:
@ -120,7 +123,8 @@ class CarInfo:
class Harness(Enum): class Harness(Enum):
nidec = "Honda Nidec" nidec = "Honda Nidec"
bosch = "Honda Bosch" bosch_a = "Honda Bosch A"
bosch_b = "Honda Bosch B"
toyota = "Toyota" toyota = "Toyota"
subaru = "Subaru" subaru = "Subaru"
fca = "FCA" fca = "FCA"
@ -141,9 +145,42 @@ class Harness(Enum):
hyundai_m = "Hyundai M" hyundai_m = "Hyundai M"
hyundai_n = "Hyundai N" hyundai_n = "Hyundai N"
hyundai_o = "Hyundai O" hyundai_o = "Hyundai O"
hyundai_p = "Hyundai P"
custom = "Developer" custom = "Developer"
obd_ii = "OBD-II" obd_ii = "OBD-II"
nissan_a = "Nissan A" nissan_a = "Nissan A"
nissan_b = "Nissan B" nissan_b = "Nissan B"
mazda = "Mazda" mazda = "Mazda"
none = "None" none = "None"
STAR_DESCRIPTIONS = {
"Gas & Brakes": { # icon and row name
"openpilot Adaptive Cruise Control (ACC)": [ # star column
[Star.FULL.value, "openpilot is able to control the gas and brakes."],
[Star.HALF.value, "openpilot is able to control the gas and brakes with some restrictions."],
[Star.EMPTY.value, "The gas and brakes are controlled by the car's stock Adaptive Cruise Control (ACC) system."],
],
Column.FSR_LONGITUDINAL.value: [
[Star.FULL.value, "Adaptive Cruise Control (ACC) operates down to 0 mph."],
[Star.EMPTY.value, "Adaptive Cruise Control (ACC) available only above certain speeds. 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."],
],
Column.STEERING_TORQUE.value: [
[Star.FULL.value, "Car has enough steering torque to take tighter turns."],
[Star.HALF.value, "Car has enough steering torque for comfortable highway driving."],
[Star.EMPTY.value, "Limited ability to make turns."],
],
},
"Support": {
Column.MAINTAINED.value: [
[Star.FULL.value, "Mainline software support, harness hardware sold by comma, lots of users, primary development target."],
[Star.EMPTY.value, "Low user count, community maintained, harness hardware not sold by comma."],
],
},
}

@ -0,0 +1,90 @@
#!/usr/bin/env python3
import capnp
import time
import traceback
from typing import Optional, Set, Tuple
import cereal.messaging as messaging
from panda.python.uds import SERVICE_TYPE
from selfdrive.car import make_can_msg
from selfdrive.boardd.boardd import can_list_to_can_capnp
from system.swaglog import cloudlog
def make_tester_present_msg(addr, bus, subaddr=None):
dat = [0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0]
if subaddr is not None:
dat.insert(0, subaddr)
dat.extend([0x0] * (8 - len(dat)))
return make_can_msg(addr, bytes(dat), bus)
def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader, subaddr: Optional[int] = None) -> bool:
# ISO-TP messages are always padded to 8 bytes
# tester present response is always a single frame
dat_offset = 1 if subaddr is not None else 0
if len(msg.dat) == 8 and 1 <= msg.dat[dat_offset] <= 7:
# success response
if msg.dat[dat_offset + 1] == (SERVICE_TYPE.TESTER_PRESENT + 0x40):
return True
# error response
if msg.dat[dat_offset + 1] == 0x7F and msg.dat[dat_offset + 2] == SERVICE_TYPE.TESTER_PRESENT:
return True
return False
def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[Tuple[int, Optional[int], int]]:
addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)]
queries: Set[Tuple[int, Optional[int], int]] = {(addr, None, bus) for addr in addr_list}
responses = queries
return get_ecu_addrs(logcan, sendcan, queries, responses, timeout=timeout, debug=debug)
def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, queries: Set[Tuple[int, Optional[int], int]],
responses: Set[Tuple[int, Optional[int], int]], timeout: float = 1, debug: bool = False) -> Set[Tuple[int, Optional[int], int]]:
ecu_responses: Set[Tuple[int, Optional[int], int]] = set() # set((addr, subaddr, bus),)
try:
msgs = [make_tester_present_msg(addr, bus, subaddr) for addr, subaddr, bus in queries]
messaging.drain_sock_raw(logcan)
sendcan.send(can_list_to_can_capnp(msgs, msgtype='sendcan'))
start_time = time.monotonic()
while time.monotonic() - start_time < timeout:
can_packets = messaging.drain_sock(logcan, wait_for_one=True)
for packet in can_packets:
for msg in packet.can:
subaddr = None if (msg.address, None, msg.src) in responses else msg.dat[0]
if (msg.address, subaddr, msg.src) in responses and is_tester_present_response(msg, subaddr):
if debug:
print(f"CAN-RX: {hex(msg.address)} - 0x{bytes.hex(msg.dat)}")
if (msg.address, subaddr, msg.src) in ecu_responses:
print(f"Duplicate ECU address: {hex(msg.address)}")
ecu_responses.add((msg.address, subaddr, msg.src))
except Exception:
cloudlog.warning(f"ECU addr scan exception: {traceback.format_exc()}")
return ecu_responses
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Get addresses of all ECUs')
parser.add_argument('--debug', action='store_true')
args = parser.parse_args()
logcan = messaging.sub_sock('can')
sendcan = messaging.pub_sock('sendcan')
time.sleep(1.0)
print("Getting ECU addresses ...")
ecu_addrs = get_all_ecu_addrs(logcan, sendcan, 1, debug=args.debug)
print()
print("Found ECUs on addresses:")
for addr, subaddr, bus in ecu_addrs:
msg = f" 0x{hex(addr)}"
if subaddr is not None:
msg += f" (sub-address: 0x{hex(subaddr)})"
print(msg)

@ -1,8 +1,8 @@
from cereal import car from cereal import car
from common.numpy_fast import clip, interp from common.numpy_fast import clip, interp
from opendbc.can.packer import CANPacker
from selfdrive.car.ford import fordcan from selfdrive.car.ford import fordcan
from selfdrive.car.ford.values import CarControllerParams from selfdrive.car.ford.values import CarControllerParams
from opendbc.can.packer import CANPacker
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
@ -17,11 +17,12 @@ def apply_ford_steer_angle_limits(apply_steer, apply_steer_last, vEgo):
return apply_steer return apply_steer
class CarController(): class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.VM = VM self.VM = VM
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.frame = 0
self.apply_steer_last = 0 self.apply_steer_last = 0
self.steer_rate_limited = False self.steer_rate_limited = False
@ -29,7 +30,7 @@ class CarController():
self.lkas_enabled_last = False self.lkas_enabled_last = False
self.steer_alert_last = False self.steer_alert_last = False
def update(self, CC, CS, frame): def update(self, CC, CS):
can_sends = [] can_sends = []
actuators = CC.actuators actuators = CC.actuators
@ -48,7 +49,7 @@ class CarController():
self.steer_rate_limited = new_steer != apply_steer self.steer_rate_limited = new_steer != apply_steer
# send steering commands at 20Hz # send steering commands at 20Hz
if (frame % CarControllerParams.LKAS_STEER_STEP) == 0: if (self.frame % CarControllerParams.LKAS_STEER_STEP) == 0:
lca_rq = 1 if CC.latActive else 0 lca_rq = 1 if CC.latActive else 0
# use LatCtlPath_An_Actl to actuate steering for now until curvature control is implemented # use LatCtlPath_An_Actl to actuate steering for now until curvature control is implemented
@ -69,15 +70,14 @@ class CarController():
can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision, can_sends.append(fordcan.create_tja_command(self.packer, lca_rq, ramp_type, precision,
path_offset, path_angle, curvature_rate, curvature)) path_offset, path_angle, curvature_rate, curvature))
send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert) send_ui = (self.main_on_last != main_on) or (self.lkas_enabled_last != CC.latActive) or (self.steer_alert_last != steer_alert)
# send lkas ui command at 1Hz or if ui state changes # send lkas ui command at 1Hz or if ui state changes
if (frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui: if (self.frame % CarControllerParams.LKAS_UI_STEP) == 0 or send_ui:
can_sends.append(fordcan.create_lkas_ui_command(self.packer, main_on, CC.latActive, steer_alert, CS.lkas_status_stock_values)) can_sends.append(fordcan.create_lkas_ui_command(self.packer, main_on, CC.latActive, steer_alert, CS.lkas_status_stock_values))
# send acc ui command at 20Hz or if ui state changes # send acc ui command at 20Hz or if ui state changes
if (frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui: if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
can_sends.append(fordcan.create_acc_ui_command(self.packer, main_on, CC.latActive, CS.acc_tja_status_stock_values)) can_sends.append(fordcan.create_acc_ui_command(self.packer, main_on, CC.latActive, CS.acc_tja_status_stock_values))
self.main_on_last = main_on self.main_on_last = main_on
@ -87,4 +87,5 @@ class CarController():
new_actuators = actuators.copy() new_actuators = actuators.copy()
new_actuators.steeringAngleDeg = apply_steer new_actuators.steeringAngleDeg = apply_steer
self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends

@ -59,7 +59,6 @@ class CarInterface(CarInterfaceBase):
# LCA can steer down to zero # LCA can steer down to zero
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
ret.steerRateCost = 1.0
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase) ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
@ -79,6 +78,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS, self.frame) return self.CC.update(c, self.CS)
self.frame += 1
return ret

@ -9,6 +9,9 @@ RADAR_MSGS = list(range(0x500, 0x540))
def _create_radar_can_parser(car_fingerprint): def _create_radar_can_parser(car_fingerprint):
if DBC[car_fingerprint]['radar'] is None:
return None
msg_n = len(RADAR_MSGS) msg_n = len(RADAR_MSGS)
signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n, signals = list(zip(['X_Rel'] * msg_n + ['Angle'] * msg_n + ['V_Rel'] * msg_n,
RADAR_MSGS * 3)) RADAR_MSGS * 3))
@ -27,6 +30,9 @@ class RadarInterface(RadarInterfaceBase):
self.updated_messages = set() self.updated_messages = set()
def update(self, can_strings): def update(self, can_strings):
if self.rcp is None:
return super().update(None)
vls = self.rcp.update_strings(can_strings) vls = self.rcp.update_strings(can_strings)
self.updated_messages.update(vls) self.updated_messages.update(vls)

@ -79,6 +79,6 @@ FW_VERSIONS = {
DBC = { DBC = {
CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', 'ford_fusion_2018_adas'), CAR.ESCAPE_MK4: dbc_dict('ford_lincoln_base_pt', None),
CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', 'ford_fusion_2018_adas'), CAR.FOCUS_MK4: dbc_dict('ford_lincoln_base_pt', None),
} }

@ -3,18 +3,20 @@ import struct
import traceback import traceback
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, List from typing import Any, List, Optional, Set, Tuple
from tqdm import tqdm from tqdm import tqdm
import panda.python.uds as uds import panda.python.uds as uds
from cereal import car from cereal import car
from selfdrive.car.ecu_addrs import get_ecu_addrs
from selfdrive.car.interfaces import get_interface_attr from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import FW_VERSIONS from selfdrive.car.fingerprints import FW_VERSIONS
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from selfdrive.car.toyota.values import CAR as TOYOTA from selfdrive.car.toyota.values import CAR as TOYOTA
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
def p16(val): def p16(val):
@ -259,7 +261,6 @@ def match_fw_to_car_exact(fw_versions_dict):
ecu_type = ecu[0] ecu_type = ecu[0]
addr = ecu[1:] addr = ecu[1:]
found_version = fw_versions_dict.get(addr, None) found_version = fw_versions_dict.get(addr, None)
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and found_version is None: if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and found_version is None:
continue continue
@ -297,11 +298,46 @@ def match_fw_to_car(fw_versions, allow_fuzzy=True):
return exact_match, matches return exact_match, matches
def get_present_ecus(logcan, sendcan):
queries = list()
parallel_queries = list()
responses = set()
versions = get_interface_attr('FW_VERSIONS', ignore_none=True)
for r in REQUESTS:
if r.brand not in versions:
continue
for brand_versions in versions[r.brand].values():
for ecu_type, addr, sub_addr in brand_versions:
# Only query ecus in whitelist if whitelist is not empty
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
a = (addr, sub_addr, r.bus)
# Build set of queries
if sub_addr is None:
if a not in parallel_queries:
parallel_queries.append(a)
else: # subaddresses must be queried one by one
if [a] not in queries:
queries.append([a])
# Build set of expected responses to filter
response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset)
responses.add((response_addr, sub_addr, r.bus))
queries.insert(0, parallel_queries)
ecu_responses: Set[Tuple[int, Optional[int], int]] = set()
for query in queries:
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1))
return ecu_responses
def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progress=False): def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progress=False):
ecu_types = {} ecu_types = {}
# Extract ECU addresses to query from fingerprints # Extract ECU addresses to query from fingerprints
# ECUs using a subadress need be queried one by one, the rest can be done in parallel # ECUs using a subaddress need be queried one by one, the rest can be done in parallel
addrs = [] addrs = []
parallel_addrs = [] parallel_addrs = []
@ -348,7 +384,7 @@ def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progr
f.ecu = ecu_types[addr] f.ecu = ecu_types[addr]
f.fwVersion = version f.fwVersion = version
f.address = addr[0] f.address = addr[0]
f.responseAddress = addr[0] + rx_offset f.responseAddress = uds.get_rx_addr_for_tx_addr(addr[0], rx_offset)
f.request = request f.request = request
if addr[1] is not None: if addr[1] is not None:

@ -1,17 +1,19 @@
from cereal import car from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.realtime import DT_CTRL
from common.numpy_fast import interp from common.numpy_fast import interp
from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.gm import gmcan from selfdrive.car.gm import gmcan
from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams from selfdrive.car.gm.values import DBC, CanBus, CarControllerParams
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
NetworkLocation = car.CarParams.NetworkLocation
class CarController: class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.start_time = 0. self.start_time = 0.
self.apply_steer_last = 0 self.apply_steer_last = 0
self.apply_gas = 0 self.apply_gas = 0
@ -24,9 +26,9 @@ class CarController:
self.params = CarControllerParams() self.params = CarControllerParams()
self.packer_pt = CANPacker(DBC[CP.carFingerprint]['pt']) self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt'])
self.packer_obj = CANPacker(DBC[CP.carFingerprint]['radar']) self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar'])
self.packer_ch = CANPacker(DBC[CP.carFingerprint]['chassis']) self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis'])
def update(self, CC, CS): def update(self, CC, CS):
actuators = CC.actuators actuators = CC.actuators
@ -60,8 +62,9 @@ class CarController:
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, lkas_enabled))
# Gas/regen and brakes - all at 25Hz if self.CP.openpilotLongitudinalControl:
if (self.frame % 4) == 0: # Gas/regen, brakes, and UI commands - all at 25Hz
if self.frame % 4 == 0:
if not CC.longActive: if not CC.longActive:
# Stock ECU sends max regen when not enabled # Stock ECU sends max regen when not enabled
self.apply_gas = self.params.MAX_ACC_REGEN self.apply_gas = self.params.MAX_ACC_REGEN
@ -78,17 +81,16 @@ class CarController:
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop)) can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, CC.enabled, at_full_stop))
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop)) can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, CanBus.CHASSIS, self.apply_brake, idx, near_stop, at_full_stop))
# Send dashboard UI commands (ACC status), 25hz # Send dashboard UI commands (ACC status)
if (self.frame % 4) == 0:
send_fcw = hud_alert == VisualAlert.fcw send_fcw = hud_alert == VisualAlert.fcw
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled, can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
hud_v_cruise * CV.MS_TO_KPH, hud_control.leadVisible, send_fcw)) hud_v_cruise * CV.MS_TO_KPH, hud_control.leadVisible, send_fcw))
# Radar needs to know current speed and yaw rate (50hz), # Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz) # and that ADAS is alive (10hz)
time_and_headlights_step = 10 if not self.CP.radarOffCan:
tt = self.frame * DT_CTRL tt = self.frame * DT_CTRL
time_and_headlights_step = 10
if self.frame % time_and_headlights_step == 0: if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4 idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx)) can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
@ -100,7 +102,7 @@ class CarController:
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx)) can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx)) can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
if self.frame % self.params.ADAS_KEEPALIVE_STEP == 0: if self.CP.networkLocation == NetworkLocation.gateway and self.frame % self.params.ADAS_KEEPALIVE_STEP == 0:
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN) can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
# Show green icon when LKA torque is applied, and # Show green icon when LKA torque is applied, and
@ -110,7 +112,7 @@ class CarController:
lka_active = CS.lkas_status == 1 lka_active = CS.lkas_status == 1
lka_critical = lka_active and abs(actuators.steer) > 0.9 lka_critical = lka_active and abs(actuators.steer) > 0.9
lka_icon_status = (lka_active, lka_critical) lka_icon_status = (lka_active, lka_critical)
if self.frame % self.params.CAMERA_KEEPALIVE_STEP == 0 or lka_icon_status != self.lka_icon_status_last: 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) 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)) can_sends.append(gmcan.create_lka_icon_command(CanBus.SW_GMLAN, lka_active, lka_critical, steer_alert))
self.lka_icon_status_last = lka_icon_status self.lka_icon_status_last = lka_icon_status

@ -3,7 +3,9 @@ from common.numpy_fast import mean
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.gm.values import DBC, CAR, AccState, CanBus, STEER_THRESHOLD from selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD
TransmissionType = car.CarParams.TransmissionType
class CarState(CarStateBase): class CarState(CarStateBase):
@ -35,7 +37,7 @@ class CarState(CarStateBase):
ret.brakePressed = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] >= 10 ret.brakePressed = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] >= 10
# Regen braking is braking # Regen braking is braking
if self.car_fingerprint == CAR.VOLT: if self.CP.transmissionType == TransmissionType.direct:
ret.brakePressed = ret.brakePressed or pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0 ret.brakePressed = ret.brakePressed or pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254. ret.gas = pt_cp.vl["AcceleratorPedal2"]["AcceleratorPedal2"] / 254.
@ -64,7 +66,7 @@ class CarState(CarStateBase):
ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1 ret.leftBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 1
ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2 ret.rightBlinker = pt_cp.vl["BCMTurnSignals"]["TurnSignals"] == 2
ret.parkingBrake = pt_cp.vl["EPBStatus"]["EPBClosed"] == 1 ret.parkingBrake = pt_cp.vl["VehicleIgnitionAlt"]["ParkBrake"] == 1
ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0 ret.cruiseState.available = pt_cp.vl["ECMEngineStatus"]["CruiseMainOn"] != 0
ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1 ret.espDisabled = pt_cp.vl["ESPStatus"]["TractionControlOn"] != 1
ret.accFaulted = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED ret.accFaulted = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.FAULTED
@ -100,7 +102,7 @@ class CarState(CarStateBase):
("LKATorqueDelivered", "PSCMStatus"), ("LKATorqueDelivered", "PSCMStatus"),
("LKATorqueDeliveredStatus", "PSCMStatus"), ("LKATorqueDeliveredStatus", "PSCMStatus"),
("TractionControlOn", "ESPStatus"), ("TractionControlOn", "ESPStatus"),
("EPBClosed", "EPBStatus"), ("ParkBrake", "VehicleIgnitionAlt"),
("CruiseMainOn", "ECMEngineStatus"), ("CruiseMainOn", "ECMEngineStatus"),
] ]
@ -110,7 +112,7 @@ class CarState(CarStateBase):
("PSCMStatus", 10), ("PSCMStatus", 10),
("ESPStatus", 10), ("ESPStatus", 10),
("BCMDoorBeltStatus", 10), ("BCMDoorBeltStatus", 10),
("EPBStatus", 20), ("VehicleIgnitionAlt", 10),
("EBCMWheelSpdFront", 20), ("EBCMWheelSpdFront", 20),
("EBCMWheelSpdRear", 20), ("EBCMWheelSpdRear", 20),
("AcceleratorPedal2", 33), ("AcceleratorPedal2", 33),
@ -120,7 +122,7 @@ class CarState(CarStateBase):
("EBCMBrakePedalPosition", 100), ("EBCMBrakePedalPosition", 100),
] ]
if CP.carFingerprint == CAR.VOLT: if CP.transmissionType == TransmissionType.direct:
signals.append(("RegenPaddle", "EBCMRegenPaddle")) signals.append(("RegenPaddle", "EBCMRegenPaddle"))
checks.append(("EBCMRegenPaddle", 50)) checks.append(("EBCMRegenPaddle", 50))
@ -133,7 +135,9 @@ class CarState(CarStateBase):
] ]
checks = [ checks = [
("ASCMLKASteeringCmd", 50), ("ASCMLKASteeringCmd", 10), # 10 Hz is the stock inactive rate (every 100ms).
# While active 50 Hz (every 20 ms) is normal
# EPS will tolerate around 200ms when active before faulting
] ]
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.LOOPBACK) return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.LOOPBACK)

@ -9,6 +9,8 @@ from selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
TransmissionType = car.CarParams.TransmissionType
NetworkLocation = car.CarParams.NetworkLocation
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise, BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel} CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel}
@ -45,7 +47,11 @@ class CarInterface(CarInterfaceBase):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint) ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "gm" ret.carName = "gm"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
ret.pcmCruise = False # stock cruise control is kept off 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
# These cars have been put into dashcam only due to both a lack of users and test coverage. # 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 # These cars likely still work fine. Once a user confirms each car works and a test route is
@ -58,24 +64,31 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
# Start with a baseline lateral tuning for all GM vehicles. Override tuning as needed in each model section below. # 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.minSteerSpeed = 7 * CV.MPH_TO_MS
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] 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.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.lateralTuning.pid.kf = 0.00004 # full torque for 20 deg at 80mph means 0.00007818594
ret.steerRateCost = 1.0
ret.steerActuatorDelay = 0.1 # Default delay, not measured yet ret.steerActuatorDelay = 0.1 # Default delay, not measured yet
if candidate == CAR.VOLT: ret.longitudinalTuning.kpBP = [5., 35.]
# supports stop and go, but initial engage must be above 18mph (which include conservatism) ret.longitudinalTuning.kpV = [2.4, 1.5]
ret.longitudinalTuning.kiBP = [0.]
ret.longitudinalTuning.kiV = [0.36]
ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
# supports stop and go, but initial engage must (conservatively) be above 18mph
ret.minEnableSpeed = 18 * CV.MPH_TO_MS ret.minEnableSpeed = 18 * CV.MPH_TO_MS
if candidate == CAR.VOLT:
ret.transmissionType = TransmissionType.direct
ret.mass = 1607. + STD_CARGO_KG ret.mass = 1607. + STD_CARGO_KG
ret.wheelbase = 2.69 ret.wheelbase = 2.69
ret.steerRatio = 17.7 # Stock 15.7, LiveParameters ret.steerRatio = 17.7 # Stock 15.7, LiveParameters
tire_stiffness_factor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters tire_stiffness_factor = 0.469 # Stock Michelin Energy Saver A/S, LiveParameters
ret.steerRatioRear = 0.
ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh ret.centerToFront = ret.wheelbase * 0.45 # Volt Gen 1, TODO corner weigh
ret.maxLateralAccel = 1.6
ret.lateralTuning.pid.kpBP = [0., 40.] ret.lateralTuning.pid.kpBP = [0., 40.]
ret.lateralTuning.pid.kpV = [0., 0.17] ret.lateralTuning.pid.kpV = [0., 0.17]
@ -85,12 +98,9 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
elif candidate == CAR.MALIBU: elif candidate == CAR.MALIBU:
# supports stop and go, but initial engage must be above 18mph (which include conservatism)
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
ret.mass = 1496. + STD_CARGO_KG ret.mass = 1496. + STD_CARGO_KG
ret.wheelbase = 2.83 ret.wheelbase = 2.83
ret.steerRatio = 15.8 ret.steerRatio = 15.8
ret.steerRatioRear = 0.
ret.centerToFront = ret.wheelbase * 0.4 # wild guess ret.centerToFront = ret.wheelbase * 0.4 # wild guess
elif candidate == CAR.HOLDEN_ASTRA: elif candidate == CAR.HOLDEN_ASTRA:
@ -98,34 +108,26 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.662 ret.wheelbase = 2.662
# Remaining parameters copied from Volt for now # Remaining parameters copied from Volt for now
ret.centerToFront = ret.wheelbase * 0.4 ret.centerToFront = ret.wheelbase * 0.4
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
ret.steerRatio = 15.7 ret.steerRatio = 15.7
ret.steerRatioRear = 0.
elif candidate == CAR.ACADIA: elif candidate == CAR.ACADIA:
ret.minEnableSpeed = -1. # engage speed is decided by pcm ret.minEnableSpeed = -1. # engage speed is decided by pcm
ret.mass = 4353. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4353. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.86 ret.wheelbase = 2.86
ret.steerRatio = 14.4 # end to end is 13.46 ret.steerRatio = 14.4 # end to end is 13.46
ret.steerRatioRear = 0.
ret.centerToFront = ret.wheelbase * 0.4 ret.centerToFront = ret.wheelbase * 0.4
ret.maxLateralAccel = 1.4
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia() ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia()
elif candidate == CAR.BUICK_REGAL: elif candidate == CAR.BUICK_REGAL:
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2 ret.mass = 3779. * CV.LB_TO_KG + STD_CARGO_KG # (3849+3708)/2
ret.wheelbase = 2.83 # 111.4 inches in meters ret.wheelbase = 2.83 # 111.4 inches in meters
ret.steerRatio = 14.4 # guess for tourx ret.steerRatio = 14.4 # guess for tourx
ret.steerRatioRear = 0.
ret.centerToFront = ret.wheelbase * 0.4 # guess for tourx ret.centerToFront = ret.wheelbase * 0.4 # guess for tourx
elif candidate == CAR.CADILLAC_ATS: elif candidate == CAR.CADILLAC_ATS:
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
ret.mass = 1601. + STD_CARGO_KG ret.mass = 1601. + STD_CARGO_KG
ret.wheelbase = 2.78 ret.wheelbase = 2.78
ret.steerRatio = 15.3 ret.steerRatio = 15.3
ret.steerRatioRear = 0.
ret.centerToFront = ret.wheelbase * 0.49 ret.centerToFront = ret.wheelbase * 0.49
elif candidate == CAR.ESCALADE_ESV: elif candidate == CAR.ESCALADE_ESV:
@ -148,14 +150,6 @@ class CarInterface(CarInterfaceBase):
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor) tire_stiffness_factor=tire_stiffness_factor)
ret.longitudinalTuning.kpBP = [5., 35.]
ret.longitudinalTuning.kpV = [2.4, 1.5]
ret.longitudinalTuning.kiBP = [0.]
ret.longitudinalTuning.kiV = [0.36]
ret.steerLimitTimer = 0.4
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
return ret return ret
# returns a car.CarState # returns a car.CarState
@ -173,7 +167,7 @@ class CarInterface(CarInterfaceBase):
ret.buttonEvents = [be] ret.buttonEvents = [be]
events = self.create_common_events(ret, pcm_enable=False) events = self.create_common_events(ret, pcm_enable=self.CP.pcmCruise)
if ret.vEgo < self.CP.minEnableSpeed: if ret.vEgo < self.CP.minEnableSpeed:
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
@ -183,12 +177,11 @@ class CarInterface(CarInterfaceBase):
events.add(car.CarEvent.EventName.belowSteerSpeed) events.add(car.CarEvent.EventName.belowSteerSpeed)
# handle button presses # handle button presses
events.events.extend(create_button_enable_events(ret.buttonEvents)) events.events.extend(create_button_enable_events(ret.buttonEvents, pcm_cruise=self.CP.pcmCruise))
ret.events = events.to_msg() ret.events = events.to_msg()
return ret return ret
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS) return self.CC.update(c, self.CS)
return ret

@ -1,4 +1,5 @@
from dataclasses import dataclass from collections import defaultdict
from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Dict, List, Union from typing import Dict, List, Union
@ -9,9 +10,9 @@ Ecu = car.CarParams.Ecu
class CarControllerParams: class CarControllerParams:
STEER_MAX = 300 # Safety limit, not LKA max. Trucks use 600. STEER_MAX = 300 # GM limit is 3Nm. Used by carcontroller to generate LKA output
STEER_STEP = 2 # control frames per command STEER_STEP = 2 # Control frames per command (50hz)
STEER_DELTA_UP = 7 STEER_DELTA_UP = 7 # Delta rates require review due to observed EPS weakness
STEER_DELTA_DOWN = 17 STEER_DELTA_DOWN = 17
MIN_STEER_SPEED = 3. # m/s MIN_STEER_SPEED = 3. # m/s
STEER_DRIVER_ALLOWANCE = 50 STEER_DRIVER_ALLOWANCE = 50
@ -24,9 +25,11 @@ class CarControllerParams:
CAMERA_KEEPALIVE_STEP = 100 CAMERA_KEEPALIVE_STEP = 100
# Volt gasbrake lookups # Volt gasbrake lookups
# TODO: These values should be confirmed on non-Volt vehicles
MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill. MAX_GAS = 3072 # Safety limit, not ACC max. Stock ACC >4096 from standstill.
ZERO_GAS = 2048 # Coasting ZERO_GAS = 2048 # Coasting
MAX_BRAKE = 350 # ~ -3.5 m/s^2 with regen MAX_BRAKE = 350 # ~ -3.5 m/s^2 with regen
MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
# Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we # Allow small margin below -3.5 m/s^2 from ISO 15622:2018 since we
# perform the closed loop control, and might need some # perform the closed loop control, and might need some
@ -36,7 +39,6 @@ class CarControllerParams:
ACCEL_MAX = 2. # m/s^2 ACCEL_MAX = 2. # m/s^2
ACCEL_MIN = -4. # m/s^2 ACCEL_MIN = -4. # m/s^2
MAX_ACC_REGEN = 1404 # Max ACC regen is slightly less than max paddle regen
GAS_LOOKUP_BP = [-1., 0., ACCEL_MAX] GAS_LOOKUP_BP = [-1., 0., ACCEL_MAX]
GAS_LOOKUP_V = [MAX_ACC_REGEN, ZERO_GAS, MAX_GAS] GAS_LOOKUP_V = [MAX_ACC_REGEN, ZERO_GAS, MAX_GAS]
BRAKE_LOOKUP_BP = [ACCEL_MIN, -1.] BRAKE_LOOKUP_BP = [ACCEL_MIN, -1.]
@ -67,16 +69,17 @@ class Footnote(Enum):
class GMCarInfo(CarInfo): class GMCarInfo(CarInfo):
package: str = "Adaptive Cruise" package: str = "Adaptive Cruise"
harness: Enum = Harness.none harness: Enum = Harness.none
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II])
CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = { CAR_INFO: Dict[str, Union[GMCarInfo, List[GMCarInfo]]] = {
CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017", harness=Harness.custom), CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017", harness=Harness.custom),
CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", footnotes=[Footnote.OBD_II], min_enable_speed=0, harness=Harness.custom), CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, harness=Harness.custom),
CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"), CAR.CADILLAC_ATS: GMCarInfo("Cadillac ATS Premium Performance 2018"),
CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017", harness=Harness.custom), CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017", harness=Harness.custom),
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo", footnotes=[Footnote.OBD_II]), CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"),
CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"), CAR.BUICK_REGAL: GMCarInfo("Buick Regal Essence 2018"),
CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "ACC + LKAS", footnotes=[Footnote.OBD_II]), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "ACC + LKAS"),
} }
@ -100,10 +103,12 @@ class CanBus:
CHASSIS = 2 CHASSIS = 2
SW_GMLAN = 3 SW_GMLAN = 3
LOOPBACK = 128 LOOPBACK = 128
DROPPED = 192
FINGERPRINTS = { FINGERPRINTS = {
CAR.HOLDEN_ASTRA: [
# Astra BK MY17, ASCM unplugged # Astra BK MY17, ASCM unplugged
CAR.HOLDEN_ASTRA: [{ {
190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 715: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7, 190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 8, 398: 8, 401: 8, 413: 8, 417: 8, 419: 8, 422: 1, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 458: 5, 479: 8, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 647: 5, 707: 8, 715: 8, 723: 8, 753: 5, 761: 7, 806: 1, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1009: 8, 1011: 6, 1017: 8, 1019: 3, 1020: 8, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 8, 1280: 4, 1300: 8, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1908: 7, 1912: 7, 1919: 7,
}], }],
CAR.VOLT: [ CAR.VOLT: [
@ -145,12 +150,4 @@ FINGERPRINTS = {
}], }],
} }
DBC = { 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'))
CAR.HOLDEN_ASTRA: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.VOLT: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.MALIBU: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.ACADIA: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.CADILLAC_ATS: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.BUICK_REGAL: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
CAR.ESCALADE_ESV: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'),
}

@ -7,7 +7,7 @@ from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import create_gas_interceptor_command from selfdrive.car import create_gas_interceptor_command
from selfdrive.car.honda import hondacan from selfdrive.car.honda import hondacan
from selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams from selfdrive.car.honda.values import CruiseButtons, VISUAL_HUD, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from selfdrive.controls.lib.drive_helpers import rate_limit from selfdrive.controls.lib.drive_helpers import rate_limit
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
@ -95,8 +95,8 @@ def process_hud_alert(hud_alert):
HUDData = namedtuple("HUDData", HUDData = namedtuple("HUDData",
["pcm_accel", "v_cruise", "car", ["pcm_accel", "v_cruise", "lead_visible",
"lanes", "fcw", "acc_alert", "steer_required"]) "lanes_visible", "fcw", "acc_alert", "steer_required"])
class CarController: class CarController:
@ -138,19 +138,6 @@ class CarController:
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL) self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL)
# vehicle hud display, wait for one update from 10Hz 0x304 msg # vehicle hud display, wait for one update from 10Hz 0x304 msg
if hud_control.lanesVisible:
hud_lanes = 1
else:
hud_lanes = 0
if CC.enabled:
if hud_control.leadVisible:
hud_car = 2
else:
hud_car = 1
else:
hud_car = 0
fcw_display, steer_required, acc_alert = process_hud_alert(hud_control.visualAlert) fcw_display, steer_required, acc_alert = process_hud_alert(hud_control.visualAlert)
# **** process the car messages **** # **** process the car messages ****
@ -172,8 +159,6 @@ class CarController:
can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint, can_sends.append(hondacan.create_steering_control(self.packer, apply_steer, CC.latActive, self.CP.carFingerprint,
idx, CS.CP.openpilotLongitudinalControl)) idx, CS.CP.openpilotLongitudinalControl))
stopping = actuators.longControlState == LongCtrlState.stopping
# wind brake from air resistance decel at high speed # wind brake from air resistance decel at high speed
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]) wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
# all of this is only relevant for HONDA NIDEC # all of this is only relevant for HONDA NIDEC
@ -204,7 +189,7 @@ class CarController:
pcm_accel = int(clip((accel / 1.44) / max_accel, 0.0, 1.0) * 0xc6) pcm_accel = int(clip((accel / 1.44) / max_accel, 0.0, 1.0) * 0xc6)
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
if self.frame % 2 == 0: if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message
idx = self.frame // 2 idx = self.frame // 2
can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CP.carFingerprint, idx)) can_sends.append(hondacan.create_bosch_supplemental_1(self.packer, self.CP.carFingerprint, idx))
# If using stock ACC, spam cancel command to kill gas when OP disengages. # If using stock ACC, spam cancel command to kill gas when OP disengages.
@ -222,6 +207,8 @@ class CarController:
if self.CP.carFingerprint in HONDA_BOSCH: if self.CP.carFingerprint in HONDA_BOSCH:
self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX) self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX)
self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V) self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V)
stopping = actuators.longControlState == LongCtrlState.stopping
can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas, can_sends.extend(hondacan.create_acc_commands(self.packer, CC.enabled, CC.longActive, self.accel, self.gas,
idx, stopping, self.CP.carFingerprint)) idx, stopping, self.CP.carFingerprint))
else: else:
@ -252,9 +239,9 @@ class CarController:
# Send dashboard UI commands. # Send dashboard UI commands.
if self.frame % 10 == 0: if self.frame % 10 == 0:
idx = (self.frame // 10) % 4 idx = (self.frame // 10) % 4
hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_car, hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible,
hud_lanes, fcw_display, acc_alert, steer_required) hud_control.lanesVisible, fcw_display, acc_alert, steer_required)
can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud)) can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud))
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH: if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH:
self.speed = pcm_speed self.speed = pcm_speed

@ -5,8 +5,9 @@ from common.conversions import Conversions as CV
from common.numpy_fast import interp from common.numpy_fast import interp
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.car.honda.hondacan import get_pt_bus
from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL, HONDA_BOSCH_RADARLESS
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType
@ -22,9 +23,9 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
("STEER_ANGLE_RATE", "STEERING_SENSORS"), ("STEER_ANGLE_RATE", "STEERING_SENSORS"),
("MOTOR_TORQUE", "STEER_MOTOR_TORQUE"), ("MOTOR_TORQUE", "STEER_MOTOR_TORQUE"),
("STEER_TORQUE_SENSOR", "STEER_STATUS"), ("STEER_TORQUE_SENSOR", "STEER_STATUS"),
("IMPERIAL_UNIT", "CAR_SPEED"),
("LEFT_BLINKER", "SCM_FEEDBACK"), ("LEFT_BLINKER", "SCM_FEEDBACK"),
("RIGHT_BLINKER", "SCM_FEEDBACK"), ("RIGHT_BLINKER", "SCM_FEEDBACK"),
("GEAR", gearbox_msg),
("SEATBELT_DRIVER_LAMP", "SEATBELT_STATUS"), ("SEATBELT_DRIVER_LAMP", "SEATBELT_STATUS"),
("SEATBELT_DRIVER_LATCHED", "SEATBELT_STATUS"), ("SEATBELT_DRIVER_LATCHED", "SEATBELT_STATUS"),
("BRAKE_PRESSED", "POWERTRAIN_DATA"), ("BRAKE_PRESSED", "POWERTRAIN_DATA"),
@ -35,6 +36,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
("BRAKE_HOLD_ACTIVE", "VSA_STATUS"), ("BRAKE_HOLD_ACTIVE", "VSA_STATUS"),
("STEER_STATUS", "STEER_STATUS"), ("STEER_STATUS", "STEER_STATUS"),
("GEAR_SHIFTER", gearbox_msg), ("GEAR_SHIFTER", gearbox_msg),
("GEAR", gearbox_msg),
("PEDAL_GAS", "POWERTRAIN_DATA"), ("PEDAL_GAS", "POWERTRAIN_DATA"),
("CRUISE_SETTING", "SCM_BUTTONS"), ("CRUISE_SETTING", "SCM_BUTTONS"),
("ACC_STATUS", "POWERTRAIN_DATA"), ("ACC_STATUS", "POWERTRAIN_DATA"),
@ -48,6 +50,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
("SEATBELT_STATUS", 10), ("SEATBELT_STATUS", 10),
("CRUISE", 10), ("CRUISE", 10),
("POWERTRAIN_DATA", 100), ("POWERTRAIN_DATA", 100),
("CAR_SPEED", 10),
("VSA_STATUS", 50), ("VSA_STATUS", 50),
("STEER_STATUS", 100), ("STEER_STATUS", 100),
("STEER_MOTOR_TORQUE", 0), # TODO: not on every car ("STEER_MOTOR_TORQUE", 0), # TODO: not on every car
@ -73,17 +76,13 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
signals.append(("BRAKE_PRESSED", "BRAKE_MODULE")) signals.append(("BRAKE_PRESSED", "BRAKE_MODULE"))
checks.append(("BRAKE_MODULE", 50)) checks.append(("BRAKE_MODULE", 50))
if CP.carFingerprint in HONDA_BOSCH: if CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}):
signals += [ signals.append(("EPB_STATE", "EPB_STATUS"))
("EPB_STATE", "EPB_STATUS"), checks.append(("EPB_STATUS", 50))
("IMPERIAL_UNIT", "CAR_SPEED"),
]
checks += [
("EPB_STATUS", 50),
("CAR_SPEED", 10),
]
if not CP.openpilotLongitudinalControl: if CP.carFingerprint in HONDA_BOSCH:
# these messages are on camera bus on radarless cars
if not CP.openpilotLongitudinalControl and CP.carFingerprint not in HONDA_BOSCH_RADARLESS:
signals += [ signals += [
("CRUISE_CONTROL_LABEL", "ACC_HUD"), ("CRUISE_CONTROL_LABEL", "ACC_HUD"),
("CRUISE_SPEED", "ACC_HUD"), ("CRUISE_SPEED", "ACC_HUD"),
@ -103,34 +102,16 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
else: else:
checks.append(("CRUISE_PARAMS", 50)) checks.append(("CRUISE_PARAMS", 50))
if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022):
signals.append(("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK")) signals.append(("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK"))
elif CP.carFingerprint == CAR.ODYSSEY_CHN: elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
signals.append(("DRIVERS_DOOR_OPEN", "SCM_BUTTONS")) signals.append(("DRIVERS_DOOR_OPEN", "SCM_BUTTONS"))
elif CP.carFingerprint in (CAR.FREED, CAR.HRV):
signals += [("DRIVERS_DOOR_OPEN", "SCM_BUTTONS"),
("WHEELS_MOVING", "STANDSTILL")]
else: else:
signals += [("DOOR_OPEN_FL", "DOORS_STATUS"), signals += [("DOOR_OPEN_FL", "DOORS_STATUS"),
("DOOR_OPEN_FR", "DOORS_STATUS"), ("DOOR_OPEN_FR", "DOORS_STATUS"),
("DOOR_OPEN_RL", "DOORS_STATUS"), ("DOOR_OPEN_RL", "DOORS_STATUS"),
("DOOR_OPEN_RR", "DOORS_STATUS"), ("DOOR_OPEN_RR", "DOORS_STATUS")]
("WHEELS_MOVING", "STANDSTILL")] checks.append(("DOORS_STATUS", 3))
checks += [
("DOORS_STATUS", 3),
("STANDSTILL", 50),
]
if CP.carFingerprint == CAR.CIVIC:
signals += [("IMPERIAL_UNIT", "HUD_SETTING"),
("EPB_STATE", "EPB_STATUS")]
checks += [
("HUD_SETTING", 50),
("EPB_STATUS", 50),
]
elif CP.carFingerprint in (CAR.ODYSSEY, CAR.ODYSSEY_CHN):
signals.append(("EPB_STATE", "EPB_STATUS"))
checks.append(("EPB_STATUS", 50))
# add gas interceptor reading if we are using it # add gas interceptor reading if we are using it
if CP.enableGasInterceptor: if CP.enableGasInterceptor:
@ -179,12 +160,18 @@ class CarState(CarStateBase):
# update prevs, update must run once per loop # update prevs, update must run once per loop
self.prev_cruise_buttons = self.cruise_buttons self.prev_cruise_buttons = self.cruise_buttons
self.prev_cruise_setting = self.cruise_setting self.prev_cruise_setting = self.cruise_setting
self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"]
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
# used for car hud message
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
# ******************* parse out can ******************* # ******************* parse out can *******************
# STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED # STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED
# panda checks if the signal is non-zero # panda checks if the signal is non-zero
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5
if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): # TODO: find a common signal across all cars
if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022):
ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"])
elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV):
ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"]) ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"])
@ -219,16 +206,12 @@ class CarState(CarStateBase):
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"] ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"] ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"]
self.cruise_setting = cp.vl["SCM_BUTTONS"]["CRUISE_SETTING"]
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk( ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(
250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"]) 250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"])
ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1 ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1
# TODO: set for all cars # TODO: set for all cars
if self.CP.carFingerprint in (CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN, CAR.CRV_5G, CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, if self.CP.carFingerprint in (HONDA_BOSCH | {CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN}):
CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E):
ret.parkingBrake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0 ret.parkingBrake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0
gear = int(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"]) gear = int(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"])
@ -248,11 +231,15 @@ class CarState(CarStateBase):
if self.CP.carFingerprint in HONDA_BOSCH: if self.CP.carFingerprint in HONDA_BOSCH:
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
ret.cruiseState.nonAdaptive = cp.vl["ACC_HUD"]["CRUISE_CONTROL_LABEL"] != 0 # ACC_HUD is on camera bus on radarless cars
ret.cruiseState.standstill = cp.vl["ACC_HUD"]["CRUISE_SPEED"] == 252. acc_hud = cp_cam.vl["ACC_HUD"] if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS else cp.vl["ACC_HUD"]
ret.cruiseState.nonAdaptive = acc_hud["CRUISE_CONTROL_LABEL"] != 0
ret.cruiseState.standstill = acc_hud["CRUISE_SPEED"] == 252.
# on certain cars, CRUISE_SPEED changes to imperial with car's unit setting
conversion_factor = CV.MPH_TO_MS if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS and not self.is_metric else CV.KPH_TO_MS
# On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this. # On set, cruise set speed pulses between 254~255 and the set speed prev is set to avoid this.
ret.cruiseState.speed = self.v_cruise_pcm_prev if cp.vl["ACC_HUD"]["CRUISE_SPEED"] > 160.0 else cp.vl["ACC_HUD"]["CRUISE_SPEED"] * CV.KPH_TO_MS ret.cruiseState.speed = self.v_cruise_pcm_prev if acc_hud["CRUISE_SPEED"] > 160.0 else acc_hud["CRUISE_SPEED"] * conversion_factor
self.v_cruise_pcm_prev = ret.cruiseState.speed self.v_cruise_pcm_prev = ret.cruiseState.speed
else: else:
ret.cruiseState.speed = cp.vl["CRUISE"]["CRUISE_SPEED_PCM"] * CV.KPH_TO_MS ret.cruiseState.speed = cp.vl["CRUISE"]["CRUISE_SPEED_PCM"] * CV.KPH_TO_MS
@ -281,23 +268,15 @@ class CarState(CarStateBase):
if ret.brake > 0.1: if ret.brake > 0.1:
ret.brakePressed = True ret.brakePressed = True
# TODO: discover the CAN msg that has the imperial unit bit for all other cars
if self.CP.carFingerprint in (CAR.CIVIC, ):
self.is_metric = not cp.vl["HUD_SETTING"]["IMPERIAL_UNIT"]
elif self.CP.carFingerprint in HONDA_BOSCH:
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
else:
self.is_metric = False
if self.CP.carFingerprint in HONDA_BOSCH: if self.CP.carFingerprint in HONDA_BOSCH:
# TODO: find the radarless AEB_STATUS bit and make sure ACCEL_COMMAND is correct to enable AEB alerts
if self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS:
ret.stockAeb = (not self.CP.openpilotLongitudinalControl) and bool(cp.vl["ACC_CONTROL"]["AEB_STATUS"] and cp.vl["ACC_CONTROL"]["ACCEL_COMMAND"] < -1e-5) ret.stockAeb = (not self.CP.openpilotLongitudinalControl) and bool(cp.vl["ACC_CONTROL"]["AEB_STATUS"] and cp.vl["ACC_CONTROL"]["ACCEL_COMMAND"] < -1e-5)
else: else:
ret.stockAeb = bool(cp_cam.vl["BRAKE_COMMAND"]["AEB_REQ_1"] and cp_cam.vl["BRAKE_COMMAND"]["COMPUTER_BRAKE"] > 1e-5) ret.stockAeb = bool(cp_cam.vl["BRAKE_COMMAND"]["AEB_REQ_1"] and cp_cam.vl["BRAKE_COMMAND"]["COMPUTER_BRAKE"] > 1e-5)
if self.CP.carFingerprint in HONDA_BOSCH:
self.stock_hud = False self.stock_hud = False
ret.stockFcw = False if self.CP.carFingerprint not in HONDA_BOSCH:
else:
ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0 ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0
self.stock_hud = cp_cam.vl["ACC_HUD"] self.stock_hud = cp_cam.vl["ACC_HUD"]
self.stock_brake = cp_cam.vl["BRAKE_COMMAND"] self.stock_brake = cp_cam.vl["BRAKE_COMMAND"]
@ -312,8 +291,7 @@ class CarState(CarStateBase):
def get_can_parser(self, CP): def get_can_parser(self, CP):
signals, checks = get_can_signals(CP, self.gearbox_msg, self.main_on_sig_msg) signals, checks = get_can_signals(CP, self.gearbox_msg, self.main_on_sig_msg)
bus_pt = 1 if CP.carFingerprint in HONDA_BOSCH else 0 return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, get_pt_bus(CP.carFingerprint))
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, bus_pt)
@staticmethod @staticmethod
def get_cam_can_parser(CP): def get_cam_can_parser(CP):
@ -322,7 +300,14 @@ class CarState(CarStateBase):
("STEERING_CONTROL", 100), ("STEERING_CONTROL", 100),
] ]
if CP.carFingerprint not in HONDA_BOSCH: if CP.carFingerprint in HONDA_BOSCH_RADARLESS and not CP.openpilotLongitudinalControl:
signals += [
("CRUISE_SPEED", "ACC_HUD"),
("CRUISE_CONTROL_LABEL", "ACC_HUD"),
]
checks.append(("ACC_HUD", 10))
elif CP.carFingerprint not in HONDA_BOSCH:
signals += [("COMPUTER_BRAKE", "BRAKE_COMMAND"), signals += [("COMPUTER_BRAKE", "BRAKE_COMMAND"),
("AEB_REQ_1", "BRAKE_COMMAND"), ("AEB_REQ_1", "BRAKE_COMMAND"),
("FCW", "BRAKE_COMMAND"), ("FCW", "BRAKE_COMMAND"),

@ -1,5 +1,5 @@
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, CAR, CarControllerParams from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, HONDA_BOSCH_RADARLESS, CAR, CarControllerParams
# CAN bus layout with relay # CAN bus layout with relay
# 0 = ACC-CAN - radar side # 0 = ACC-CAN - radar side
@ -7,8 +7,9 @@ from selfdrive.car.honda.values import HondaFlags, HONDA_BOSCH, CAR, CarControll
# 2 = ACC-CAN - camera side # 2 = ACC-CAN - camera side
# 3 = F-CAN A - OBDII port # 3 = F-CAN A - OBDII port
def get_pt_bus(car_fingerprint): def get_pt_bus(car_fingerprint):
return 1 if car_fingerprint in HONDA_BOSCH else 0 return 1 if car_fingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) else 0
def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False): def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False):
@ -18,6 +19,7 @@ def get_lkas_cmd_bus(car_fingerprint, radar_disabled=False):
# normally steering commands are sent to radar, which forwards them to powertrain bus # normally steering commands are sent to radar, which forwards them to powertrain bus
return 0 return 0
def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, stock_brake): def create_brake_command(packer, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, fcw, idx, car_fingerprint, stock_brake):
# TODO: do we loose pressure if we keep pump off for long? # TODO: do we loose pressure if we keep pump off for long?
brakelights = apply_brake > 0 brakelights = apply_brake > 0
@ -78,6 +80,7 @@ def create_acc_commands(packer, enabled, active, accel, gas, idx, stopping, car_
return commands return commands
def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, radar_disabled): def create_steering_control(packer, apply_steer, lkas_active, car_fingerprint, idx, radar_disabled):
values = { values = {
"STEER_TORQUE": apply_steer if lkas_active else 0, "STEER_TORQUE": apply_steer if lkas_active else 0,
@ -98,50 +101,47 @@ def create_bosch_supplemental_1(packer, car_fingerprint, idx):
return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values, idx) return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values, idx)
def create_ui_commands(packer, CP, pcm_speed, hud, is_metric, idx, stock_hud): def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, idx, stock_hud):
commands = [] commands = []
bus_pt = get_pt_bus(CP.carFingerprint) bus_pt = get_pt_bus(CP.carFingerprint)
radar_disabled = CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl radar_disabled = CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl
bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled) bus_lkas = get_lkas_cmd_bus(CP.carFingerprint, radar_disabled)
if CP.openpilotLongitudinalControl: if CP.openpilotLongitudinalControl:
if CP.carFingerprint in HONDA_BOSCH:
acc_hud_values = { acc_hud_values = {
'CRUISE_SPEED': hud.v_cruise, 'CRUISE_SPEED': hud.v_cruise,
'ENABLE_MINI_CAR': 1, 'ENABLE_MINI_CAR': 1,
'SET_TO_1': 1, 'HUD_DISTANCE': 0, # max distance setting on display
'HUD_LEAD': hud.car,
'HUD_DISTANCE': 3,
'ACC_ON': hud.car != 0,
'SET_TO_X1': 1,
'IMPERIAL_UNIT': int(not is_metric),
'FCM_OFF': 1,
}
else:
acc_hud_values = {
'PCM_SPEED': pcm_speed * CV.MS_TO_KPH,
'PCM_GAS': hud.pcm_accel,
'CRUISE_SPEED': hud.v_cruise,
'ENABLE_MINI_CAR': 1,
'HUD_LEAD': hud.car,
'HUD_DISTANCE': 3, # max distance setting on display
'IMPERIAL_UNIT': int(not is_metric), 'IMPERIAL_UNIT': int(not is_metric),
'HUD_LEAD': 2 if enabled and hud.lead_visible else 1 if enabled else 0,
'SET_ME_X01_2': 1, 'SET_ME_X01_2': 1,
'SET_ME_X01': 1,
"FCM_OFF": stock_hud["FCM_OFF"],
"FCM_OFF_2": stock_hud["FCM_OFF_2"],
"FCM_PROBLEM": stock_hud["FCM_PROBLEM"],
"ICONS": stock_hud["ICONS"],
} }
if CP.carFingerprint in HONDA_BOSCH:
acc_hud_values['ACC_ON'] = int(enabled)
acc_hud_values['FCM_OFF'] = 1
acc_hud_values['FCM_OFF_2'] = 1
else:
acc_hud_values['PCM_SPEED'] = pcm_speed * CV.MS_TO_KPH
acc_hud_values['PCM_GAS'] = hud.pcm_accel
acc_hud_values['SET_ME_X01'] = 1
acc_hud_values['FCM_OFF'] = stock_hud['FCM_OFF']
acc_hud_values['FCM_OFF_2'] = stock_hud['FCM_OFF_2']
acc_hud_values['FCM_PROBLEM'] = stock_hud['FCM_PROBLEM']
acc_hud_values['ICONS'] = stock_hud['ICONS']
commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values, idx)) commands.append(packer.make_can_msg("ACC_HUD", bus_pt, acc_hud_values, idx))
lkas_hud_values = { lkas_hud_values = {
'SET_ME_X41': 0x41, 'SET_ME_X41': 0x41,
'STEERING_REQUIRED': hud.steer_required, 'STEERING_REQUIRED': hud.steer_required,
'SOLID_LANES': hud.lanes, 'SOLID_LANES': hud.lanes_visible,
'BEEP': 0, 'BEEP': 0,
} }
if CP.carFingerprint in HONDA_BOSCH_RADARLESS:
lkas_hud_values['LANE_LINES'] = 3
lkas_hud_values['DASHED_LANES'] = hud.lanes_visible
if not (CP.flags & HondaFlags.BOSCH_EXT_HUD): if not (CP.flags & HondaFlags.BOSCH_EXT_HUD):
lkas_hud_values['SET_ME_X48'] = 0x48 lkas_hud_values['SET_ME_X48'] = 0x48
@ -169,5 +169,6 @@ def spam_buttons_command(packer, button_val, idx, car_fingerprint):
'CRUISE_BUTTONS': button_val, 'CRUISE_BUTTONS': button_val,
'CRUISE_SETTING': 0, 'CRUISE_SETTING': 0,
} }
bus = get_pt_bus(car_fingerprint) # send buttons to camera on radarless cars
bus = 2 if car_fingerprint in HONDA_BOSCH_RADARLESS else get_pt_bus(car_fingerprint)
return packer.make_can_msg("SCM_BUTTONS", bus, values, idx) return packer.make_can_msg("SCM_BUTTONS", bus, values, idx)

@ -3,7 +3,7 @@ from cereal import car
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.numpy_fast import interp 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 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_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
@ -37,6 +37,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.hondaBosch)]
ret.radarOffCan = True ret.radarOffCan = True
if candidate not in HONDA_BOSCH_RADARLESS:
# Disable the radar and let openpilot control longitudinal # Disable the radar and let openpilot control longitudinal
# WARNING: THIS DISABLES AEB! # WARNING: THIS DISABLES AEB!
ret.openpilotLongitudinalControl = disable_radar ret.openpilotLongitudinalControl = disable_radar
@ -100,12 +101,11 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 8000], [0, 2560, 3840]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 8000], [0, 2560, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.1]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.1]]
else: else:
ret.maxLateralAccel = 1.7
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
tire_stiffness_factor = 1. tire_stiffness_factor = 1.
elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL): elif candidate in (CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CIVIC_2022):
stop_and_go = True stop_and_go = True
ret.mass = CivicParams.MASS ret.mass = CivicParams.MASS
ret.wheelbase = CivicParams.WHEELBASE ret.wheelbase = CivicParams.WHEELBASE
@ -113,7 +113,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.38 # 10.93 is end-to-end spec ret.steerRatio = 15.38 # 10.93 is end-to-end spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 1. tire_stiffness_factor = 1.
ret.maxLateralAccel = 1.6
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.ACCORD, CAR.ACCORDH): elif candidate in (CAR.ACCORD, CAR.ACCORDH):
@ -128,7 +127,6 @@ class CarInterface(CarInterfaceBase):
if eps_modified: if eps_modified:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
else: else:
ret.maxLateralAccel = 1.6
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.ACURA_ILX: elif candidate == CAR.ACURA_ILX:
@ -165,7 +163,6 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 10000], [0, 2560, 3840]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 10000], [0, 2560, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.21], [0.07]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.21], [0.07]]
else: else:
ret.maxLateralAccel = 1.7
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
tire_stiffness_factor = 0.677 tire_stiffness_factor = 0.677
@ -190,7 +187,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 13.06 ret.steerRatio = 13.06
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.75 tire_stiffness_factor = 0.75
ret.maxLateralAccel = 1.7
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
elif candidate == CAR.FREED: elif candidate == CAR.FREED:
@ -222,7 +218,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.38 ret.centerToFront = ret.wheelbase * 0.38
ret.steerRatio = 15.0 # as spec ret.steerRatio = 15.0 # as spec
tire_stiffness_factor = 0.444 tire_stiffness_factor = 0.444
ret.maxLateralAccel = 0.9
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
@ -232,7 +227,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.75 ret.wheelbase = 2.75
ret.centerToFront = ret.wheelbase * 0.41 ret.centerToFront = ret.wheelbase * 0.41
ret.steerRatio = 11.95 # as spec ret.steerRatio = 11.95 # as spec
ret.maxLateralAccel = 1.2
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
tire_stiffness_factor = 0.677 tire_stiffness_factor = 0.677
@ -245,7 +239,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.35 # as spec ret.steerRatio = 14.35 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.82 tire_stiffness_factor = 0.82
ret.maxLateralAccel = 1.5
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
elif candidate == CAR.ODYSSEY_CHN: elif candidate == CAR.ODYSSEY_CHN:
@ -266,7 +259,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 17.25 # as spec ret.steerRatio = 17.25 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.444 tire_stiffness_factor = 0.444
ret.maxLateralAccel = 1.5
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.RIDGELINE: elif candidate == CAR.RIDGELINE:
@ -277,7 +269,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.59 # as spec ret.steerRatio = 15.59 # as spec
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.444 tire_stiffness_factor = 0.444
ret.maxLateralAccel = 1.3
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
elif candidate == CAR.INSIGHT: elif candidate == CAR.INSIGHT:
@ -288,7 +279,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.0 # 12.58 is spec end-to-end ret.steerRatio = 15.0 # 12.58 is spec end-to-end
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.82 tire_stiffness_factor = 0.82
ret.maxLateralAccel = 1.4
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.HONDA_E: elif candidate == CAR.HONDA_E:
@ -315,6 +305,9 @@ class CarInterface(CarInterfaceBase):
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH: if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_BOSCH_LONG ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_BOSCH_LONG
if candidate in HONDA_BOSCH_RADARLESS:
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HONDA_RADARLESS
# min speed to enable ACC. if car can do stop and go, then set enabling speed # 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 # to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc # conflict with PCM acc
@ -330,14 +323,13 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor=tire_stiffness_factor) tire_stiffness_factor=tire_stiffness_factor)
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRateCost = 0.5
ret.steerLimitTimer = 0.8 ret.steerLimitTimer = 0.8
return ret return ret
@staticmethod @staticmethod
def init(CP, logcan, sendcan): def init(CP, logcan, sendcan):
if CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl: if CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and CP.openpilotLongitudinalControl:
disable_ecu(logcan, sendcan, bus=1, addr=0x18DAB0F1, com_cont_req=b'\x28\x83\x03') disable_ecu(logcan, sendcan, bus=1, addr=0x18DAB0F1, com_cont_req=b'\x28\x83\x03')
# returns a car.CarState # returns a car.CarState

@ -75,6 +75,7 @@ class CAR:
CIVIC = "HONDA CIVIC 2016" CIVIC = "HONDA CIVIC 2016"
CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019"
CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019"
CIVIC_2022 = "HONDA CIVIC 2022"
ACURA_ILX = "ACURA ILX 2016" ACURA_ILX = "ACURA ILX 2016"
CRV = "HONDA CR-V 2016" CRV = "HONDA CR-V 2016"
CRV_5G = "HONDA CR-V 2017" CRV_5G = "HONDA CR-V 2017"
@ -108,33 +109,37 @@ class HondaCarInfo(CarInfo):
CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = {
CAR.ACCORD: [ CAR.ACCORD: [
HondaCarInfo("Honda Accord 2018-21", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), HondaCarInfo("Honda Accord 2018-21", "All", video_link="https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), HondaCarInfo("Honda Inspire 2018", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
], ],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-21", "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),
CAR.CIVIC_BOSCH: [ CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-20", "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), HondaCarInfo("Honda Civic 2019-20", "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), HondaCarInfo("Honda Civic Hatchback 2017-21", harness=Harness.bosch_a),
], ],
CAR.CIVIC_BOSCH_DIESEL: None, # same platform CAR.CIVIC_BOSCH_DIESEL: None, # same platform
CAR.CIVIC_2022: [
HondaCarInfo("Honda Civic 2022", "All", min_steer_speed=0., harness=Harness.bosch_b),
HondaCarInfo("Honda Civic Hatchback 2022", "All", min_steer_speed=0., harness=Harness.bosch_b),
],
CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.ACURA_ILX: HondaCarInfo("Acura ILX 2016-19", "AcuraWatch Plus", min_steer_speed=25. * CV.MPH_TO_MS, harness=Harness.nidec),
CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring", harness=Harness.nidec), CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring", harness=Harness.nidec),
CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-21", harness=Harness.bosch), CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-21", harness=Harness.bosch_a),
CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring CAR.CRV_EU: None, # HondaCarInfo("Honda CR-V EU", "Touring"), # Euro version of CRV Touring
CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", harness=Harness.bosch), CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", harness=Harness.bosch_a),
CAR.FIT: HondaCarInfo("Honda Fit 2018-19", harness=Harness.nidec), CAR.FIT: HondaCarInfo("Honda Fit 2018-19", harness=Harness.nidec),
CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec), CAR.FREED: HondaCarInfo("Honda Freed 2020", harness=Harness.nidec),
CAR.HRV: HondaCarInfo("Honda HR-V 2019-20", harness=Harness.nidec), CAR.HRV: HondaCarInfo("Honda HR-V 2019-20", harness=Harness.nidec),
CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", 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.ODYSSEY_CHN: None, # Chinese version of Odyssey
CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec), CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", harness=Harness.nidec),
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.PILOT: HondaCarInfo("Honda Pilot 2016-21", harness=Harness.nidec), CAR.PILOT: HondaCarInfo("Honda Pilot 2016-21", harness=Harness.nidec),
CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", harness=Harness.nidec), CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", harness=Harness.nidec),
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", harness=Harness.nidec), CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", harness=Harness.nidec),
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-21", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch), CAR.HONDA_E: HondaCarInfo("Honda e 2020", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a),
} }
@ -1392,6 +1397,40 @@ FW_VERSIONS = {
b'57114-TYF-E030\x00\x00' b'57114-TYF-E030\x00\x00'
], ],
}, },
CAR.CIVIC_2022: {
(Ecu.eps, 0x18DA30F1, None): [
b'39990-T39-A130\x00\x00',
b'39990-T43-J020\x00\x00',
],
(Ecu.gateway, 0x18DAEFF1, None): [
b'38897-T20-A020\x00\x00',
b'38897-T20-A510\x00\x00',
b'38897-T21-A010\x00\x00',
],
(Ecu.srs, 0x18DA53F1, None): [
b'77959-T20-A970\x00\x00',
b'77959-T47-A940\x00\x00',
],
(Ecu.combinationMeter, 0x18DA60F1, None): [
b'78108-T21-A220\x00\x00',
b'78108-T21-A620\x00\x00',
b'78108-T23-A110\x00\x00',
],
(Ecu.vsa, 0x18DA28F1, None): [
b'57114-T20-AB40\x00\x00',
b'57114-T43-JB30\x00\x00',
],
(Ecu.transmission, 0x18da1ef1, None): [
b'28101-65D-A020\x00\x00',
b'28101-65D-A120\x00\x00',
b'28101-65H-A020\x00\x00',
],
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
b'37805-64L-A540\x00\x00',
b'37805-64S-A540\x00\x00',
b'37805-64S-A720\x00\x00',
],
},
} }
DBC = { DBC = {
@ -1417,6 +1456,7 @@ DBC = {
CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.RIDGELINE: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None), CAR.INSIGHT: dbc_dict('honda_insight_ex_2019_can_generated', None),
CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None), CAR.HONDA_E: dbc_dict('acura_rdx_2020_can_generated', None),
CAR.CIVIC_2022: dbc_dict('honda_civic_ex_2022_can_generated', None),
} }
STEER_THRESHOLD = { STEER_THRESHOLD = {
@ -1429,5 +1469,6 @@ HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY}
HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE} CAR.PILOT, CAR.PASSPORT, CAR.RIDGELINE}
HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E} CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022}
HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G} HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G}
HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022}

@ -1,11 +1,11 @@
from cereal import car from cereal import car
from common.realtime import DT_CTRL
from common.numpy_fast import clip, interp
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.numpy_fast import clip, interp
from common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.hyundai import hda2can, hyundaican from selfdrive.car.hyundai import hda2can, hyundaican
from selfdrive.car.hyundai.values import Buttons, CarControllerParams, HDA2_CAR, CAR from selfdrive.car.hyundai.values import Buttons, CarControllerParams, HDA2_CAR, CAR
from opendbc.can.packer import CANPacker
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState

@ -5,7 +5,7 @@ from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine from opendbc.can.can_define import CANDefine
from selfdrive.car.hyundai.values import DBC, STEER_THRESHOLD, FEATURES, HDA2_CAR, EV_CAR, HYBRID_CAR, Buttons from selfdrive.car.hyundai.values import DBC, FEATURES, HDA2_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
PREV_BUTTON_SAMPLES = 4 PREV_BUTTON_SAMPLES = 4
@ -32,6 +32,8 @@ class CarState(CarStateBase):
self.park_brake = False self.park_brake = False
self.buttons_counter = 0 self.buttons_counter = 0
self.params = CarControllerParams(CP)
def update(self, cp, cp_cam): def update(self, cp, cp_cam):
if self.CP.carFingerprint in HDA2_CAR: if self.CP.carFingerprint in HDA2_CAR:
return self.update_hda2(cp, cp_cam) return self.update_hda2(cp, cp_cam)
@ -61,7 +63,7 @@ class CarState(CarStateBase):
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"]) 50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"] ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"] ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0 ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
# cruise state # cruise state
@ -157,7 +159,7 @@ class CarState(CarStateBase):
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1 ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"] ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"] ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD ret.steeringPressed = abs(ret.steeringTorque) > self.params.STEER_THRESHOLD
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"]["LEFT_LAMP"], ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"]["LEFT_LAMP"],
cp.vl["BLINKERS"]["RIGHT_LAMP"]) cp.vl["BLINKERS"]["RIGHT_LAMP"])

@ -7,7 +7,6 @@ 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_enable_events, create_button_event, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.disable_ecu import disable_ecu from selfdrive.car.disable_ecu import disable_ecu
from selfdrive.controls.lib.latcontrol_torque import set_torque_tune
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -40,7 +39,6 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} or candidate in HDA2_CAR ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30} or candidate in HDA2_CAR
ret.steerActuatorDelay = 0.1 # Default delay ret.steerActuatorDelay = 0.1 # Default delay
ret.steerRateCost = 0.5
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
tire_stiffness_factor = 1. tire_stiffness_factor = 1.
@ -52,7 +50,6 @@ class CarInterface(CarInterfaceBase):
ret.stopAccel = 0.0 ret.stopAccel = 0.0
ret.longitudinalActuatorDelayUpperBound = 1.0 # s ret.longitudinalActuatorDelayUpperBound = 1.0 # s
if candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022): if candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022):
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.mass = 3982. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3982. * CV.LB_TO_KG + STD_CARGO_KG
@ -60,24 +57,19 @@ class CarInterface(CarInterfaceBase):
# Values from optimizer # Values from optimizer
ret.steerRatio = 16.55 # 13.8 is spec end-to-end ret.steerRatio = 16.55 # 13.8 is spec end-to-end
tire_stiffness_factor = 0.82 tire_stiffness_factor = 0.82
ret.maxLateralAccel = 3.2
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[9., 22.], [9., 22.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[9., 22.], [9., 22.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.35], [0.05, 0.09]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2, 0.35], [0.05, 0.09]]
elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID): elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID):
ret.lateralTuning.pid.kf = 0.00005
ret.mass = 1513. + STD_CARGO_KG ret.mass = 1513. + STD_CARGO_KG
ret.wheelbase = 2.84 ret.wheelbase = 2.84
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
tire_stiffness_factor = 0.65 tire_stiffness_factor = 0.65
ret.maxLateralAccel = 2.5 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
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.SONATA_LF: elif candidate == CAR.SONATA_LF:
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.mass = 4497. * CV.LB_TO_KG ret.mass = 4497. * CV.LB_TO_KG
ret.wheelbase = 2.804 ret.wheelbase = 2.804
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
ret.maxLateralAccel = 1.8
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.PALISADE: elif candidate == CAR.PALISADE:
@ -86,7 +78,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.90 ret.wheelbase = 2.90
ret.steerRatio = 15.6 * 1.15 ret.steerRatio = 15.6 * 1.15
tire_stiffness_factor = 0.63 tire_stiffness_factor = 0.63
ret.maxLateralAccel = 2.5
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.05]]
elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30): elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30):
@ -99,21 +90,17 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
ret.minSteerSpeed = 32 * CV.MPH_TO_MS ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.ELANTRA_2021: elif candidate == CAR.ELANTRA_2021:
ret.lateralTuning.pid.kf = 0.00005
ret.mass = (2800. * CV.LB_TO_KG) + STD_CARGO_KG ret.mass = (2800. * CV.LB_TO_KG) + STD_CARGO_KG
ret.wheelbase = 2.72 ret.wheelbase = 2.72
ret.steerRatio = 12.9 ret.steerRatio = 12.9
tire_stiffness_factor = 0.65 tire_stiffness_factor = 0.65
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.ELANTRA_HEV_2021: elif candidate == CAR.ELANTRA_HEV_2021:
ret.lateralTuning.pid.kf = 0.00005
ret.mass = (3017. * CV.LB_TO_KG) + STD_CARGO_KG ret.mass = (3017. * CV.LB_TO_KG) + STD_CARGO_KG
ret.wheelbase = 2.72 ret.wheelbase = 2.72
ret.steerRatio = 12.9 ret.steerRatio = 12.9
tire_stiffness_factor = 0.65 tire_stiffness_factor = 0.65
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.HYUNDAI_GENESIS: elif candidate == CAR.HYUNDAI_GENESIS:
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.mass = 2060. + STD_CARGO_KG ret.mass = 2060. + STD_CARGO_KG
@ -143,7 +130,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.7 ret.wheelbase = 2.7
ret.steerRatio = 13.73 # Spec ret.steerRatio = 13.73 # Spec
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
ret.maxLateralAccel = 3.0
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
if candidate not in (CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022): if candidate not in (CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.IONIQ_HEV_2022):
@ -170,9 +156,9 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.5 tire_stiffness_factor = 0.5
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.TUCSON_DIESEL_2019: elif candidate == CAR.TUCSON:
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.mass = 3633. * CV.LB_TO_KG ret.mass = 3520. * CV.LB_TO_KG
ret.wheelbase = 2.67 ret.wheelbase = 2.67
ret.steerRatio = 14.00 * 1.15 ret.steerRatio = 14.00 * 1.15
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
@ -193,7 +179,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.7 ret.wheelbase = 2.7
ret.steerRatio = 13.9 if CAR.KIA_NIRO_HEV_2021 else 13.73 # Spec ret.steerRatio = 13.9 if CAR.KIA_NIRO_HEV_2021 else 13.73 # Spec
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
ret.maxLateralAccel = 2.9
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
if candidate == CAR.KIA_NIRO_HEV: if candidate == CAR.KIA_NIRO_HEV:
@ -213,19 +198,16 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.indi.actuatorEffectivenessBP = [0.] ret.lateralTuning.indi.actuatorEffectivenessBP = [0.]
ret.lateralTuning.indi.actuatorEffectivenessV = [1.8] ret.lateralTuning.indi.actuatorEffectivenessV = [1.8]
elif candidate in (CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_H): elif candidate in (CAR.KIA_OPTIMA, CAR.KIA_OPTIMA_H):
ret.lateralTuning.pid.kf = 0.00005
ret.mass = 3558. * CV.LB_TO_KG ret.mass = 3558. * CV.LB_TO_KG
ret.wheelbase = 2.80 ret.wheelbase = 2.80
ret.steerRatio = 13.75 ret.steerRatio = 13.75
tire_stiffness_factor = 0.5 tire_stiffness_factor = 0.5
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.KIA_STINGER: elif candidate == CAR.KIA_STINGER:
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.mass = 1825. + STD_CARGO_KG ret.mass = 1825. + STD_CARGO_KG
ret.wheelbase = 2.78 ret.wheelbase = 2.78
ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable
ret.maxLateralAccel = 2.4
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.KIA_FORTE: elif candidate == CAR.KIA_FORTE:
@ -251,7 +233,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.85 ret.wheelbase = 2.85
ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims) ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims)
tire_stiffness_factor = 0.5 tire_stiffness_factor = 0.5
ret.maxLateralAccel = 2.1
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.25], [0.05]]
elif candidate == CAR.KIA_EV6: elif candidate == CAR.KIA_EV6:
@ -261,9 +242,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput),
get_safety_config(car.CarParams.SafetyModel.hyundaiHDA2)] get_safety_config(car.CarParams.SafetyModel.hyundaiHDA2)]
tire_stiffness_factor = 0.65 tire_stiffness_factor = 0.65
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
ret.maxLateralAccel = 2.
set_torque_tune(ret.lateralTuning, ret.maxLateralAccel, 0.01)
# Genesis # Genesis
elif candidate == CAR.GENESIS_G70: elif candidate == CAR.GENESIS_G70:
@ -370,5 +349,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS) return self.CC.update(c, self.CS)
return ret

@ -13,21 +13,27 @@ class CarControllerParams:
ACCEL_MAX = 2.0 # m/s ACCEL_MAX = 2.0 # m/s
def __init__(self, CP): def __init__(self, CP):
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 7
self.STEER_DRIVER_ALLOWANCE = 50
self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_DRIVER_FACTOR = 1
self.STEER_THRESHOLD = 150
if CP.carFingerprint in HDA2_CAR:
self.STEER_MAX = 270
self.STEER_DRIVER_ALLOWANCE = 250
self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_THRESHOLD = 250
# To determine the limit for your car, find the maximum value that the stock LKAS will request. # 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. # If the max stock LKAS request is <384, add your car to this list.
if CP.carFingerprint in HDA2_CAR:
self.STEER_MAX = 150
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.HYUNDAI_GENESIS, CAR.ELANTRA_GT_I30, CAR.IONIQ, 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_HEV,
CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER): CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO, CAR.KIA_STINGER):
self.STEER_MAX = 255 self.STEER_MAX = 255
else: else:
self.STEER_MAX = 384 self.STEER_MAX = 384
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 7
self.STEER_DRIVER_ALLOWANCE = 50
self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_DRIVER_FACTOR = 1
class CAR: class CAR:
@ -52,7 +58,7 @@ class CAR:
SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022" SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022"
SONATA = "HYUNDAI SONATA 2020" SONATA = "HYUNDAI SONATA 2020"
SONATA_LF = "HYUNDAI SONATA 2019" SONATA_LF = "HYUNDAI SONATA 2019"
TUCSON_DIESEL_2019 = "HYUNDAI TUCSON DIESEL 2019" TUCSON = "HYUNDAI TUCSON 2019"
PALISADE = "HYUNDAI PALISADE 2020" PALISADE = "HYUNDAI PALISADE 2020"
VELOSTER = "HYUNDAI VELOSTER 2019" VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
@ -80,8 +86,9 @@ class CAR:
@dataclass @dataclass
class HyundaiCarInfo(CarInfo): class HyundaiCarInfo(CarInfo):
# TODO: we can probably remove LKAS. LKAS is standard on many
# HKG and for others, it's likely packaged together with SCC
package: str = "SCC + LKAS" package: str = "SCC + LKAS"
good_torque: bool = True
CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
@ -105,7 +112,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in 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), CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-22", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", harness=Harness.hyundai_a),
CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", harness=Harness.hyundai_e), CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", harness=Harness.hyundai_e),
CAR.TUCSON_DIESEL_2019: HyundaiCarInfo("Hyundai Tucson Diesel 2019", harness=Harness.hyundai_l), CAR.TUCSON: [
HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, harness=Harness.hyundai_l),
HyundaiCarInfo("Hyundai Tucson Diesel 2019", harness=Harness.hyundai_l),
],
CAR.PALISADE: [ CAR.PALISADE: [
HyundaiCarInfo("Hyundai Palisade 2020-21", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", harness=Harness.hyundai_h), HyundaiCarInfo("Hyundai Palisade 2020-21", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", harness=Harness.hyundai_h),
HyundaiCarInfo("Kia Telluride 2020", harness=Harness.hyundai_h), HyundaiCarInfo("Kia Telluride 2020", harness=Harness.hyundai_h),
@ -118,7 +128,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Forte 2018", harness=Harness.hyundai_b), HyundaiCarInfo("Kia Forte 2018", harness=Harness.hyundai_b),
HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g),
], ],
CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", "SCC + LFA", harness=Harness.hyundai_a), CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", "SCC", harness=Harness.hyundai_a),
CAR.KIA_NIRO_EV: [ CAR.KIA_NIRO_EV: [
HyundaiCarInfo("Kia Niro Electric 2019-20", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_f), HyundaiCarInfo("Kia Niro Electric 2019-20", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_f),
HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Niro Electric 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness=Harness.hyundai_c),
@ -141,7 +151,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
], ],
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018", 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_CEED: HyundaiCarInfo("Kia Ceed 2019", harness=Harness.hyundai_e),
CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "All", harness=Harness.none), CAR.KIA_EV6: HyundaiCarInfo("Kia EV6 2022", "All", harness=Harness.hyundai_p),
# Genesis # Genesis
CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018", "All", harness=Harness.hyundai_f), CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018", "All", harness=Harness.hyundai_f),
@ -498,6 +508,7 @@ FW_VERSIONS = {
], ],
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00LFF LKAS AT USA LHD 1.00 1.01 95740-C1000 E51', b'\xf1\x00LFF LKAS AT USA LHD 1.00 1.01 95740-C1000 E51',
b'\xf1\x00LFF LKAS AT USA LHD 1.01 1.02 95740-C1000 E52',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24NL1\xb0\x9f\xee\xf5', b'\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24NL1\xb0\x9f\xee\xf5',
@ -506,20 +517,25 @@ FW_VERSIONS = {
b'\xf1\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf1\x816T6B4051\x00\x00\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24SL2n\x8d\xbe\xd8', b'\xf1\x87\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf1\x816T6B4051\x00\x00\xf1\x006T6H0_C2\x00\x006T6B4051\x00\x00TLF0G24SL2n\x8d\xbe\xd8',
b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2\x00\x00\x00\x00', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2\x00\x00\x00\x00',
b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm', b'\xf1\x87LAHSGN012918KF10\x98\x88x\x87\x88\x88x\x87\x88\x88\x98\x88\x87w\x88w\x88\x88\x98\x886o\xf6\xff\x98w\x7f\xff3\x00\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm',
b'\xf1\x87LAJSG49645724HF0\x87x\x87\x88\x87www\x88\x99\xa8\x89\x88\x99\xa8\x89\x88\x99\xa8\x89S_\xfb\xff\x87f\x7f\xff^2\xf1\x816W3B1051\x00\x00\xf1\x006W351_C2\x00\x006W3B1051\x00\x00TLF0T20NL2H\r\xbdm',
], ],
}, },
CAR.TUCSON_DIESEL_2019: { CAR.TUCSON: {
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00TL__ FCA F-CUP 1.00 1.01 99110-D3500 ', b'\xf1\x00TL__ FCA F-CUP 1.00 1.01 99110-D3500 ',
b'\xf1\x00TL__ FCA F-CUP 1.00 1.02 99110-D3510 ',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8971TLC2NAIDDIR002\xf1\x8271TLC2NAIDDIR002', b'\xf1\x8971TLC2NAIDDIR002\xf1\x8271TLC2NAIDDIR002',
b'\xf1\x81606G3051\x00\x00\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00TL MFC AT KOR LHD 1.00 1.02 95895-D3800 180719', b'\xf1\x00TL MFC AT KOR LHD 1.00 1.02 95895-D3800 180719',
b'\xf1\x00TL MFC AT USA LHD 1.00 1.06 95895-D3800 190107',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x87LBJXAN202299KF22\x87x\x87\x88ww\x87xx\x88\x97\x88\x87\x88\x98x\x88\x99\x98\x89\x87o\xf6\xff\x87w\x7f\xff\x12\x9a\xf1\x81U083\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U083\x00\x00\x00\x00\x00\x00TTL2V20KL1\x8fRn\x8a', b'\xf1\x87LBJXAN202299KF22\x87x\x87\x88ww\x87xx\x88\x97\x88\x87\x88\x98x\x88\x99\x98\x89\x87o\xf6\xff\x87w\x7f\xff\x12\x9a\xf1\x81U083\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U083\x00\x00\x00\x00\x00\x00TTL2V20KL1\x8fRn\x8a',
b'\xf1\x87KMLDCU585233TJ20wx\x87\x88x\x88\x98\x89vfwfwwww\x87f\x9f\xff\x98\xff\x7f\xf9\xf7s\xf1\x816T6G4051\x00\x00\xf1\x006T6J0_C2\x00\x006T6G4051\x00\x00TTL4G24NH2\x00\x00\x00\x00',
], ],
}, },
CAR.SANTA_FE: { CAR.SANTA_FE: {
@ -845,7 +861,7 @@ FW_VERSIONS = {
}, },
CAR.KIA_CEED: { CAR.KIA_CEED: {
(Ecu.fwdRadar, 0x7D0, None): [b'\xf1\000CD__ SCC F-CUP 1.00 1.02 99110-J7000 ', ], (Ecu.fwdRadar, 0x7D0, None): [b'\xf1\000CD__ SCC F-CUP 1.00 1.02 99110-J7000 ', ],
(Ecu.esp, 0x7D4, None): [b'\xf1\000CD MDPS C 1.00 1.06 56310-XX000 4CDEC106', ], (Ecu.eps, 0x7D4, None): [b'\xf1\000CD MDPS C 1.00 1.06 56310-XX000 4CDEC106', ],
(Ecu.fwdCamera, 0x7C4, None): [b'\xf1\000CD LKAS AT EUR LHD 1.00 1.01 99211-J7000 B40', ], (Ecu.fwdCamera, 0x7C4, None): [b'\xf1\000CD LKAS AT EUR LHD 1.00 1.01 99211-J7000 B40', ],
(Ecu.engine, 0x7E0, None): [b'\001TCD-JECU4F202H0K', ], (Ecu.engine, 0x7E0, None): [b'\001TCD-JECU4F202H0K', ],
(Ecu.transmission, 0x7E1, None): [ (Ecu.transmission, 0x7E1, None): [
@ -1129,9 +1145,6 @@ FW_VERSIONS = {
b'\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', b'\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
b'\xf1\x8799110L5000\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ', b'\xf1\x8799110L5000\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
], ],
(Ecu.esp, 0x7b0, None): [
b'\xf1\x87\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7d4, None): [ (Ecu.eps, 0x7d4, None): [
b'\xf1\x8756310-L5500\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5500 4DNHC102', b'\xf1\x8756310-L5500\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5500 4DNHC102',
b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102', b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102',
@ -1198,11 +1211,11 @@ CHECKSUM = {
FEATURES = { FEATURES = {
# which message has the gear # which message has the gear
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA}, "use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON_DIESEL_2019}, "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}, "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},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 # 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_DIESEL_2019}, "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},
} }
HDA2_CAR = {CAR.KIA_EV6, } HDA2_CAR = {CAR.KIA_EV6, }
@ -1250,12 +1263,10 @@ DBC = {
CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None), CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.TUCSON_DIESEL_2019: dbc_dict('hyundai_kia_generic', None), CAR.TUCSON: dbc_dict('hyundai_kia_generic', None),
CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None), CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_CEED: 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('kia_ev6', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar'),
} }
STEER_THRESHOLD = 150

@ -1,3 +1,4 @@
import yaml
import os import os
import time import time
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
@ -20,6 +21,34 @@ MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
ACCEL_MAX = 2.0 ACCEL_MAX = 2.0
ACCEL_MIN = -3.5 ACCEL_MIN = -3.5
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.yaml')
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.yaml')
TORQUE_SUBSTITUTE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/substitute.yaml')
def get_torque_params(candidate):
with open(TORQUE_SUBSTITUTE_PATH) as f:
sub = yaml.load(f, Loader=yaml.CSafeLoader)
if candidate in sub:
candidate = sub[candidate]
with open(TORQUE_PARAMS_PATH) as f:
params = yaml.load(f, Loader=yaml.CSafeLoader)
with open(TORQUE_OVERRIDE_PATH) as f:
override = yaml.load(f, Loader=yaml.CSafeLoader)
# Ensure no overlap
if sum([candidate in x for x in [sub, params, override]]) > 1:
raise RuntimeError(f'{candidate} is defined twice in torque config')
if candidate in override:
out = override[candidate]
elif candidate in params:
out = params[candidate]
else:
raise NotImplementedError(f"Did not find torque params for {candidate}")
return {key: out[i] for i, key in enumerate(params['legend'])}
# generic car and radar interfaces # generic car and radar interfaces
@ -81,7 +110,7 @@ class CarInterfaceBase(ABC):
ret.steerControlType = car.CarParams.SteerControlType.torque ret.steerControlType = car.CarParams.SteerControlType.torque
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
ret.wheelSpeedFactor = 1.0 ret.wheelSpeedFactor = 1.0
ret.maxLateralAccel = float('nan') ret.maxLateralAccel = get_torque_params(candidate)['MAX_LAT_ACCEL_MEASURED']
ret.pcmCruise = True # openpilot's state is tied to the PCM's cruise state on most cars ret.pcmCruise = True # openpilot's state is tied to the PCM's cruise state on most cars
ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this
@ -105,6 +134,18 @@ class CarInterfaceBase(ABC):
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
return ret return ret
@staticmethod
def configure_torque_tune(candidate, tune, steering_angle_deadzone_deg=0.0):
params = get_torque_params(candidate)
tune.init('torque')
tune.torque.useSteeringAngle = True
tune.torque.kp = 1.0 / params['LAT_ACCEL_FACTOR']
tune.torque.kf = 1.0 / params['LAT_ACCEL_FACTOR']
tune.torque.ki = 0.1 / params['LAT_ACCEL_FACTOR']
tune.torque.friction = params['FRICTION']
tune.torque.steeringAngleDeadzoneDeg = steering_angle_deadzone_deg
@abstractmethod @abstractmethod
def _update(self, c: car.CarControl) -> car.CarState: def _update(self, c: car.CarControl) -> car.CarState:
pass pass

@ -4,13 +4,13 @@ from functools import partial
from typing import Optional from typing import Optional
import cereal.messaging as messaging import cereal.messaging as messaging
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.boardd.boardd import can_list_to_can_capnp
from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr from panda.python.uds import CanClient, IsoTpMessage, FUNCTIONAL_ADDRS, get_rx_addr_for_tx_addr
class IsoTpParallelQuery: class IsoTpParallelQuery:
def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addr=False, debug=False): def __init__(self, sendcan, logcan, bus, addrs, request, response, response_offset=0x8, functional_addr=False, debug=False, response_pending_timeout=10):
self.sendcan = sendcan self.sendcan = sendcan
self.logcan = logcan self.logcan = logcan
self.bus = bus self.bus = bus
@ -18,6 +18,7 @@ class IsoTpParallelQuery:
self.response = response self.response = response
self.debug = debug self.debug = debug
self.functional_addr = functional_addr self.functional_addr = functional_addr
self.response_pending_timeout = response_pending_timeout
self.real_addrs = [] self.real_addrs = []
for a in addrs: for a in addrs:
@ -71,10 +72,7 @@ class IsoTpParallelQuery:
messaging.drain_sock(self.logcan) messaging.drain_sock(self.logcan)
self.msg_buffer = defaultdict(list) self.msg_buffer = defaultdict(list)
def get_data(self, timeout, total_timeout=None): def get_data(self, timeout, total_timeout=60.):
if total_timeout is None:
total_timeout = 10 * timeout
self._drain_rx() self._drain_rx()
# Create message objects # Create message objects
@ -100,7 +98,7 @@ class IsoTpParallelQuery:
results = {} results = {}
start_time = time.monotonic() start_time = time.monotonic()
last_response_time = start_time response_timeouts = {tx_addr: start_time + timeout for tx_addr in self.msg_addrs}
while True: while True:
self.rx() self.rx()
@ -123,21 +121,27 @@ class IsoTpParallelQuery:
response_valid = dat[:len(expected_response)] == expected_response response_valid = dat[:len(expected_response)] == expected_response
if response_valid: if response_valid:
last_response_time = time.monotonic() response_timeouts[tx_addr] = time.monotonic() + timeout
if counter + 1 < len(self.request): if counter + 1 < len(self.request):
msg.send(self.request[counter + 1]) msg.send(self.request[counter + 1])
request_counter[tx_addr] += 1 request_counter[tx_addr] += 1
else: else:
results[tx_addr] = dat[len(expected_response):] results[tx_addr] = dat[len(expected_response):]
request_done[tx_addr] = True request_done[tx_addr] = True
else:
error_code = dat[2] if len(dat) > 2 else -1
if error_code == 0x78:
response_timeouts[tx_addr] = time.monotonic() + self.response_pending_timeout
if self.debug:
cloudlog.warning(f"iso-tp query response pending: {tx_addr}")
else: else:
request_done[tx_addr] = True request_done[tx_addr] = True
cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}") cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}")
cur_time = time.monotonic() cur_time = time.monotonic()
if cur_time - last_response_time > timeout: if cur_time - max(response_timeouts.values()) > 0:
for tx_addr in msgs: for tx_addr in msgs:
if (request_counter[tx_addr] > 0) and (not request_done[tx_addr]): if request_counter[tx_addr] > 0 and not request_done[tx_addr]:
cloudlog.warning(f"iso-tp query timeout after receiving response: {tx_addr}") cloudlog.warning(f"iso-tp query timeout after receiving response: {tx_addr}")
break break

@ -1,45 +1,48 @@
from cereal import car from cereal import car
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.mazda import mazdacan from selfdrive.car.mazda import mazdacan
from selfdrive.car.mazda.values import CarControllerParams, Buttons from selfdrive.car.mazda.values import CarControllerParams, Buttons
from selfdrive.car import apply_std_steer_torque_limits
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
class CarController():
class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
self.steer_rate_limited = False self.steer_rate_limited = False
self.brake_counter = 0 self.brake_counter = 0
self.frame = 0
def update(self, c, CS, frame): def update(self, CC, CS):
can_sends = [] can_sends = []
apply_steer = 0 apply_steer = 0
self.steer_rate_limited = False self.steer_rate_limited = False
if c.latActive: if CC.latActive:
# calculate steer and also set limits due to driver torque # calculate steer and also set limits due to driver torque
new_steer = int(round(c.actuators.steer * CarControllerParams.STEER_MAX)) new_steer = int(round(CC.actuators.steer * CarControllerParams.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last,
CS.out.steeringTorque, CarControllerParams) CS.out.steeringTorque, CarControllerParams)
self.steer_rate_limited = new_steer != apply_steer self.steer_rate_limited = new_steer != apply_steer
if CS.out.standstill and frame % 5 == 0: if CC.enabled:
if CS.out.standstill and self.frame % 5 == 0:
# Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds # Mazda Stop and Go requires a RES button (or gas) press if the car stops more than 3 seconds
# Send Resume button at 20hz if we're engaged at standstill to support full stop and go! # Send Resume button at 20hz if we're engaged at standstill to support full stop and go!
# TODO: improve the resume trigger logic by looking at actual radar data # TODO: improve the resume trigger logic by looking at actual radar data
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME)) can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.RESUME))
if c.cruiseControl.cancel: if CC.cruiseControl.cancel:
# If brake is pressed, let us wait >70ms before trying to disable crz to avoid # If brake is pressed, let us wait >70ms before trying to disable crz to avoid
# a race condition with the stock system, where the second cancel from openpilot # a race condition with the stock system, where the second cancel from openpilot
# will disable the crz 'main on'. crz ctrl msg runs at 50hz. 70ms allows us to # will disable the crz 'main on'. crz ctrl msg runs at 50hz. 70ms allows us to
# read 3 messages and most likely sync state before we attempt cancel. # read 3 messages and most likely sync state before we attempt cancel.
self.brake_counter = self.brake_counter + 1 self.brake_counter = self.brake_counter + 1
if frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7): if self.frame % 10 == 0 and not (CS.out.brakePressed and self.brake_counter < 7):
# Cancel Stock ACC if it's enabled while OP is disengaged # Cancel Stock ACC if it's enabled while OP is disengaged
# Send at a rate of 10hz until we sync with stock ACC state # Send at a rate of 10hz until we sync with stock ACC state
can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL)) can_sends.append(mazdacan.create_button_cmd(self.packer, self.CP.carFingerprint, CS.crz_btns_counter, Buttons.CANCEL))
@ -49,18 +52,19 @@ class CarController():
self.apply_steer_last = apply_steer self.apply_steer_last = apply_steer
# send HUD alerts # send HUD alerts
if frame % 50 == 0: if self.frame % 50 == 0:
ldw = c.hudControl.visualAlert == VisualAlert.ldw ldw = CC.hudControl.visualAlert == VisualAlert.ldw
steer_required = c.hudControl.visualAlert == VisualAlert.steerRequired steer_required = CC.hudControl.visualAlert == VisualAlert.steerRequired
# TODO: find a way to silence audible warnings so we can add more hud alerts # TODO: find a way to silence audible warnings so we can add more hud alerts
steer_required = steer_required and CS.lkas_allowed_speed steer_required = steer_required and CS.lkas_allowed_speed
can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required)) can_sends.append(mazdacan.create_alert_command(self.packer, CS.cam_laneinfo, ldw, steer_required))
# send steering command # send steering command
can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint, can_sends.append(mazdacan.create_steering_control(self.packer, self.CP.carFingerprint,
frame, apply_steer, CS.cam_lkas)) self.frame, apply_steer, CS.cam_lkas))
new_actuators = c.actuators.copy() new_actuators = CC.actuators.copy()
new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends

@ -79,6 +79,7 @@ class CarState(CarStateBase):
# it should be used for carState.cruiseState.nonAdaptive instead # it should be used for carState.cruiseState.nonAdaptive instead
ret.cruiseState.available = cp.vl["CRZ_CTRL"]["CRZ_AVAILABLE"] == 1 ret.cruiseState.available = cp.vl["CRZ_CTRL"]["CRZ_AVAILABLE"] == 1
ret.cruiseState.enabled = cp.vl["CRZ_CTRL"]["CRZ_ACTIVE"] == 1 ret.cruiseState.enabled = cp.vl["CRZ_CTRL"]["CRZ_ACTIVE"] == 1
ret.cruiseState.standstill = cp.vl["PEDALS"]["STANDSTILL"] == 1
ret.cruiseState.speed = cp.vl["CRZ_EVENTS"]["CRZ_SPEED"] * CV.KPH_TO_MS ret.cruiseState.speed = cp.vl["CRZ_EVENTS"]["CRZ_SPEED"] * CV.KPH_TO_MS
if ret.cruiseState.enabled: if ret.cruiseState.enabled:

@ -25,7 +25,6 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = candidate not in (CAR.CX5_2022, CAR.CX9_2021) ret.dashcamOnly = candidate not in (CAR.CX5_2022, CAR.CX9_2021)
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRateCost = 1.0
ret.steerLimitTimer = 0.8 ret.steerLimitTimer = 0.8
tire_stiffness_factor = 0.70 # not optimized yet tire_stiffness_factor = 0.70 # not optimized yet
@ -91,6 +90,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS, self.frame) return self.CC.update(c, self.CS)
self.frame += 1
return ret

@ -40,8 +40,8 @@ CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = {
CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-17"), CAR.CX9: MazdaCarInfo("Mazda CX-9 2016-17"),
CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017"), CAR.MAZDA3: MazdaCarInfo("Mazda 3 2017"),
CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017"), CAR.MAZDA6: MazdaCarInfo("Mazda 6 2017"),
CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021", good_torque=True), CAR.CX9_2021: MazdaCarInfo("Mazda CX-9 2021"),
CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022", good_torque=True), CAR.CX5_2022: MazdaCarInfo("Mazda CX-5 2022"),
} }
@ -65,6 +65,7 @@ FW_VERSIONS = {
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'PX2G-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',
], ],
(Ecu.fwdRadar, 0x764, None): [ (Ecu.fwdRadar, 0x764, None): [
b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'K131-67XK2-F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@ -77,6 +78,7 @@ FW_VERSIONS = {
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 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',
], ],
}, },
CAR.CX5: { CAR.CX5: {

@ -2,7 +2,7 @@
import math import math
from cereal import car from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.swaglog import cloudlog from system.swaglog import cloudlog
import cereal.messaging as messaging import cereal.messaging as messaging
from selfdrive.car import gen_empty_fingerprint, get_safety_config from selfdrive.car import gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase

@ -1,25 +1,27 @@
from cereal import car from cereal import car
from common.numpy_fast import clip, interp from common.numpy_fast import clip, interp
from selfdrive.car.nissan import nissancan
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
from selfdrive.car.nissan import nissancan
from selfdrive.car.nissan.values import CAR, CarControllerParams from selfdrive.car.nissan.values import CAR, CarControllerParams
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
class CarController(): class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.car_fingerprint = CP.carFingerprint self.car_fingerprint = CP.carFingerprint
self.frame = 0
self.lkas_max_torque = 0 self.lkas_max_torque = 0
self.last_angle = 0 self.last_angle = 0
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
def update(self, c, CS, frame, actuators, cruise_cancel, hud_alert, def update(self, CC, CS):
left_line, right_line, left_lane_depart, right_lane_depart): actuators = CC.actuators
hud_control = CC.hudControl
pcm_cancel_cmd = CC.cruiseControl.cancel
can_sends = [] can_sends = []
@ -28,9 +30,9 @@ class CarController():
lkas_hud_info_msg = CS.lkas_hud_info_msg lkas_hud_info_msg = CS.lkas_hud_info_msg
apply_angle = actuators.steeringAngleDeg apply_angle = actuators.steeringAngleDeg
steer_hud_alert = 1 if hud_alert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0 steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0
if c.latActive: if CC.latActive:
# # windup slower # # windup slower
if self.last_angle * apply_angle > 0. and abs(apply_angle) > abs(self.last_angle): if self.last_angle * apply_angle > 0. and abs(apply_angle) > abs(self.last_angle):
angle_rate_lim = interp(CS.out.vEgo, CarControllerParams.ANGLE_DELTA_BP, CarControllerParams.ANGLE_DELTA_V) angle_rate_lim = interp(CS.out.vEgo, CarControllerParams.ANGLE_DELTA_BP, CarControllerParams.ANGLE_DELTA_V)
@ -57,25 +59,25 @@ class CarController():
self.last_angle = apply_angle self.last_angle = apply_angle
if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA) and cruise_cancel: if self.CP.carFingerprint in (CAR.ROGUE, CAR.XTRAIL, CAR.ALTIMA) and pcm_cancel_cmd:
can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg, frame)) can_sends.append(nissancan.create_acc_cancel_cmd(self.packer, self.car_fingerprint, CS.cruise_throttle_msg, self.frame))
# TODO: Find better way to cancel! # TODO: Find better way to cancel!
# For some reason spamming the cancel button is unreliable on the Leaf # For some reason spamming the cancel button is unreliable on the Leaf
# We now cancel by making propilot think the seatbelt is unlatched, # We now cancel by making propilot think the seatbelt is unlatched,
# this generates a beep and a warning message every time you disengage # this generates a beep and a warning message every time you disengage
if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC) and frame % 2 == 0: if self.CP.carFingerprint in (CAR.LEAF, CAR.LEAF_IC) and self.frame % 2 == 0:
can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, cruise_cancel)) can_sends.append(nissancan.create_cancel_msg(self.packer, CS.cancel_msg, pcm_cancel_cmd))
can_sends.append(nissancan.create_steering_control( can_sends.append(nissancan.create_steering_control(
self.packer, apply_angle, frame, c.enabled, self.lkas_max_torque)) self.packer, apply_angle, self.frame, CC.enabled, self.lkas_max_torque))
if lkas_hud_msg and lkas_hud_info_msg: if lkas_hud_msg and lkas_hud_info_msg:
if frame % 2 == 0: if self.frame % 2 == 0:
can_sends.append(nissancan.create_lkas_hud_msg( can_sends.append(nissancan.create_lkas_hud_msg(
self.packer, lkas_hud_msg, c.enabled, left_line, right_line, left_lane_depart, right_lane_depart)) self.packer, lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart))
if frame % 50 == 0: if self.frame % 50 == 0:
can_sends.append(nissancan.create_lkas_hud_info_msg( can_sends.append(nissancan.create_lkas_hud_info_msg(
self.packer, lkas_hud_info_msg, steer_hud_alert self.packer, lkas_hud_info_msg, steer_hud_alert
)) ))
@ -83,4 +85,5 @@ class CarController():
new_actuators = actuators.copy() new_actuators = actuators.copy()
new_actuators.steeringAngleDeg = apply_angle new_actuators.steeringAngleDeg = apply_angle
self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.car.nissan.values import CAR
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config 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.interfaces import CarInterfaceBase
from selfdrive.car.nissan.values import CAR
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -14,7 +15,6 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)]
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.steerRateCost = 0.5
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
@ -68,10 +68,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
hud_control = c.hudControl return self.CC.update(c, self.CS)
ret = self.CC.update(c, self.CS, self.frame, c.actuators,
c.cruiseControl.cancel, hud_control.visualAlert,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
return ret

@ -1,10 +1,10 @@
from opendbc.can.packer import CANPacker
from selfdrive.car import apply_std_steer_torque_limits from selfdrive.car import apply_std_steer_torque_limits
from selfdrive.car.subaru import subarucan from selfdrive.car.subaru import subarucan
from selfdrive.car.subaru.values import DBC, PREGLOBAL_CARS, CarControllerParams from selfdrive.car.subaru.values import DBC, PREGLOBAL_CARS, CarControllerParams
from opendbc.can.packer import CANPacker
class CarController(): class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
self.apply_steer_last = 0 self.apply_steer_last = 0
@ -12,16 +12,20 @@ class CarController():
self.es_lkas_cnt = -1 self.es_lkas_cnt = -1
self.cruise_button_prev = 0 self.cruise_button_prev = 0
self.steer_rate_limited = False self.steer_rate_limited = False
self.frame = 0
self.p = CarControllerParams(CP) self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint]['pt']) self.packer = CANPacker(DBC[CP.carFingerprint]['pt'])
def update(self, c, CS, frame, actuators, pcm_cancel_cmd, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart): def update(self, CC, CS):
actuators = CC.actuators
hud_control = CC.hudControl
pcm_cancel_cmd = CC.cruiseControl.cancel
can_sends = [] can_sends = []
# *** steering *** # *** steering ***
if (frame % self.p.STEER_STEP) == 0: if (self.frame % self.p.STEER_STEP) == 0:
apply_steer = int(round(actuators.steer * self.p.STEER_MAX)) apply_steer = int(round(actuators.steer * self.p.STEER_MAX))
@ -31,13 +35,13 @@ class CarController():
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.p)
self.steer_rate_limited = new_steer != apply_steer self.steer_rate_limited = new_steer != apply_steer
if not c.latActive: if not CC.latActive:
apply_steer = 0 apply_steer = 0
if self.CP.carFingerprint in PREGLOBAL_CARS: if self.CP.carFingerprint in PREGLOBAL_CARS:
can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP)) can_sends.append(subarucan.create_preglobal_steering_control(self.packer, apply_steer, self.frame, self.p.STEER_STEP))
else: else:
can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, frame, self.p.STEER_STEP)) can_sends.append(subarucan.create_steering_control(self.packer, apply_steer, self.frame, self.p.STEER_STEP))
self.apply_steer_last = apply_steer self.apply_steer_last = apply_steer
@ -70,10 +74,11 @@ class CarController():
self.es_distance_cnt = CS.es_distance_msg["Counter"] self.es_distance_cnt = CS.es_distance_msg["Counter"]
if self.es_lkas_cnt != CS.es_lkas_msg["Counter"]: if self.es_lkas_cnt != CS.es_lkas_msg["Counter"]:
can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, c.enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart)) can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, CC.enabled, hud_control.visualAlert, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart))
self.es_lkas_cnt = CS.es_lkas_msg["Counter"] self.es_lkas_cnt = CS.es_lkas_msg["Counter"]
new_actuators = actuators.copy() new_actuators = actuators.copy()
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends

@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config 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.interfaces import CarInterfaceBase
from selfdrive.car.subaru.values import CAR, PREGLOBAL_CARS
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@ -22,7 +23,6 @@ class CarInterface(CarInterfaceBase):
ret.dashcamOnly = candidate in PREGLOBAL_CARS ret.dashcamOnly = candidate in PREGLOBAL_CARS
ret.steerRateCost = 0.7
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
if candidate == CAR.ASCENT: if candidate == CAR.ASCENT:
@ -51,7 +51,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.5 ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17 # learned, 14 stock ret.steerRatio = 17 # learned, 14 stock
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.maxLateralAccel = 1.3
ret.lateralTuning.pid.kf = 0.00005 ret.lateralTuning.pid.kf = 0.00005
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.045, 0.042, 0.20], [0.04, 0.035, 0.045]]
@ -62,7 +61,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.5 ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 17 # learned, 14 stock ret.steerRatio = 17 # learned, 14 stock
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.maxLateralAccel = 3.2
ret.lateralTuning.pid.kf = 0.000038 ret.lateralTuning.pid.kf = 0.000038
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]] 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]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
@ -120,9 +118,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
hud_control = c.hudControl return self.CC.update(c, self.CS)
ret = self.CC.update(c, self.CS, self.frame, c.actuators,
c.cruiseControl.cancel, hud_control.visualAlert,
hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)
self.frame += 1
return ret

@ -41,7 +41,7 @@ class SubaruCarInfo(CarInfo):
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-20"), CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-20", "All"),
CAR.IMPREZA: [ CAR.IMPREZA: [
SubaruCarInfo("Subaru Impreza 2017-19"), SubaruCarInfo("Subaru Impreza 2017-19"),
SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"),
@ -50,7 +50,7 @@ CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
SubaruCarInfo("Subaru Impreza 2020-21"), SubaruCarInfo("Subaru Impreza 2020-21"),
SubaruCarInfo("Subaru Crosstrek 2020-21"), SubaruCarInfo("Subaru Crosstrek 2020-21"),
], ],
CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21"), CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"),
CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"), CAR.FORESTER_PREGLOBAL: SubaruCarInfo("Subaru Forester 2017-18"),
CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"), CAR.LEGACY_PREGLOBAL: SubaruCarInfo("Subaru Legacy 2015-18"),
CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"), CAR.OUTBACK_PREGLOBAL: SubaruCarInfo("Subaru Outback 2015-17"),

@ -42,7 +42,6 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
ret.steerActuatorDelay = 0.25 ret.steerActuatorDelay = 0.25
ret.steerRateCost = 0.5
if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS): if candidate in (CAR.AP2_MODELS, CAR.AP1_MODELS):
ret.mass = 2100. + STD_CARGO_KG ret.mass = 2100. + STD_CARGO_KG
@ -65,5 +64,4 @@ class CarInterface(CarInterfaceBase):
return ret return ret
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS) return self.CC.update(c, self.CS)
return ret

@ -22,7 +22,7 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
FINGERPRINTS = { FINGERPRINTS = {
CAR.AP2_MODELS: [ CAR.AP2_MODELS: [
{ {
1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 538: 8, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 576: 3, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1625: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2015: 8, 2043: 5, 2045: 4 1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4, 309: 5, 325: 8, 328: 5, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 518: 8, 520: 4, 522: 8, 524: 8, 526: 8, 532: 3, 536: 8, 537: 3, 538: 8, 542: 8, 551: 5, 552: 2, 556: 8, 558: 8, 568: 8, 569: 8, 574: 8, 576: 3, 577: 8, 582: 5, 583: 8, 584: 4, 585: 8, 590: 8, 601: 8, 606: 8, 608: 1, 622: 8, 627: 6, 638: 8, 641: 8, 643: 8, 692: 8, 693: 8, 695: 8, 696: 8, 697: 8, 699: 8, 700: 8, 701: 8, 702: 8, 703: 8, 704: 8, 708: 8, 709: 8, 710: 8, 711: 8, 712: 8, 728: 8, 744: 8, 760: 8, 772: 8, 775: 8, 776: 8, 777: 8, 778: 8, 782: 8, 788: 8, 791: 8, 792: 8, 796: 2, 797: 8, 798: 6, 799: 8, 804: 8, 805: 8, 807: 8, 808: 1, 811: 8, 812: 8, 813: 8, 814: 5, 815: 8, 820: 8, 823: 8, 824: 8, 829: 8, 830: 5, 836: 8, 840: 8, 845: 8, 846: 5, 848: 8, 852: 8, 853: 8, 856: 4, 857: 6, 861: 8, 862: 5, 872: 8, 876: 8, 877: 8, 879: 8, 880: 8, 882: 8, 884: 8, 888: 8, 893: 8, 894: 8, 901: 6, 904: 3, 905: 8, 906: 8, 908: 2, 909: 8, 910: 8, 912: 8, 920: 8, 921: 8, 925: 4, 926: 6, 936: 8, 941: 8, 949: 8, 952: 8, 953: 6, 968: 8, 969: 6, 970: 8, 971: 8, 977: 8, 984: 8, 987: 8, 990: 8, 1000: 8, 1001: 8, 1006: 8, 1007: 8, 1008: 8, 1010: 6, 1014: 1, 1015: 8, 1016: 8, 1017: 8, 1018: 8, 1020: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1, 1033: 1, 1034: 8, 1048: 1, 1049: 8, 1061: 8, 1064: 8, 1065: 8, 1070: 8, 1080: 8, 1081: 8, 1097: 8, 1113: 8, 1129: 8, 1145: 8, 1160: 4, 1177: 8, 1281: 8, 1328: 8, 1329: 8, 1332: 8, 1335: 8, 1337: 8, 1353: 8, 1368: 8, 1412: 8, 1436: 8, 1476: 8, 1481: 8, 1497: 8, 1513: 8, 1519: 8, 1601: 8, 1605: 8, 1617: 8, 1621: 8, 1625: 8, 1665: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1824: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8, 1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2015: 8, 2043: 5, 2045: 4
}, },
], ],
CAR.AP1_MODELS: [ CAR.AP1_MODELS: [

@ -71,6 +71,7 @@ routes = [
TestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX), TestRoute("f34a60d68d83b1e5|2020-10-06--14-35-55", HONDA.ACURA_RDX),
TestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE), TestRoute("54fd8451b3974762|2021-04-01--14-50-10", HONDA.RIDGELINE),
TestRoute("2d5808fae0b38ac6|2021-09-01--17-14-11", HONDA.HONDA_E), 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("6fe86b4e410e4c37|2020-07-22--16-27-13", HYUNDAI.HYUNDAI_GENESIS),
TestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70), TestRoute("70c5bec28ec8e345|2020-08-08--12-22-23", HYUNDAI.GENESIS_G70),
@ -84,7 +85,7 @@ routes = [
TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS), TestRoute("c75a59efa0ecd502|2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), TestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), TestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON_DIESEL_2019), TestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), TestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), TestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
TestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019), TestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019),
@ -144,6 +145,7 @@ routes = [
TestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH), TestRoute("ec429c0f37564e3c|2020-02-01--17-28-12", TOYOTA.LEXUS_NXH),
TestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX), TestRoute("964c09eb11ca8089|2020-11-03--22-04-00", TOYOTA.LEXUS_NX),
TestRoute("3fd5305f8b6ca765|2021-04-28--19-26-49", TOYOTA.LEXUS_NX_TSS2), 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("0a302ffddbb3e3d3|2020-02-08--16-19-08", TOYOTA.HIGHLANDER_TSS2),
TestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2), TestRoute("437e4d2402abf524|2021-05-25--07-58-50", TOYOTA.HIGHLANDERH_TSS2),
TestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER), TestRoute("3183cd9b021e89ce|2021-05-25--10-34-44", TOYOTA.HIGHLANDER),

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math
import unittest import unittest
import importlib import importlib
from parameterized import parameterized from parameterized import parameterized
@ -32,14 +33,16 @@ class TestCarInterfaces(unittest.TestCase):
assert car_interface assert car_interface
self.assertGreater(car_params.mass, 1) self.assertGreater(car_params.mass, 1)
self.assertGreater(car_params.steerRateCost, 1e-3) self.assertGreater(car_params.maxLateralAccel, 0)
if car_params.steerControlType != car.CarParams.SteerControlType.angle: if car_params.steerControlType != car.CarParams.SteerControlType.angle:
tuning = car_params.lateralTuning.which() tuning = car_params.lateralTuning.which()
if tuning == 'pid': if tuning == 'pid':
self.assertTrue(len(car_params.lateralTuning.pid.kpV)) self.assertTrue(len(car_params.lateralTuning.pid.kpV))
elif tuning == 'torque': elif tuning == 'torque':
self.assertTrue(car_params.lateralTuning.torque.kf > 0) kf = car_params.lateralTuning.torque.kf
self.assertTrue(not math.isnan(kf) and kf > 0)
self.assertTrue(not math.isnan(car_params.lateralTuning.torque.friction))
elif tuning == 'indi': elif tuning == 'indi':
self.assertTrue(len(car_params.lateralTuning.indi.outerLoopGainV)) self.assertTrue(len(car_params.lateralTuning.indi.outerLoopGainV))

@ -3,6 +3,7 @@ import unittest
from selfdrive.car.car_helpers import interfaces, get_interface_attr 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 import CARS_MD_OUT, CARS_MD_TEMPLATE, generate_cars_md, get_all_car_info
from selfdrive.car.docs_definitions import Column, Star
class TestCarDocs(unittest.TestCase): class TestCarDocs(unittest.TestCase):
@ -15,17 +16,18 @@ class TestCarDocs(unittest.TestCase):
current_cars_md = f.read() current_cars_md = f.read()
self.assertEqual(generated_cars_md, current_cars_md, self.assertEqual(generated_cars_md, current_cars_md,
"Run selfdrive/car/docs.py to generate new supported cars documentation") "Run selfdrive/car/docs.py to update the compatibility documentation")
def test_missing_car_info(self): def test_missing_car_info(self):
all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys() all_car_info_platforms = get_interface_attr("CAR_INFO", combine_brands=True).keys()
for platform in sorted(interfaces.keys()): for platform in sorted(interfaces.keys()):
if platform not in all_car_info_platforms: with self.subTest(platform=platform):
self.fail("Platform: {} doesn't exist in CarInfo".format(platform)) self.assertTrue(platform in all_car_info_platforms, "Platform: {} doesn't exist in CarInfo".format(platform))
def test_naming_conventions(self): def test_naming_conventions(self):
# Asserts market-standard car naming conventions by make # Asserts market-standard car naming conventions by brand
for car in self.all_cars: for car in self.all_cars:
with self.subTest(car=car):
tokens = car.model.lower().split(" ") tokens = car.model.lower().split(" ")
if car.car_name == "hyundai": if car.car_name == "hyundai":
self.assertNotIn("phev", tokens, "Use `Plug-in Hybrid`") self.assertNotIn("phev", tokens, "Use `Plug-in Hybrid`")
@ -37,6 +39,15 @@ class TestCarDocs(unittest.TestCase):
if "rav4" in tokens: if "rav4" in tokens:
self.assertIn("RAV4", car.model, "Use correct capitalization") self.assertIn("RAV4", car.model, "Use correct capitalization")
def test_torque_star(self):
# Asserts brand-specific assumptions around steering torque star
for car in self.all_cars:
with self.subTest(car=car):
if car.car_name == "honda":
self.assertIn(car.row[Column.STEERING_TORQUE], (Star.EMPTY, Star.HALF), f"{car.name} has full torque star")
elif car.car_name in ("toyota", "hyundai"):
self.assertNotEqual(car.row[Column.STEERING_TORQUE], Star.EMPTY, f"{car.name} has no torque star")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -121,7 +121,6 @@ class TestCarModelBase(unittest.TestCase):
# make sure car params are within a valid range # make sure car params are within a valid range
self.assertGreater(self.CP.mass, 1) self.assertGreater(self.CP.mass, 1)
self.assertGreater(self.CP.steerRateCost, 1e-3)
if self.CP.steerControlType != car.CarParams.SteerControlType.angle: if self.CP.steerControlType != car.CarParams.SteerControlType.angle:
tuning = self.CP.lateralTuning.which() tuning = self.CP.lateralTuning.which()
@ -222,6 +221,7 @@ class TestCarModelBase(unittest.TestCase):
# TODO: check rest of panda's carstate (steering, ACC main on, etc.) # TODO: check rest of panda's carstate (steering, ACC main on, etc.)
checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev() checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev()
checks['cruiseState'] += CS.cruiseState.enabled and not CS.cruiseState.available
# TODO: remove this exception once this mismatch is resolved # TODO: remove this exception once this mismatch is resolved
brake_pressed = CS.brakePressed brake_pressed = CS.brakePressed

@ -0,0 +1,29 @@
legend: [LAT_ACCEL_FACTOR, MAX_LAT_ACCEL_MEASURED, FRICTION]
### angle control
# Nissan appears to have torque
NISSAN X-TRAIL 2017: [.nan, 1.5, .nan]
NISSAN ALTIMA 2020: [.nan, 1.5, .nan]
NISSAN LEAF 2018 Instrument Cluster: [.nan, 1.5, .nan]
NISSAN LEAF 2018: [.nan, 1.5, .nan]
NISSAN ROGUE 2019: [.nan, 1.5, .nan]
# Tesla has high torque
TESLA AP1 MODEL S: [.nan, 2.5, .nan]
TESLA AP2 MODEL S: [.nan, 2.5, .nan]
# Guess
FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan]
FORD FOCUS 4TH GEN: [.nan, 1.5, .nan]
###
# No steering wheel
COMMA BODY: [.nan, 1000, .nan]
# Totally new car
KIA EV6 2022: [3.0, 2.5, 0.0]
# Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0]
# Manually checked
HONDA CIVIC 2022: [2.5, 1.2, 0.15]

@ -0,0 +1,96 @@
ACURA ILX 2016: [1.524988973896102, 0.519011053086259, 0.34236219253028]
ACURA RDX 2018: [0.9987728568686902, 0.5323765166196301, 0.303218805715844]
ACURA RDX 2020: [1.4314459806646749, 0.33874701282109954, 0.18048847083897598]
AUDI A3 3RD GEN: [1.5122414863077502, 1.7443517531719404, 0.15194151892450905]
AUDI Q3 2ND GEN: [1.4439223359448605, 1.2254955789112076, 0.1413798895978097]
CHEVROLET VOLT PREMIER 2017: [1.5961527626411784, 1.8422651988094612, 0.1572393918005158]
CHRYSLER PACIFICA 2018: [1.593387270257916, 1.3366521181047952, 0.13776367250652022]
CHRYSLER PACIFICA 2020: [1.4323553627965695, 1.509076559398423, 0.14328246159386085]
CHRYSLER PACIFICA HYBRID 2017: [1.3032470208409048, 1.06831764583744, 0.13287170990024627]
CHRYSLER PACIFICA HYBRID 2018: [1.6068280248761635, 1.2943025830995154, 0.1358557824293823]
CHRYSLER PACIFICA HYBRID 2019: [1.4624643614072217, 1.1958788168371808, 0.15748488008472716]
GENESIS G70 2018: [3.8520195946707947, 2.354697063349854, 0.06830285485626221]
GMC ACADIA DENALI 2018: [1.3181430320331884, 1.1853735340610179, 0.3450592280031644]
HONDA ACCORD 2018: [1.7135052593468778, 0.3461280068322071, 0.21579936052863807]
HONDA ACCORD HYBRID 2018: [1.6651615004829625, 0.30322180951193245, 0.2083000440586149]
HONDA CIVIC (BOSCH) 2019: [1.691708637466905, 0.40132900729454185, 0.25460295304024094]
HONDA CIVIC 2016: [1.6528895627785531, 0.4018518740819229, 0.25458812851328544]
HONDA CR-V 2016: [0.7667141440182675, 0.5927571534745969, 0.40909087636157127]
HONDA CR-V 2017: [2.01323205142022, 0.2700612209345081, 0.2238412881331528]
HONDA CR-V HYBRID 2019: [2.072034634644233, 0.7152085160516978, 0.20237105008376083]
HONDA FIT 2018: [1.5719981427109775, 0.5712761407108976, 0.110773383324281]
HONDA HRV 2019: [2.0661212805710205, 0.7521343418694775, 0.17760375789242094]
HONDA INSIGHT 2019: [1.5201671214069354, 0.5660229120683284, 0.25808042580281876]
HONDA ODYSSEY 2018: [1.8774809275211801, 0.8394431662987996, 0.2096978613792822]
HONDA PASSPORT 2021: [1.5305538930036766, 0.7956063674638759, 0.19599407381531284]
HONDA PILOT 2017: [1.7262026201812795, 0.9470005614967523, 0.21351430733218763]
HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328]
HYUNDAI GENESIS 2015-2016: [1.8466226943929824, 1.5552063647830634, 0.0984484465421171]
HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276]
HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778]
HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482]
HYUNDAI KONA ELECTRIC 2019: [4.398306735170212, 3.2961956260770484, 0.08651833437845884]
HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651]
HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945]
HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963]
HYUNDAI SANTA FE PlUG-IN HYBRID 2022: [1.6953050513611045, 1.5837614296206861, 0.12672855941458458]
HYUNDAI SONATA 2019: [2.2200457811703953, 1.2967330275895228, 0.14039920986586393]
HYUNDAI SONATA 2020: [3.284505627881726, 2.1259108157250735, 0.08452048323586728]
HYUNDAI SONATA HYBRID 2021: [2.8990264092395734, 2.061410192222139, 0.0899805488717382]
JEEP GRAND CHEROKEE 2019: [1.7321233388827006, 1.289689569171081, 0.15046331002097185]
JEEP GRAND CHEROKEE V6 2018: [1.8776598027756923, 1.4057367824262523, 0.11725947414922003]
KIA K5 2021: [2.405339728085138, 1.460032270828705, 0.11650989850813716]
KIA NIRO EV 2020: [2.9215954981365337, 2.1500583840260044, 0.09236802474810267]
KIA SORENTO GT LINE 2018: [2.464854685101844, 1.5335274218367956, 0.12056170567599558]
KIA STINGER GT2 2018: [2.7499043387418967, 1.849652021986449, 0.12048334239559202]
LEXUS ES 2019: [2.0203086922726112, 2.134803912579666, 0.12757526789308554]
LEXUS ES HYBRID 2019: [2.392442298703042, 1.863360677810788, 0.17690002108856212]
LEXUS NX 2018: [2.302625600642627, 2.1382378491466625, 0.14986840878892838]
LEXUS NX 2020: [2.4331999786982936, 2.1045680431705414, 0.14099899317761067]
LEXUS NX HYBRID 2018: [2.4025593501080955, 1.8080446063815507, 0.15349361249519017]
LEXUS RX 2016: [1.5876816543130423, 1.0427699298523752, 0.21334066732397142]
LEXUS RX 2020: [1.5228812994274734, 1.431102486563665, 0.2093316728710659]
LEXUS RX HYBRID 2017: [1.6984261557042386, 1.3211501880159107, 0.1820354534928893]
LEXUS RX HYBRID 2020: [1.5522309889823778, 1.255230465866663, 0.2220954003055114]
MAZDA CX-9 2021: [1.7601682915983443, 1.0889677335154337, 0.17713792194297195]
SKODA SUPERB 3RD GEN: [1.166437404652981, 1.1686163012668165, 0.12194533036948708]
SUBARU FORESTER 2019: [3.6617001649776793, 2.342197172531713, 0.11075960785398745]
SUBARU IMPREZA LIMITED 2019: [1.0670704910352047, 0.8234374840709592, 0.20986563268614938]
SUBARU IMPREZA SPORT 2020: [2.6068223389108303, 2.134872342760203, 0.15261513193561627]
TOYOTA AVALON 2016: [2.5185770183845646, 1.7153346784214922, 0.10603968787111022]
TOYOTA AVALON 2019: [1.7036141952825095, 1.239619084240008, 0.08459830394899492]
TOYOTA AVALON 2022: [2.3154403649717357, 2.7777922854327124, 0.11453999639164605]
TOYOTA C-HR 2018: [1.5591084333664578, 1.271271459066948, 0.20259087058453193]
TOYOTA C-HR 2021: [1.7678810166088303, 1.3742176337919942, 0.2319674583741509]
TOYOTA CAMRY 2018: [2.1172995371905015, 1.7156177222420887, 0.13519250664782062]
TOYOTA CAMRY 2021: [2.6922769557433055, 2.3476510120007434, 0.1450430192989234]
TOYOTA CAMRY HYBRID 2018: [2.0974120828287774, 1.7996193116697359, 0.13823613467632756]
TOYOTA CAMRY HYBRID 2021: [2.6426668350384457, 2.3901492458927986, 0.16103875108816076]
TOYOTA COROLLA 2017: [3.117154369115421, 1.8438132575043773, 0.12289685869250652]
TOYOTA COROLLA HYBRID TSS2 2019: [2.3287672277252005, 1.8118712531729109, 0.2215868445753317]
TOYOTA COROLLA TSS2 2019: [2.4204464833010175, 1.9258612322678952, 0.20670411068012526]
TOYOTA HIGHLANDER 2017: [1.8696367437248915, 1.626293990451463, 0.17485372210240796]
TOYOTA HIGHLANDER 2020: [2.022340166827233, 1.6183134804881791, 0.14592306380054457]
TOYOTA HIGHLANDER HYBRID 2018: [1.9421825202382728, 1.6433903296845025, 0.16928956792275918]
TOYOTA HIGHLANDER HYBRID 2020: [2.103373061114133, 2.104015182965606, 0.14447040132184993]
TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565]
TOYOTA PRIUS 2017: [2.0183401513314294, 1.5023147650693636, 0.20856908464957724]
TOYOTA PRIUS TSS2 2021: [2.327639738920072, 1.9104337425537743, 0.2030762265549664]
TOYOTA RAV4 2017: [2.085695074355425, 2.2142832316984733, 0.13339165270103975]
TOYOTA RAV4 2019: [2.5038362866776835, 2.0993589721530252, 0.1552425356342368]
TOYOTA RAV4 2019 8965: [2.5084506298290377, 2.4216520504763475, 0.11992835265067918]
TOYOTA RAV4 2019 x02: [2.7209621987605024, 2.2148637653781593, 0.10862567142268198]
TOYOTA RAV4 HYBRID 2017: [1.9796257271652042, 1.7503987331707576, 0.14628860048885406]
TOYOTA RAV4 HYBRID 2019: [2.2271858492309153, 2.074844961405639, 0.14382216826893632]
TOYOTA RAV4 HYBRID 2019 8965: [2.1077397198131336, 1.8162215166877735, 0.13891369391200137]
TOYOTA RAV4 HYBRID 2019 x02: [2.803624333289342, 2.272367966572498, 0.11364569214387774]
TOYOTA RAV4 HYBRID 2022: [2.241883248393209, 1.9304407208090029, 0.1565442715453653]
TOYOTA RAV4 HYBRID 2022 x02: [3.044930631831037, 2.3979189796380918, 0.14023209146703736]
TOYOTA SIENNA 2018: [1.8660896232147548, 1.3208264576110418, 0.18799149615227198]
VOLKSWAGEN ARTEON 1ST GEN: [1.45136518053819, 1.3639364049316804, 0.23806361745695032]
VOLKSWAGEN ATLAS 1ST GEN: [1.4677006726964945, 1.6733266634075656, 0.12959584092073367]
VOLKSWAGEN GOLF 7TH GEN: [1.3750394140491293, 1.5814743077200641, 0.2018321939386586]
VOLKSWAGEN JETTA 7TH GEN: [1.2271623034089392, 1.216955117387, 0.19437384688370712]
VOLKSWAGEN PASSAT 8TH GEN: [1.3432120736752917, 1.7087275587362314, 0.19444383787326647]
VOLKSWAGEN TIGUAN 2ND GEN: [0.9711965500094828, 1.0001565939459098, 0.1465626137072916]
legend: [LAT_ACCEL_FACTOR, MAX_LAT_ACCEL_MEASURED, FRICTION]

@ -0,0 +1,75 @@
MAZDA 3: MAZDA CX-9 2021
MAZDA 6: MAZDA CX-9 2021
MAZDA CX-5: MAZDA CX-9 2021
MAZDA CX-5 2022: MAZDA CX-9 2021
MAZDA CX-9: MAZDA CX-9 2021
TOYOTA ALPHARD HYBRID 2021 : TOYOTA SIENNA 2018
TOYOTA ALPHARD 2020: TOYOTA SIENNA 2018
TOYOTA PRIUS v 2017 : TOYOTA PRIUS 2017
TOYOTA RAV4 2022: TOYOTA RAV4 HYBRID 2022
TOYOTA C-HR HYBRID 2018: TOYOTA C-HR 2018
LEXUS IS 2018: LEXUS NX 2018
LEXUS CT HYBRID 2018 : LEXUS NX 2018
LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018
LEXUS NX HYBRID 2020: LEXUS NX 2020
LEXUS RC 2020: LEXUS NX 2020
TOYOTA AVALON HYBRID 2019: TOYOTA AVALON 2019
TOYOTA AVALON HYBRID 2022: TOYOTA AVALON 2022
KIA OPTIMA SX 2019 & 2016: HYUNDAI SONATA 2020
KIA OPTIMA HYBRID 2017 & SPORTS 2019: HYUNDAI SONATA 2020
KIA FORTE E 2018 & GT 2021: HYUNDAI SONATA 2020
KIA CEED INTRO ED 2019: HYUNDAI SONATA 2020
KIA SELTOS 2021: HYUNDAI SONATA 2020
KIA NIRO HYBRID 2019: KIA NIRO EV 2020
KIA NIRO HYBRID 2021: KIA NIRO EV 2020
HYUNDAI VELOSTER 2019: HYUNDAI SONATA 2019
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 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
HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019
HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020
HYUNDAI ELANTRA 2021: HYUNDAI SONATA 2020
HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019
HYUNDAI SANTA FE 2022: HYUNDAI SANTA FE HYBRID 2022
GENESIS G90 2017: GENESIS G70 2018
GENESIS G80 2017: GENESIS G70 2018
GENESIS G70 2020: HYUNDAI SONATA 2020
HONDA FREED 2020: HONDA ODYSSEY 2018
HONDA CR-V EU 2016: HONDA CR-V 2016
HONDA CIVIC SEDAN 1.6 DIESEL 2019: HONDA CIVIC (BOSCH) 2019
HONDA E 2020: HONDA CIVIC (BOSCH) 2019
HONDA ODYSSEY CHN 2019: HONDA ODYSSEY 2018
BUICK REGAL ESSENCE 2018: CHEVROLET VOLT PREMIER 2017
CADILLAC ESCALADE ESV 2016: CHEVROLET VOLT PREMIER 2017
CADILLAC ATS Premium Performance 2018: CHEVROLET VOLT PREMIER 2017
CHEVROLET MALIBU PREMIER 2017: CHEVROLET VOLT PREMIER 2017
HOLDEN ASTRA RS-V BK 2017: CHEVROLET VOLT PREMIER 2017
SKODA OCTAVIA 3RD GEN: SKODA SUPERB 3RD GEN
SKODA SCALA 1ST GEN: SKODA SUPERB 3RD GEN
SKODA KODIAQ 1ST GEN: SKODA SUPERB 3RD GEN
SKODA KAROQ 1ST GEN: SKODA SUPERB 3RD GEN
SKODA KAMIQ 1ST GEN: SKODA SUPERB 3RD GEN
VOLKSWAGEN T-ROC 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN
VOLKSWAGEN T-CROSS 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN
VOLKSWAGEN TOURAN 2ND GEN: VOLKSWAGEN TIGUAN 2ND GEN
VOLKSWAGEN TRANSPORTER T6.1: VOLKSWAGEN TIGUAN 2ND GEN
AUDI Q2 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN
VOLKSWAGEN TAOS 1ST GEN: VOLKSWAGEN TIGUAN 2ND GEN
VOLKSWAGEN POLO 6TH GEN: VOLKSWAGEN GOLF 7TH GEN
SEAT LEON 3RD GEN: VOLKSWAGEN GOLF 7TH GEN
SEAT ATECA 1ST GEN: VOLKSWAGEN GOLF 7TH 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
SUBARU FORESTER 2017 - 2018: SUBARU IMPREZA LIMITED 2019
SUBARU LEGACY 2015 - 2018: SUBARU IMPREZA LIMITED 2019
SUBARU ASCENT LIMITED 2019: SUBARU FORESTER 2019

@ -31,6 +31,8 @@ class CarInterface(CarInterfaceBase):
ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop
stop_and_go = False stop_and_go = False
steering_angle_deadzone_deg = 0.0
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
if candidate == CAR.PRIUS: if candidate == CAR.PRIUS:
stop_and_go = True stop_and_go = True
@ -38,8 +40,11 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.74 # unknown end-to-end spec ret.steerRatio = 15.74 # unknown end-to-end spec
tire_stiffness_factor = 0.6371 # hand-tune tire_stiffness_factor = 0.6371 # hand-tune
ret.mass = 3045. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3045. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 1.7 # Only give steer angle deadzone to for bad angle sensor prius
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.06) for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
steering_angle_deadzone_deg = 1.0
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
elif candidate == CAR.PRIUS_V: elif candidate == CAR.PRIUS_V:
stop_and_go = True stop_and_go = True
@ -47,8 +52,7 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 17.4 ret.steerRatio = 17.4
tire_stiffness_factor = 0.5533 tire_stiffness_factor = 0.5533
ret.mass = 3340. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3340. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 1.8 CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.06)
elif candidate in (CAR.RAV4, CAR.RAV4H): elif candidate in (CAR.RAV4, CAR.RAV4H):
stop_and_go = True if (candidate in CAR.RAV4H) else False stop_and_go = True if (candidate in CAR.RAV4H) else False
@ -56,16 +60,12 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16.88 # 14.5 is spec end-to-end ret.steerRatio = 16.88 # 14.5 is spec end-to-end
tire_stiffness_factor = 0.5533 tire_stiffness_factor = 0.5533
ret.mass = 3650. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid ret.mass = 3650. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
ret.maxLateralAccel = 1.8
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.06)
elif candidate == CAR.COROLLA: elif candidate == CAR.COROLLA:
ret.wheelbase = 2.70 ret.wheelbase = 2.70
ret.steerRatio = 18.27 ret.steerRatio = 18.27
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.mass = 2860. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid ret.mass = 2860. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
ret.maxLateralAccel = 2.8
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.024)
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RXH, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2): elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RXH, CAR.LEXUS_RX_TSS2, CAR.LEXUS_RXH_TSS2):
stop_and_go = True stop_and_go = True
@ -74,7 +74,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelSpeedFactor = 1.035 ret.wheelSpeedFactor = 1.035
tire_stiffness_factor = 0.5533 tire_stiffness_factor = 0.5533
ret.mass = 4481. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max ret.mass = 4481. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
ret.maxLateralAccel = 1.4
set_lat_tune(ret.lateralTuning, LatTunes.PID_C) set_lat_tune(ret.lateralTuning, LatTunes.PID_C)
elif candidate in (CAR.CHR, CAR.CHRH): elif candidate in (CAR.CHR, CAR.CHRH):
@ -83,7 +82,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 13.6 ret.steerRatio = 13.6
tire_stiffness_factor = 0.7933 tire_stiffness_factor = 0.7933
ret.mass = 3300. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3300. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 1.3
set_lat_tune(ret.lateralTuning, LatTunes.PID_F) set_lat_tune(ret.lateralTuning, LatTunes.PID_F)
elif candidate in (CAR.CAMRY, CAR.CAMRYH, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2): elif candidate in (CAR.CAMRY, CAR.CAMRYH, CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2):
@ -92,11 +90,7 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 13.7 ret.steerRatio = 13.7
tire_stiffness_factor = 0.7933 tire_stiffness_factor = 0.7933
ret.mass = 3400. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid ret.mass = 3400. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
if candidate in (CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2): if candidate not in (CAR.CAMRY_TSS2, CAR.CAMRYH_TSS2):
ret.maxLateralAccel = 2.4
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.05)
else:
ret.maxLateralAccel = 2.0
set_lat_tune(ret.lateralTuning, LatTunes.PID_C) set_lat_tune(ret.lateralTuning, LatTunes.PID_C)
elif candidate in (CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2): elif candidate in (CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2):
@ -105,7 +99,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16.0 ret.steerRatio = 16.0
tire_stiffness_factor = 0.8 tire_stiffness_factor = 0.8
ret.mass = 4700. * CV.LB_TO_KG + STD_CARGO_KG # 4260 + 4-5 people ret.mass = 4700. * CV.LB_TO_KG + STD_CARGO_KG # 4260 + 4-5 people
ret.maxLateralAccel = 2.0
set_lat_tune(ret.lateralTuning, LatTunes.PID_G) set_lat_tune(ret.lateralTuning, LatTunes.PID_G)
elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH): elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDERH):
@ -114,7 +107,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16.0 ret.steerRatio = 16.0
tire_stiffness_factor = 0.8 tire_stiffness_factor = 0.8
ret.mass = 4607. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid limited ret.mass = 4607. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid limited
ret.maxLateralAccel = 1.8
set_lat_tune(ret.lateralTuning, LatTunes.PID_G) set_lat_tune(ret.lateralTuning, LatTunes.PID_G)
elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALONH_2019, CAR.AVALON_TSS2, CAR.AVALONH_TSS2): elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALONH_2019, CAR.AVALON_TSS2, CAR.AVALONH_TSS2):
@ -125,7 +117,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.8 # Found at https://pressroom.toyota.com/releases/2016+avalon+product+specs.download ret.steerRatio = 14.8 # Found at https://pressroom.toyota.com/releases/2016+avalon+product+specs.download
tire_stiffness_factor = 0.7983 tire_stiffness_factor = 0.7983
ret.mass = 3505. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid ret.mass = 3505. * CV.LB_TO_KG + STD_CARGO_KG # mean between normal and hybrid
ret.maxLateralAccel = 1.6
set_lat_tune(ret.lateralTuning, LatTunes.PID_H) set_lat_tune(ret.lateralTuning, LatTunes.PID_H)
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022): elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4H_TSS2, CAR.RAV4H_TSS2_2022):
@ -134,7 +125,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.3 ret.steerRatio = 14.3
tire_stiffness_factor = 0.7933 tire_stiffness_factor = 0.7933
ret.mass = 3585. * CV.LB_TO_KG + STD_CARGO_KG # Average between ICE and Hybrid ret.mass = 3585. * CV.LB_TO_KG + STD_CARGO_KG # Average between ICE and Hybrid
ret.maxLateralAccel = 2.5
set_lat_tune(ret.lateralTuning, LatTunes.PID_D) set_lat_tune(ret.lateralTuning, LatTunes.PID_D)
# 2019+ RAV4 TSS2 uses two different steering racks and specific tuning seems to be necessary. # 2019+ RAV4 TSS2 uses two different steering racks and specific tuning seems to be necessary.
@ -150,8 +140,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 13.9 ret.steerRatio = 13.9
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.mass = 3060. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3060. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 2.0
set_lat_tune(ret.lateralTuning, LatTunes.TORQUE, MAX_LAT_ACCEL=ret.maxLateralAccel, FRICTION=0.07)
elif candidate in (CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_ESH): elif candidate in (CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_ESH):
stop_and_go = True stop_and_go = True
@ -159,7 +147,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 16.0 # not optimized ret.steerRatio = 16.0 # not optimized
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.mass = 3677. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max ret.mass = 3677. * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
ret.maxLateralAccel = 2.2
set_lat_tune(ret.lateralTuning, LatTunes.PID_D) set_lat_tune(ret.lateralTuning, LatTunes.PID_D)
elif candidate == CAR.SIENNA: elif candidate == CAR.SIENNA:
@ -168,7 +155,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 15.5 ret.steerRatio = 15.5
tire_stiffness_factor = 0.444 tire_stiffness_factor = 0.444
ret.mass = 4590. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4590. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 1.6
set_lat_tune(ret.lateralTuning, LatTunes.PID_J) set_lat_tune(ret.lateralTuning, LatTunes.PID_J)
elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_RC): elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_RC):
@ -186,13 +172,12 @@ class CarInterface(CarInterfaceBase):
ret.mass = 3108 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max ret.mass = 3108 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
set_lat_tune(ret.lateralTuning, LatTunes.PID_M) set_lat_tune(ret.lateralTuning, LatTunes.PID_M)
elif candidate in (CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2): elif candidate in (CAR.LEXUS_NX, CAR.LEXUS_NXH, CAR.LEXUS_NX_TSS2, CAR.LEXUS_NXH_TSS2):
stop_and_go = True stop_and_go = True
ret.wheelbase = 2.66 ret.wheelbase = 2.66
ret.steerRatio = 14.7 ret.steerRatio = 14.7
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.mass = 4070 * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4070 * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 2.0
set_lat_tune(ret.lateralTuning, LatTunes.PID_C) set_lat_tune(ret.lateralTuning, LatTunes.PID_C)
elif candidate == CAR.PRIUS_TSS2: elif candidate == CAR.PRIUS_TSS2:
@ -201,7 +186,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 13.4 # True steerRatio from older prius ret.steerRatio = 13.4 # True steerRatio from older prius
tire_stiffness_factor = 0.6371 # hand-tune tire_stiffness_factor = 0.6371 # hand-tune
ret.mass = 3115. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3115. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 2.0
set_lat_tune(ret.lateralTuning, LatTunes.PID_N) set_lat_tune(ret.lateralTuning, LatTunes.PID_N)
elif candidate == CAR.MIRAI: elif candidate == CAR.MIRAI:
@ -210,7 +194,6 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.8 ret.steerRatio = 14.8
tire_stiffness_factor = 0.8 tire_stiffness_factor = 0.8
ret.mass = 4300. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4300. * CV.LB_TO_KG + STD_CARGO_KG
ret.maxLateralAccel = 2.4
set_lat_tune(ret.lateralTuning, LatTunes.PID_C) set_lat_tune(ret.lateralTuning, LatTunes.PID_C)
elif candidate in (CAR.ALPHARD_TSS2, CAR.ALPHARDH_TSS2): elif candidate in (CAR.ALPHARD_TSS2, CAR.ALPHARDH_TSS2):
@ -221,7 +204,6 @@ class CarInterface(CarInterfaceBase):
ret.mass = 4305. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4305. * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_J) set_lat_tune(ret.lateralTuning, LatTunes.PID_J)
ret.steerRateCost = 1.
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
# TODO: get actual value, for now starting with reasonable value for # TODO: get actual value, for now starting with reasonable value for
@ -292,5 +274,4 @@ class CarInterface(CarInterfaceBase):
# pass in a car.CarControl # pass in a car.CarControl
# to be called @ 100hz # to be called @ 100hz
def apply(self, c): def apply(self, c):
ret = self.CC.update(c, self.CS) return self.CC.update(c, self.CS)
return ret

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from enum import Enum from enum import Enum
from selfdrive.controls.lib.latcontrol_torque import set_torque_tune
class LongTunes(Enum): class LongTunes(Enum):
PEDAL = 0 PEDAL = 0
@ -24,7 +23,6 @@ class LatTunes(Enum):
PID_L = 13 PID_L = 13
PID_M = 14 PID_M = 14
PID_N = 15 PID_N = 15
TORQUE = 16
###### LONG ###### ###### LONG ######
@ -50,10 +48,8 @@ def set_long_tune(tune, name):
###### LAT ###### ###### LAT ######
def set_lat_tune(tune, name, MAX_LAT_ACCEL=2.5, FRICTION=0.01, use_steering_angle=True): def set_lat_tune(tune, name, MAX_LAT_ACCEL=2.5, FRICTION=0.01, steering_angle_deadzone_deg=0.0, use_steering_angle=True):
if name == LatTunes.TORQUE: if 'PID' in str(name):
set_torque_tune(tune, MAX_LAT_ACCEL, FRICTION)
elif 'PID' in str(name):
tune.init('pid') tune.init('pid')
tune.pid.kiBP = [0.0] tune.pid.kiBP = [0.0]
tune.pid.kpBP = [0.0] tune.pid.kpBP = [0.0]

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save