Merge remote-tracking branch 'upstream/master' into globally-fix-buttons-transitioning-without-going-unpressed

pull/26463/head
Shane Smiskol 2 years ago
commit 85dea93e8a
  1. 2
      .github/workflows/selfdrive_tests.yaml
  2. 2
      .github/workflows/tools_tests.yaml
  3. 1
      .gitignore
  4. 2
      Dockerfile.openpilot_base_cl
  5. 31
      Jenkinsfile
  6. 26
      RELEASES.md
  7. 18
      SConstruct
  8. 2
      body
  9. 2
      cereal
  10. 1
      common/params.cc
  11. 2
      common/version.h
  12. 29
      docs/CARS.md
  13. 2
      docs/INTEGRATION.md
  14. 2
      opendbc
  15. 2
      panda
  16. 43
      poetry.lock
  17. 2
      pyproject.toml
  18. 14
      release/files_common
  19. BIN
      selfdrive/assets/fonts/JetBrainsMono-Medium.ttf
  20. 4
      selfdrive/assets/img_couch.svg
  21. 10
      selfdrive/assets/img_experimental.svg
  22. 4
      selfdrive/assets/img_experimental_grey.svg
  23. 4
      selfdrive/assets/img_experimental_white.svg
  24. 3
      selfdrive/boardd/boardd.cc
  25. 63
      selfdrive/boardd/panda.cc
  26. 13
      selfdrive/boardd/panda.h
  27. 3
      selfdrive/boardd/panda_comms.h
  28. 25
      selfdrive/boardd/spi.cc
  29. 19
      selfdrive/boardd/tests/test_boardd_usbprotocol.cc
  30. 11
      selfdrive/car/body/interface.py
  31. 13
      selfdrive/car/body/values.py
  32. 15
      selfdrive/car/chrysler/interface.py
  33. 1
      selfdrive/car/docs.py
  34. 10
      selfdrive/car/docs_definitions.py
  35. 13
      selfdrive/car/ford/interface.py
  36. 4
      selfdrive/car/gm/carstate.py
  37. 29
      selfdrive/car/gm/interface.py
  38. 30
      selfdrive/car/gm/values.py
  39. 20
      selfdrive/car/honda/carcontroller.py
  40. 9
      selfdrive/car/honda/interface.py
  41. 55
      selfdrive/car/honda/values.py
  42. 8
      selfdrive/car/hyundai/carcontroller.py
  43. 10
      selfdrive/car/hyundai/carstate.py
  44. 3
      selfdrive/car/hyundai/hyundaican.py
  45. 34
      selfdrive/car/hyundai/interface.py
  46. 72
      selfdrive/car/hyundai/values.py
  47. 43
      selfdrive/car/interfaces.py
  48. 6
      selfdrive/car/isotp_parallel_query.py
  49. 10
      selfdrive/car/mazda/interface.py
  50. 33
      selfdrive/car/mock/interface.py
  51. 20
      selfdrive/car/nissan/interface.py
  52. 14
      selfdrive/car/subaru/interface.py
  53. 8
      selfdrive/car/subaru/values.py
  54. 8
      selfdrive/car/tesla/interface.py
  55. 4
      selfdrive/car/tesla/values.py
  56. 5
      selfdrive/car/tests/routes.py
  57. 2
      selfdrive/car/tests/test_car_interfaces.py
  58. 18
      selfdrive/car/tests/test_models.py
  59. 10
      selfdrive/car/torque_data/override.yaml
  60. 6
      selfdrive/car/torque_data/params.yaml
  61. 3
      selfdrive/car/torque_data/substitute.yaml
  62. 18
      selfdrive/car/toyota/carstate.py
  63. 21
      selfdrive/car/toyota/interface.py
  64. 15
      selfdrive/car/toyota/values.py
  65. 34
      selfdrive/car/volkswagen/carstate.py
  66. 13
      selfdrive/car/volkswagen/interface.py
  67. 1
      selfdrive/car/volkswagen/pqcan.py
  68. 35
      selfdrive/car/volkswagen/values.py
  69. 79
      selfdrive/controls/controlsd.py
  70. 131
      selfdrive/controls/lib/drive_helpers.py
  71. 18
      selfdrive/controls/lib/events.py
  72. 2
      selfdrive/controls/lib/longcontrol.py
  73. 2
      selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
  74. 16
      selfdrive/controls/lib/longitudinal_planner.py
  75. 127
      selfdrive/controls/tests/test_cruise_speed.py
  76. 9
      selfdrive/controls/tests/test_following_distance.py
  77. 6
      selfdrive/controls/tests/test_state_machine.py
  78. 2
      selfdrive/debug/test_fw_query_on_routes.py
  79. 1
      selfdrive/locationd/laikad.py
  80. 12
      selfdrive/locationd/liblocationd.cc
  81. 73
      selfdrive/locationd/locationd.cc
  82. 9
      selfdrive/locationd/locationd.h
  83. 4
      selfdrive/locationd/torqued.py
  84. 1
      selfdrive/loggerd/loggerd.h
  85. 1
      selfdrive/loggerd/tests/test_logger.cc
  86. 4
      selfdrive/manager/process_config.py
  87. 6
      selfdrive/manager/test/test_manager.py
  88. 5
      selfdrive/modeld/SConscript
  89. 6
      selfdrive/modeld/models/commonmodel.h
  90. 5
      selfdrive/modeld/models/driving.cc
  91. 66
      selfdrive/modeld/models/nav.cc
  92. 73
      selfdrive/modeld/models/nav.h
  93. 3
      selfdrive/modeld/models/navmodel.onnx
  94. 3
      selfdrive/modeld/models/navmodel_q.dlc
  95. 12
      selfdrive/modeld/navmodeld
  96. 59
      selfdrive/modeld/navmodeld.cc
  97. 15
      selfdrive/monitoring/driver_monitor.py
  98. 8
      selfdrive/monitoring/test_monitoring.py
  99. 43
      selfdrive/navd/map_renderer.cc
  100. 3
      selfdrive/navd/map_renderer.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -7,7 +7,7 @@ on:
pull_request: pull_request:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }} group: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true cancel-in-progress: true
env: env:

@ -7,7 +7,7 @@ on:
pull_request: pull_request:
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }} group: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true cancel-in-progress: true
env: env:

1
.gitignore vendored

@ -52,6 +52,7 @@ selfdrive/sensord/_sensord
system/camerad/camerad system/camerad/camerad
system/camerad/test/ae_gray_test system/camerad/test/ae_gray_test
selfdrive/modeld/_modeld selfdrive/modeld/_modeld
selfdrive/modeld/_navmodeld
selfdrive/modeld/_dmonitoringmodeld selfdrive/modeld/_dmonitoringmodeld
/src/ /src/

@ -17,7 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends\
# Intel OpenCL driver # Intel OpenCL driver
ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz
ARG INTEL_DRIVER_URL=http://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532 ARG INTEL_DRIVER_URL=https://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532
RUN mkdir -p /tmp/opencl-driver-intel RUN mkdir -p /tmp/opencl-driver-intel
WORKDIR /tmp/opencl-driver-intel WORKDIR /tmp/opencl-driver-intel
RUN echo INTEL_DRIVER is $INTEL_DRIVER && \ RUN echo INTEL_DRIVER is $INTEL_DRIVER && \

31
Jenkinsfile vendored

@ -59,7 +59,7 @@ pipeline {
branch 'devel-staging' branch 'devel-staging'
} }
steps { steps {
phone_steps("tici", [ phone_steps("tici-needs-can", [
["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"], ["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
]) ])
} }
@ -111,28 +111,36 @@ pipeline {
R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}" R3_PUSH = "${env.BRANCH_NAME == 'master' ? '1' : ' '}"
} }
steps { steps {
phone_steps("tici", [ phone_steps("tici-needs-can", [
["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"], ["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR EXTRA_FILES='tools/' ./build_devel.sh"],
["build openpilot", "cd selfdrive/manager && ./build.py"], ["build openpilot", "cd selfdrive/manager && ./build.py"],
["check dirty", "release/check-dirty.sh"], ["check dirty", "release/check-dirty.sh"],
["test manager", "python selfdrive/manager/test/test_manager.py"],
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"], ["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"], ["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"],
]) ])
} }
} }
stage('loopback-tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici-loopback", [
["build openpilot", "cd selfdrive/manager && ./build.py"],
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
])
}
}
stage('HW + Unit Tests') { stage('HW + Unit Tests') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici2", [ phone_steps("tici-common", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
["test power draw", "python system/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 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"],
["test sensord", "python selfdrive/sensord/tests/test_sensord.py"],
["test pigeond", "python selfdrive/sensord/tests/test_pigeond.py"], ["test pigeond", "python selfdrive/sensord/tests/test_pigeond.py"],
["test manager", "python selfdrive/manager/test/test_manager.py"],
]) ])
} }
} }
@ -159,27 +167,32 @@ pipeline {
} }
} }
stage('sensord (LSM-C)') { stage('sensord') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici-lsmc", [ phone_steps("tici-lsmc", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd selfdrive/sensord/tests && python -m unittest test_sensord.py"], ["test sensord", "cd selfdrive/sensord/tests && python -m unittest test_sensord.py"],
]) ])
phone_steps("tici-bmx-lsm", [
["build", "cd selfdrive/manager && ./build.py"],
["test sensord", "cd selfdrive/sensord/tests && python -m unittest test_sensord.py"],
])
} }
} }
stage('replay') { stage('replay') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps { steps {
phone_steps("tici3", [ phone_steps("tici-common", [
["build", "cd selfdrive/manager && ./build.py"], ["build", "cd selfdrive/manager && ./build.py"],
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"], ["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
]) ])
} }
} }
}
} }
} }
}
} }

@ -1,35 +1,43 @@
Version 0.8.17 (2022-11-21) Version 0.9.1 (2022-12-XX)
========================
* Adjust alert volume using ambient noise level
* Removed driver monitoring timer resetting on interaction if face detected and distracted
* Chevrolet Bolt EV 2022-23 support thanks to JasonJShuler!
* Hyundai Tucson 2022-23 support
* Kia Sorento Plug-in Hybrid 2022 support thanks to sunnyhaibin!
Version 0.9.0 (2022-11-21)
======================== ========================
* New driving model * New driving model
* Internal feature space information content increased tenfold during training (to ~700 bits), which makes the model dramatically more accurate * Internal feature space information content increased tenfold during training to ~700 bits, which makes the model dramatically more accurate
* Less reliance on previous frames makes model more reactive and snappy * Less reliance on previous frames makes model more reactive and snappy
* Trained in new reprojective simulator * Trained in new reprojective simulator
* Trained in 36hrs from scratch, compared to one week for previous releases * Trained in 36 hours from scratch, compared to one week for previous releases
* Training now simulates both lateral and longitudinal behavior, which allows openpilot to slow down for turns, stop at traffic lights, and more in experimental mode * Training now simulates both lateral and longitudinal behavior, which allows openpilot to slow down for turns, stop at traffic lights, and more in experimental mode
* Driver monitoring updates
* New bigger model with added end-to-end distracted trigger
* Reduced false positives during driver calibration
* Experimental driving mode * Experimental driving mode
* End-to-end longitudinal control * End-to-end longitudinal control
* Stops for traffic lights and stop signs * Stops for traffic lights and stop signs
* Slows down for turns * Slows down for turns
* openpilot defaults to chill mode, enable experimental in settings * openpilot defaults to chill mode, enable experimental mode in settings
* Driver monitoring updates
* New bigger model with added end-to-end distracted trigger
* Reduced false positives during driver calibration
* Self-tuning torque controller: learns parameters live for each car * Self-tuning torque controller: learns parameters live for each car
* Torque controller used on all Toyota, Lexus, Hyundai, Kia, and Genesis models * Torque controller used on all Toyota, Lexus, Hyundai, Kia, and Genesis models
* UI updates * UI updates
* Multi-language in navigation
* Matched speeds shown on car's dash * Matched speeds shown on car's dash
* Multi-language in navigation
* Improved update experience * Improved update experience
* Border turns grey while overriding steering * Border turns grey while overriding steering
* Bookmark events while driving; view them in comma connect * Bookmark events while driving; view them in comma connect
* New onroad visualization for experimental mode * New onroad visualization for experimental mode
* AGNOS 6
* tools: new and improved cabana thanks to deanlee! * tools: new and improved cabana thanks to deanlee!
* Experimental longitudinal support for Volkswagen, CAN-FD Hyundai, and new GM models * Experimental longitudinal support for Volkswagen, CAN-FD Hyundai, and new GM models
* Genesis GV70 2022-23 support thanks to zunichky and sunnyhaibin! * Genesis GV70 2022-23 support thanks to zunichky and sunnyhaibin!
* Hyundai Santa Cruz 2021-22 support thanks to sunnyhaibin! * Hyundai Santa Cruz 2021-22 support thanks to sunnyhaibin!
* Kia Sportage 2023 support thanks to sunnyhaibin! * Kia Sportage 2023 support thanks to sunnyhaibin!
* Kia Sportage Hybrid 2023 support thanks to sunnyhaibin! * Kia Sportage Hybrid 2023 support thanks to sunnyhaibin!
* Kia Stinger 2022 support thanks to sunnyhaibin!
Version 0.8.16 (2022-08-26) Version 0.8.16 (2022-08-26)
======================== ========================

@ -298,12 +298,15 @@ if arch == "Darwin":
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"] qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin")) qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
else: else:
qt_env['QTDIR'] = "/usr" qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
qt_env['QTDIR'] = qt_install_prefix
qt_dirs = [ qt_dirs = [
f"/usr/include/{real_arch}-linux-gnu/qt5", f"{qt_install_headers}",
f"/usr/include/{real_arch}-linux-gnu/qt5/QtGui/5.12.8/QtGui", f"{qt_install_headers}/QtGui/5.12.8/QtGui",
] ]
qt_dirs += [f"/usr/include/{real_arch}-linux-gnu/qt5/Qt{m}" for m in qt_modules] qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
qt_libs = [f"Qt5{m}" for m in qt_modules] qt_libs = [f"Qt5{m}" for m in qt_modules]
if arch == "larch64": if arch == "larch64":
@ -406,10 +409,10 @@ if arch != "Darwin":
# build submodules # build submodules
SConscript([ SConscript([
'cereal/SConscript',
'body/board/SConscript', 'body/board/SConscript',
'panda/board/SConscript', 'cereal/SConscript',
'opendbc/can/SConscript', 'opendbc/can/SConscript',
'panda/SConscript',
]) ])
SConscript(['third_party/SConscript']) SConscript(['third_party/SConscript'])
@ -439,9 +442,6 @@ if arch in ['x86_64', 'Darwin'] or GetOption('extras'):
Export('opendbc') Export('opendbc')
SConscript(['tools/cabana/SConscript']) SConscript(['tools/cabana/SConscript'])
if GetOption('test'):
SConscript('panda/tests/safety/SConscript')
external_sconscript = GetOption('external_sconscript') external_sconscript = GetOption('external_sconscript')
if external_sconscript: if external_sconscript:
SConscript([external_sconscript]) SConscript([external_sconscript])

@ -1 +1 @@
Subproject commit 04aeb30ce0bb14759989cd374158233877e1e151 Subproject commit dc780f858c1ef641471d09b72569e199e3e10acb

@ -1 +1 @@
Subproject commit afafa0a2a537d775842ab2e1bf20cb9a33b34f9a Subproject commit 7765176413c0bb14143fe2469d5390ea0ea61a1e

@ -103,6 +103,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT}, {"DisablePowerDown", PERSISTENT},
{"ExperimentalMode", PERSISTENT}, {"ExperimentalMode", PERSISTENT},
{"ExperimentalModeConfirmed", PERSISTENT},
{"ExperimentalLongitudinalEnabled", PERSISTENT}, // WARNING: THIS MAY DISABLE AEB {"ExperimentalLongitudinalEnabled", PERSISTENT}, // WARNING: THIS MAY DISABLE AEB
{"DisableUpdates", PERSISTENT}, {"DisableUpdates", PERSISTENT},
{"DisengageOnAccelerator", PERSISTENT}, {"DisengageOnAccelerator", PERSISTENT},

@ -1 +1 @@
#define COMMA_VERSION "0.8.17" #define COMMA_VERSION "0.9.1"

@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system.
# 214 Supported Cars # 219 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness| |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|
@ -19,6 +19,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM| |Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|GM|
|Chevrolet|Volt 2017-18[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II| |Chevrolet|Volt 2017-18[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|OBD-II|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
@ -60,8 +61,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J| |Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai J|
|Hyundai|i30 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Hyundai|i30 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Hyundai|Ioniq 5 (with HDA II) 2022-23|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q| |Hyundai|Ioniq 5 (with HDA II) 2022-23|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai Q|
|Hyundai|Ioniq 5 (without HDA II) 2022-23|Highway Driving Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K| |Hyundai|Ioniq 5 (without HDA II) 2022-23|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
@ -82,14 +83,16 @@ A supported vehicle is one that just works when you install a comma three. All s
|Hyundai|Sonata 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Hyundai|Sonata 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Hyundai|Sonata Hybrid 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Hyundai|Tucson 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Hyundai|Tucson 2023|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Hyundai|Tucson Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| |Hyundai|Tucson Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA| |Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|FCA|
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Kia|EV6 (with HDA II) 2022|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P| |Kia|EV6 (with HDA II) 2022|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai P|
|Kia|EV6 (without HDA II) 2022|Highway Driving Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L| |Kia|EV6 (without HDA II) 2022|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai L|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G| |Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai G|
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
@ -104,9 +107,11 @@ A supported vehicle is one that just works when you install a comma three. All s
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A| |Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E| |Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai E|
|Kia|Sorento Plug-in Hybrid 2022-23|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai A|
|Kia|Sportage 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| |Kia|Sportage 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N| |Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai N|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C| |Kia|Stinger 2018-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai C|
|Kia|Stinger 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H| |Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
@ -136,7 +141,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Crosstrek 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A| |Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Subaru A|
@ -150,7 +155,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[<sup>9</sup>](#footnotes)| |Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533[<sup>9</sup>](#footnotes)|
|Škoda|Superb 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
@ -164,7 +169,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>5</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Camry 2018-20|All|Stock|0 mph[<sup>5</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Toyota|Camry 2021-22|All|openpilot|0 mph[<sup>5</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Camry 2021-22|All|openpilot|0 mph[<sup>5</sup>](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Toyota|Camry Hybrid 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota| |Toyota|Corolla 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota| |Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|
@ -224,8 +229,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533| |Volkswagen|Touran 2017|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,8</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|J533|
<a id="footnotes"></a> <a id="footnotes"></a>
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `master-ci`. Using openpilot longitudinal may disable Automatic Emergency Braking (AEB). <br /> <sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
<sup>2</sup>When the Driver Support Unit (DSU) is disconnected, 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>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
<sup>3</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br /> <sup>3</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br /> <sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
<sup>5</sup>openpilot operates above 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>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />

@ -8,4 +8,4 @@ Additionally, on specific supported cars (see ACC column in [supported cars](CAR
* Stock ACC is replaced by openpilot ACC. * Stock ACC is replaced by openpilot ACC.
* openpilot FCW operates in addition to stock FCW. * openpilot FCW operates in addition to stock FCW.
openpilot should preserve all other vehicle's stock features, including, but are not limited to: FCW, Automatic Emergency Braking (AEB), auto high-beam, blind spot warning, and side collision warning. openpilot should preserve all other vehicle's stock features, including, but not limited to: FCW, Automatic Emergency Braking (AEB), auto high-beam, blind spot warning, and side collision warning.

@ -1 +1 @@
Subproject commit 296f190000a2e71408e207ba21a2257cc853ec15 Subproject commit 94fff4782be263efad10032a612b3c96a120c0b7

@ -1 +1 @@
Subproject commit 3ac7536c94c560756293e299c514c03ea20d83a4 Subproject commit 4edd1a602131ec2f09a604a4bd28e7d00e334458

43
poetry.lock generated

@ -2439,6 +2439,20 @@ typing-extensions = ">=3.6.2.1"
[package.extras] [package.extras]
lint = ["clang-format (==13.0.0)", "flake8", "mypy (==0.782)", "types-protobuf (==3.18.4)"] lint = ["clang-format (==13.0.0)", "flake8", "mypy (==0.782)", "types-protobuf (==3.18.4)"]
[[package]]
name = "onnx2torch"
version = "1.5.4"
description = "Nice Onnx to Pytorch converter"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
numpy = ">=1.16.4"
onnx = ">=1.9.0"
torch = ">=1.8.0"
torchvision = ">=0.9.0"
[[package]] [[package]]
name = "onnxoptimizer" name = "onnxoptimizer"
version = "0.3.1" version = "0.3.1"
@ -3696,6 +3710,20 @@ category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "sounddevice"
version = "0.4.5"
description = "Play and Record Sound with Python"
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
CFFI = ">=1.0"
[package.extras]
numpy = ["NumPy"]
[[package]] [[package]]
name = "soupsieve" name = "soupsieve"
version = "2.3.2.post1" version = "2.3.2.post1"
@ -4386,7 +4414,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "~3.8" python-versions = "~3.8"
content-hash = "d2854112975a9d83a9540175b2d430487e40e0292d48a1ba6c591db60a08c136" content-hash = "331a7b700c17c618a4f0fa7d0ec3e5b585c1dc467f6f03261c3778f729058f55"
[metadata.files] [metadata.files]
adal = [ adal = [
@ -6388,6 +6416,9 @@ onnx = [
{file = "onnx-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:af90427ca04c6b7b8107c2021e1273227a3ef1a7a01f3073039cae7855a59833"}, {file = "onnx-1.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:af90427ca04c6b7b8107c2021e1273227a3ef1a7a01f3073039cae7855a59833"},
{file = "onnx-1.12.0.tar.gz", hash = "sha256:13b3e77d27523b9dbf4f30dfc9c959455859d5e34e921c44f712d69b8369eff9"}, {file = "onnx-1.12.0.tar.gz", hash = "sha256:13b3e77d27523b9dbf4f30dfc9c959455859d5e34e921c44f712d69b8369eff9"},
] ]
onnx2torch = [
{file = "onnx2torch-1.5.4-py3-none-any.whl", hash = "sha256:fd1a0fe05072bfb9f3d86d9330299b130b41f11bd4ae634db17078974e711725"},
]
onnxoptimizer = [ onnxoptimizer = [
{file = "onnxoptimizer-0.3.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e73a5e2e3ca4db9bff54f7131768749c861677b97ee811a136fcf1a52783cf6e"}, {file = "onnxoptimizer-0.3.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e73a5e2e3ca4db9bff54f7131768749c861677b97ee811a136fcf1a52783cf6e"},
{file = "onnxoptimizer-0.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bf2bfe0dc43f0776867688e1759122dec049ff4f45f7221931b687fe7e139e"}, {file = "onnxoptimizer-0.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8bf2bfe0dc43f0776867688e1759122dec049ff4f45f7221931b687fe7e139e"},
@ -6764,6 +6795,7 @@ pycryptodome = [
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:7c9ed8aa31c146bef65d89a1b655f5f4eab5e1120f55fc297713c89c9e56ff0b"},
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:5099c9ca345b2f252f0c28e96904643153bae9258647585e5e6f649bb7a1844a"},
{file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"}, {file = "pycryptodome-3.15.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2ec709b0a58b539a4f9d33fb8508264c3678d7edb33a68b8906ba914f71e8c13"},
{file = "pycryptodome-3.15.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:2ae53125de5b0d2c95194d957db9bb2681da8c24d0fb0fe3b056de2bcaf5d837"},
{file = "pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win32.whl", hash = "sha256:fd2184aae6ee2a944aaa49113e6f5787cdc5e4db1eb8edb1aea914bd75f33a0c"},
{file = "pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"}, {file = "pycryptodome-3.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7e3a8f6ee405b3bd1c4da371b93c31f7027944b2bcce0697022801db93120d83"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b9c5b1a1977491533dfd31e01550ee36ae0249d78aae7f632590db833a5012b8"},
@ -6771,12 +6803,14 @@ pycryptodome = [
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:2aa55aae81f935a08d5a3c2042eb81741a43e044bd8a81ea7239448ad751f763"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c3640deff4197fa064295aaac10ab49a0d55ef3d6a54ae1499c40d646655c89f"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"}, {file = "pycryptodome-3.15.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:045d75527241d17e6ef13636d845a12e54660aa82e823b3b3341bcf5af03fa79"},
{file = "pycryptodome-3.15.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:eb6fce570869e70cc8ebe68eaa1c26bed56d40ad0f93431ee61d400525433c54"},
{file = "pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"}, {file = "pycryptodome-3.15.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9ee40e2168f1348ae476676a2e938ca80a2f57b14a249d8fe0d3cdf803e5a676"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:4c3ccad74eeb7b001f3538643c4225eac398c77d617ebb3e57571a897943c667"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:1b22bcd9ec55e9c74927f6b1f69843cb256fb5a465088ce62837f793d9ffea88"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:57f565acd2f0cf6fb3e1ba553d0cb1f33405ec1f9c5ded9b9a0a5320f2c0bd3d"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4b52cb18b0ad46087caeb37a15e08040f3b4c2d444d58371b6f5d786d95534c2"},
{file = "pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"}, {file = "pycryptodome-3.15.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:092a26e78b73f2530b8bd6b3898e7453ab2f36e42fd85097d705d6aba2ec3e5e"},
{file = "pycryptodome-3.15.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:50ca7e587b8e541eb6c192acf92449d95377d1f88908c0a32ac5ac2703ebe28b"},
{file = "pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"}, {file = "pycryptodome-3.15.0-cp35-abi3-win32.whl", hash = "sha256:e244ab85c422260de91cda6379e8e986405b4f13dc97d2876497178707f87fc1"},
{file = "pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"}, {file = "pycryptodome-3.15.0-cp35-abi3-win_amd64.whl", hash = "sha256:c77126899c4b9c9827ddf50565e93955cb3996813c18900c16b2ea0474e130e9"},
{file = "pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"}, {file = "pycryptodome-3.15.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:9eaadc058106344a566dc51d3d3a758ab07f8edde013712bc8d22032a86b264f"},
@ -7515,6 +7549,13 @@ sortedcontainers = [
{file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
] ]
sounddevice = [
{file = "sounddevice-0.4.5-py3-none-any.whl", hash = "sha256:5cea4afd9412e731f50ae09a54d68b10628a604cfd56b42a976c54d424c6c39d"},
{file = "sounddevice-0.4.5-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:0875173595a8bd5a66b5a03a3d958e7b89c3b956b8befbe4491a24a3ce7784c0"},
{file = "sounddevice-0.4.5-py3-none-win32.whl", hash = "sha256:442adf53850916374a58f902200aaf9412227378181264e60c966da64be47d41"},
{file = "sounddevice-0.4.5-py3-none-win_amd64.whl", hash = "sha256:d3216c5d3d678c3301058e9aac7000879e255140c524c9ef98730091b67ea676"},
{file = "sounddevice-0.4.5.tar.gz", hash = "sha256:2fe0d41299e4f3037dad2acede4eff0666b34a1fa3da5335e47120373964bef5"},
]
soupsieve = [ soupsieve = [
{file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},

@ -49,6 +49,7 @@ sentry-sdk = "^1.6.0"
setproctitle = "^1.2.3" setproctitle = "^1.2.3"
six = "^1.16.0" six = "^1.16.0"
smbus2 = "^0.4.2" smbus2 = "^0.4.2"
sounddevice = "^0.4.5"
sympy = "^1.10.1" sympy = "^1.10.1"
timezonefinder = "^6.0.1" timezonefinder = "^6.0.1"
tqdm = "^4.64.0" tqdm = "^4.64.0"
@ -146,6 +147,7 @@ mpld3 = "^0.5.8"
msgpack-python = "^0.5.6" msgpack-python = "^0.5.6"
networkx = "~2.3" networkx = "~2.3"
nvidia-ml-py3 = "^7.352.0" nvidia-ml-py3 = "^7.352.0"
onnx2torch = "^1.5.4"
onnxoptimizer = "^0.3.1" onnxoptimizer = "^0.3.1"
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu113/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl" } opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu113/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl" }
osmium = "^3.3.0" osmium = "^3.3.0"

@ -71,6 +71,7 @@ selfdrive/rtshield.py
selfdrive/statsd.py selfdrive/statsd.py
system/logmessaged.py system/logmessaged.py
system/micd.py
system/swaglog.py system/swaglog.py
system/version.py system/version.py
@ -95,6 +96,7 @@ selfdrive/boardd/panda_comms.h
selfdrive/boardd/panda_comms.cc selfdrive/boardd/panda_comms.cc
selfdrive/boardd/set_time.py selfdrive/boardd/set_time.py
selfdrive/boardd/pandad.py selfdrive/boardd/pandad.py
selfdrive/boardd/tests/test_boardd_loopback.py
selfdrive/car/__init__.py selfdrive/car/__init__.py
selfdrive/car/docs_definitions.py selfdrive/car/docs_definitions.py
@ -215,6 +217,7 @@ system/hardware/tici/amplifier.py
system/hardware/tici/updater system/hardware/tici/updater
system/hardware/tici/iwlist.py system/hardware/tici/iwlist.py
system/hardware/pc/__init__.py system/hardware/pc/__init__.py
system/hardware/pc/hardware.h
system/hardware/pc/hardware.py system/hardware/pc/hardware.py
selfdrive/locationd/__init__.py selfdrive/locationd/__init__.py
@ -347,20 +350,28 @@ selfdrive/manager/test/test_manager.py
selfdrive/modeld/__init__.py selfdrive/modeld/__init__.py
selfdrive/modeld/SConscript selfdrive/modeld/SConscript
selfdrive/modeld/modeld.cc selfdrive/modeld/modeld.cc
selfdrive/modeld/navmodeld.cc
selfdrive/modeld/dmonitoringmodeld.cc selfdrive/modeld/dmonitoringmodeld.cc
selfdrive/modeld/constants.py selfdrive/modeld/constants.py
selfdrive/modeld/modeld selfdrive/modeld/modeld
selfdrive/modeld/navmodeld
selfdrive/modeld/dmonitoringmodeld selfdrive/modeld/dmonitoringmodeld
selfdrive/modeld/models/commonmodel.cc selfdrive/modeld/models/commonmodel.cc
selfdrive/modeld/models/commonmodel.h selfdrive/modeld/models/commonmodel.h
selfdrive/modeld/models/driving.cc selfdrive/modeld/models/driving.cc
selfdrive/modeld/models/driving.h selfdrive/modeld/models/driving.h
selfdrive/modeld/models/supercombo.onnx
selfdrive/modeld/models/dmonitoring.cc selfdrive/modeld/models/dmonitoring.cc
selfdrive/modeld/models/dmonitoring.h selfdrive/modeld/models/dmonitoring.h
selfdrive/modeld/models/supercombo.onnx
selfdrive/modeld/models/dmonitoring_model_q.dlc selfdrive/modeld/models/dmonitoring_model_q.dlc
selfdrive/modeld/models/nav.cc
selfdrive/modeld/models/nav.h
selfdrive/modeld/models/navmodel_q.dlc
selfdrive/modeld/transforms/loadyuv.cc selfdrive/modeld/transforms/loadyuv.cc
selfdrive/modeld/transforms/loadyuv.h selfdrive/modeld/transforms/loadyuv.h
selfdrive/modeld/transforms/loadyuv.cl selfdrive/modeld/transforms/loadyuv.cl
@ -483,6 +494,7 @@ cereal/visionipc/*.pxd
panda/.gitignore panda/.gitignore
panda/__init__.py panda/__init__.py
panda/SConscript
panda/board/** panda/board/**
panda/certs/** panda/certs/**
panda/crypto/** panda/crypto/**

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<path d="M 278.364 122.673 C 282.129 126.545 284.418 132.064 284.5 137.465 L 284.5 200.562 C 284.419 205.963 282.129 211.48 278.364 215.352 C 274.492 219.119 268.972 221.407 263.571 221.488 L 259.262 221.488 L 259.262 238.417 C 259.128 241.171 257.425 244.12 255.107 245.614 C 252.655 246.875 249.249 246.874 246.796 245.613 C 244.48 244.12 242.777 241.171 242.643 238.417 L 242.643 221.488 L 57.357 221.488 L 57.357 238.417 C 57.223 241.171 55.519 244.121 53.203 245.614 C 50.75 246.875 47.344 246.875 44.892 245.614 C 42.575 244.12 40.872 241.171 40.738 238.417 L 40.738 221.488 L 36.427 221.488 C 31.027 221.407 25.508 219.119 21.636 215.352 C 17.871 211.48 15.582 205.961 15.5 200.56 L 15.5 137.463 C 15.581 132.062 17.871 126.545 21.636 122.673 C 25.508 118.906 31.029 116.618 36.429 116.536 L 40.738 116.536 L 40.738 87.024 C 40.426 78.719 43.434 70.271 48.95 64.028 C 54.579 57.887 62.818 53.986 71.131 53.441 L 228.982 53.444 C 237.295 53.989 245.421 57.888 251.05 64.028 C 256.566 70.272 259.574 78.719 259.262 87.024 L 259.262 116.536 L 263.573 116.536 C 268.974 116.618 274.492 118.906 278.364 122.673 Z M 60.911 75.581 C 58.147 78.782 56.932 82.617 57.35 86.825 L 57.357 118.356 C 60.791 119.893 63.643 122.022 66.115 125.423 C 68.512 128.877 69.916 133.256 69.976 137.46 L 69.976 167.014 L 230.024 167.014 L 230.024 137.466 C 230.084 133.262 231.488 128.877 233.884 125.423 C 236.357 122.021 239.208 119.892 242.643 118.356 L 242.643 86.988 C 243.06 82.779 241.852 78.782 239.089 75.581 C 236.438 72.283 232.951 70.432 228.785 70.059 L 71.215 70.059 C 67.049 70.433 63.563 72.283 60.911 75.581 Z M 33.383 203.605 C 34.241 204.572 35.14 204.944 36.431 204.87 L 53.357 204.87 L 53.357 137.465 C 53.432 136.174 53.06 135.278 52.093 134.42 C 51.236 133.454 50.336 133.08 49.046 133.155 L 36.429 133.155 C 35.139 133.08 34.295 133.506 33.382 134.419 C 32.47 135.332 32.044 136.176 32.119 137.466 L 32.119 200.56 C 32.044 201.851 32.416 202.747 33.383 203.605 Z M 69.976 204.87 L 230.024 204.87 L 230.024 183.631 L 69.976 183.631 L 69.976 204.87 Z M 267.881 137.465 C 267.956 136.174 267.584 135.278 266.618 134.419 C 265.758 133.453 264.86 133.08 263.57 133.155 L 250.953 133.155 C 249.663 133.08 248.766 133.452 247.908 134.419 C 246.941 135.277 246.568 136.176 246.643 137.466 L 246.643 204.87 L 263.571 204.87 C 264.862 204.944 265.759 204.572 266.616 203.606 C 267.583 202.748 267.956 201.849 267.881 200.559 L 267.881 137.465 Z" fill="black" style="stroke-width: 4px; vector-effect: non-scaling-stroke; fill-opacity: 0.4;"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@ -0,0 +1,10 @@
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="url(#paint0_radial_1352_397)"/>
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="#E11C1C"/>
<defs>
<radialGradient id="paint0_radial_1352_397" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(150 150) rotate(90) scale(125)">
<stop offset="0.135417" stop-color="#E11C1C"/>
<stop offset="1" stop-color="#FF8C22"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,4 @@
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="black" fill-opacity="0.36"/>
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="black" fill-opacity="0.36"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,4 @@
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M182.019 181.218C123.429 239.83 67.543 258.769 54.9241 245.243C51.3182 241.636 51.3182 236.226 51.3182 231.717C51.3182 207.37 74.7548 160.481 124.331 114.489C129.74 109.98 129.74 101.864 125.233 96.454C120.725 91.0438 112.612 91.0438 107.204 95.5525C95.4866 106.373 85.5713 117.194 76.5569 128.016C51.3181 89.242 45.9096 61.2866 54.0213 52.2701C63.9367 42.351 100.894 50.4672 144.162 82.9291C157.683 92.8482 171.203 104.57 183.823 117.196C193.738 127.115 203.654 137.936 211.767 148.757C202.754 160.48 192.838 170.399 182.021 181.22L182.019 181.218ZM246.019 54.0753C255.033 63.0928 248.724 91.9498 225.287 128.918C217.174 118.999 209.062 109.982 200.048 100.063C191.034 91.0451 181.119 82.0274 171.204 73.9125C209.064 49.5654 237.006 45.0578 246.019 54.0753ZM264.048 36.04C243.316 15.3002 200.047 24.318 150.471 58.5842C100.894 24.317 56.7244 15.3005 35.9947 36.04C15.2627 56.7799 24.2771 99.1619 59.4313 149.66C38.6993 179.417 26.0793 208.272 26.0793 231.718C26.0793 247.949 32.3888 257.868 36.896 263.278C45.0091 271.394 54.9244 275 67.5433 275C105.403 275 158.583 241.635 200.049 200.157C209.063 191.14 218.077 181.22 225.288 171.301C249.625 209.176 255.034 237.128 246.02 246.144C238.809 253.357 218.076 250.653 193.739 238.028C187.43 235.322 180.218 237.127 176.613 243.438C173.008 249.75 175.712 256.965 182.022 260.571C200.951 270.49 218.979 274.999 232.501 274.999C245.121 274.999 255.036 271.391 263.148 263.277C283.88 242.537 274.866 199.252 240.612 149.657C272.163 105.473 286.583 59.4852 264.05 36.0397L264.048 36.04Z" fill="white"/>
<path d="M175.414 154.169C175.414 171.6 161.29 185.729 143.866 185.729C126.441 185.729 112.317 171.6 112.317 154.169C112.317 136.738 126.441 122.609 143.866 122.609C161.29 122.609 175.414 136.738 175.414 154.169Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -528,9 +528,6 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
cnt++; cnt++;
sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update? sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update?
// Other pandas don't have fan/IR to control
if (panda->hw_type != cereal::PandaState::PandaType::UNO && panda->hw_type != cereal::PandaState::PandaType::DOS) continue;
if (sm.updated("deviceState") && !no_fan_control) { if (sm.updated("deviceState") && !no_fan_control) {
// Fan speed // Fan speed
uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired(); uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired();

@ -6,7 +6,6 @@
#include <stdexcept> #include <stdexcept>
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "panda/board/dlc_to_len.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "common/util.h" #include "common/util.h"
@ -24,7 +23,8 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) {
(hw_type != cereal::PandaState::PandaType::GREY_PANDA)); (hw_type != cereal::PandaState::PandaType::GREY_PANDA));
has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) || has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) ||
(hw_type == cereal::PandaState::PandaType::DOS); (hw_type == cereal::PandaState::PandaType::DOS) ||
(hw_type == cereal::PandaState::PandaType::TRES);
return; return;
} }
@ -169,22 +169,15 @@ static uint8_t len_to_dlc(uint8_t len) {
} }
} }
static void write_packet(uint8_t *dest, int *write_pos, const uint8_t *src, size_t size) {
for (int i = 0, &pos = *write_pos; i < size; ++i, ++pos) {
// Insert counter every 64 bytes (first byte of 64 bytes USB packet)
if (pos % USBPACKET_MAX_SIZE == 0) {
dest[pos] = pos / USBPACKET_MAX_SIZE;
pos++;
}
dest[pos] = src[i];
}
}
void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data_list, void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data_list,
std::function<void(uint8_t *, size_t)> write_func) { std::function<void(uint8_t *, size_t)> write_func) {
int32_t pos = 0; int32_t pos = 0;
uint8_t send_buf[2 * USB_TX_SOFT_LIMIT]; uint8_t send_buf[2 * USB_TX_SOFT_LIMIT];
uint32_t magic = CAN_TRANSACTION_MAGIC;
memcpy(&send_buf[0], &magic, sizeof(uint32_t));
pos += sizeof(uint32_t);
for (auto cmsg : can_data_list) { for (auto cmsg : can_data_list) {
// check if the message is intended for this panda // check if the message is intended for this panda
uint8_t bus = cmsg.getSrc(); uint8_t bus = cmsg.getSrc();
@ -202,16 +195,19 @@ void Panda::pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data
header.data_len_code = data_len_code; header.data_len_code = data_len_code;
header.bus = bus - bus_offset; header.bus = bus - bus_offset;
write_packet(send_buf, &pos, (uint8_t *)&header, sizeof(can_header)); memcpy(&send_buf[pos], (uint8_t *)&header, sizeof(can_header));
write_packet(send_buf, &pos, (uint8_t *)can_data.begin(), can_data.size()); pos += sizeof(can_header);
memcpy(&send_buf[pos], (uint8_t *)can_data.begin(), can_data.size());
pos += can_data.size();
if (pos >= USB_TX_SOFT_LIMIT) { if (pos >= USB_TX_SOFT_LIMIT) {
write_func(send_buf, pos); write_func(send_buf, pos);
pos = 0; pos = sizeof(uint32_t);
} }
} }
// send remaining packets // send remaining packets
if (pos > 0) write_func(send_buf, pos); if (pos > sizeof(uint32_t)) write_func(send_buf, pos);
} }
void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) { void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
@ -234,33 +230,38 @@ bool Panda::can_receive(std::vector<can_frame>& out_vec) {
} }
bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec) { bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec) {
recv_buf.clear(); if (size < sizeof(uint32_t)) {
for (int i = 0; i < size; i += USBPACKET_MAX_SIZE) { return true;
if (data[i] != i / USBPACKET_MAX_SIZE) { }
LOGE("CAN: MALFORMED USB RECV PACKET");
uint32_t magic;
memcpy(&magic, &data[0], sizeof(uint32_t));
if (magic != CAN_TRANSACTION_MAGIC) {
LOGE("CAN recv: buffer didn't start with magic");
handle->comms_healthy = false; handle->comms_healthy = false;
return false; return false;
} }
int chunk_len = std::min(USBPACKET_MAX_SIZE, (size - i));
recv_buf.insert(recv_buf.end(), &data[i + 1], &data[i + chunk_len]);
}
int pos = 0; int pos = sizeof(uint32_t);
while (pos < recv_buf.size()) { while (pos < size) {
can_header header; can_header header;
memcpy(&header, &recv_buf[pos], CANPACKET_HEAD_SIZE); memcpy(&header, &data[pos], sizeof(can_header));
can_frame &canData = out_vec.emplace_back(); can_frame &canData = out_vec.emplace_back();
canData.busTime = 0; canData.busTime = 0;
canData.address = header.addr; canData.address = header.addr;
canData.src = header.bus + bus_offset; canData.src = header.bus + bus_offset;
if (header.rejected) { canData.src += CANPACKET_REJECTED; } if (header.rejected) {
if (header.returned) { canData.src += CANPACKET_RETURNED; } canData.src += CAN_REJECTED_BUS_OFFSET;
}
if (header.returned) {
canData.src += CAN_RETURNED_BUS_OFFSET;
}
const uint8_t data_len = dlc_to_len[header.data_len_code]; const uint8_t data_len = dlc_to_len[header.data_len_code];
canData.dat.assign((char *)&recv_buf[pos + CANPACKET_HEAD_SIZE], data_len); canData.dat.assign((char *)&data[pos + sizeof(can_header)], data_len);
pos += CANPACKET_HEAD_SIZE + data_len; pos += sizeof(can_header) + data_len;
} }
return true; return true;
} }

@ -11,20 +11,16 @@
#include "cereal/gen/cpp/car.capnp.h" #include "cereal/gen/cpp/car.capnp.h"
#include "cereal/gen/cpp/log.capnp.h" #include "cereal/gen/cpp/log.capnp.h"
#include "panda/board/health.h" #include "panda/board/health.h"
#include "panda/board/can_definitions.h"
#include "selfdrive/boardd/panda_comms.h" #include "selfdrive/boardd/panda_comms.h"
#define PANDA_CAN_CNT 3
#define PANDA_BUS_CNT 4
#define USB_TX_SOFT_LIMIT (0x100U) #define USB_TX_SOFT_LIMIT (0x100U)
#define USBPACKET_MAX_SIZE (0x40) #define USBPACKET_MAX_SIZE (0x40)
#define RECV_SIZE (0x4000U) #define RECV_SIZE (0x4000U)
#define CANPACKET_HEAD_SIZE 5U
#define CANPACKET_MAX_SIZE 72U #define CAN_REJECTED_BUS_OFFSET 0xC0U
#define CANPACKET_REJECTED (0xC0U) #define CAN_RETURNED_BUS_OFFSET 0x80U
#define CANPACKET_RETURNED (0x80U)
struct __attribute__((packed)) can_header { struct __attribute__((packed)) can_header {
uint8_t reserved : 1; uint8_t reserved : 1;
@ -47,7 +43,6 @@ struct can_frame {
class Panda { class Panda {
private: private:
std::unique_ptr<PandaCommsHandle> handle; std::unique_ptr<PandaCommsHandle> handle;
std::vector<uint8_t> recv_buf;
public: public:
Panda(std::string serial="", uint32_t bus_offset=0); Panda(std::string serial="", uint32_t bus_offset=0);

@ -13,8 +13,6 @@
#define TIMEOUT 0 #define TIMEOUT 0
#define SPI_BUF_SIZE 1024 #define SPI_BUF_SIZE 1024
const bool PANDA_NO_RETRY = getenv("PANDA_NO_RETRY");
// comms base class // comms base class
class PandaCommsHandle { class PandaCommsHandle {
@ -52,7 +50,6 @@ public:
private: private:
libusb_context *ctx = NULL; libusb_context *ctx = NULL;
libusb_device_handle *dev_handle = NULL; libusb_device_handle *dev_handle = NULL;
std::vector<uint8_t> recv_buf;
void handle_usb_issue(int err, const char func[]); void handle_usb_issue(int err, const char func[]);
}; };

@ -6,6 +6,7 @@
#include <cstring> #include <cstring>
#include "common/util.h" #include "common/util.h"
#include "common/timing.h"
#include "common/swaglog.h" #include "common/swaglog.h"
#include "panda/board/comms_definitions.h" #include "panda/board/comms_definitions.h"
#include "selfdrive/boardd/panda_comms.h" #include "selfdrive/boardd/panda_comms.h"
@ -24,6 +25,9 @@ struct __attribute__((packed)) spi_header {
uint16_t max_rx_len; uint16_t max_rx_len;
}; };
const int SPI_MAX_RETRIES = 5;
const int SPI_ACK_TIMEOUT = 50; // milliseconds
PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) { PandaSpiHandle::PandaSpiHandle(std::string serial) : PandaCommsHandle(serial) {
LOGD("opening SPI panda: %s", serial.c_str()); LOGD("opening SPI panda: %s", serial.c_str());
@ -109,7 +113,7 @@ int PandaSpiHandle::bulk_read(unsigned char endpoint, unsigned char* data, int l
int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len) { int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len) {
std::lock_guard lk(hw_lock); std::lock_guard lk(hw_lock);
const int xfer_size = 0x40; const int xfer_size = 0x40 * 15;
int ret = 0; int ret = 0;
uint16_t length = (tx_data != NULL) ? tx_len : rx_len; uint16_t length = (tx_data != NULL) ? tx_len : rx_len;
@ -119,7 +123,8 @@ int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t t
int len = std::min(xfer_size, tx_len - (xfer_size * i)); int len = std::min(xfer_size, tx_len - (xfer_size * i));
d = spi_transfer_retry(endpoint, tx_data + (xfer_size * i), len, NULL, 0); d = spi_transfer_retry(endpoint, tx_data + (xfer_size * i), len, NULL, 0);
} else { } else {
d = spi_transfer_retry(endpoint, NULL, 0, rx_data + (xfer_size * i), xfer_size); uint16_t to_read = std::min(xfer_size, rx_len - ret);
d = spi_transfer_retry(endpoint, NULL, 0, rx_data + (xfer_size * i), to_read);
} }
if (d < 0) { if (d < 0) {
@ -137,15 +142,11 @@ int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t t
return ret; return ret;
} }
std::vector<std::string> PandaSpiHandle::list() { std::vector<std::string> PandaSpiHandle::list() {
// TODO: list all pandas available over SPI // TODO: list all pandas available over SPI
return {}; return {};
} }
void add_checksum(uint8_t *data, int data_len) { void add_checksum(uint8_t *data, int data_len) {
data[data_len] = SPI_CHECKSUM_START; data[data_len] = SPI_CHECKSUM_START;
for (int i=0; i < data_len; i++) { for (int i=0; i < data_len; i++) {
@ -165,17 +166,19 @@ bool check_checksum(uint8_t *data, int data_len) {
int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len) { int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len) {
int ret; int ret;
int count = SPI_MAX_RETRIES;
std::lock_guard lk(hw_lock); std::lock_guard lk(hw_lock);
do { do {
// TODO: handle error // TODO: handle error
ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len); ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len);
} while (ret < 0 && connected && !PANDA_NO_RETRY); count--;
} while (ret < 0 && connected && count > 0);
return ret; return ret;
} }
int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) { int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) {
// TODO: add timeout? double start_millis = millis_since_boot();
while (true) { while (true) {
int ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer); int ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
if (ret < 0) { if (ret < 0) {
@ -189,6 +192,12 @@ int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) {
LOGW("SPI: got NACK"); LOGW("SPI: got NACK");
return -1; return -1;
} }
// handle timeout
if (millis_since_boot() - start_millis > SPI_ACK_TIMEOUT) {
LOGE("SPI: timed out waiting for ACK");
return -1;
}
} }
return 0; return 0;

@ -6,8 +6,6 @@
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "selfdrive/boardd/panda.h" #include "selfdrive/boardd/panda.h"
const unsigned char dlc_to_len[] = {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 12U, 16U, 20U, 24U, 32U, 48U, 64U};
int random_int(int min, int max) { int random_int(int min, int max) {
std::random_device dev; std::random_device dev;
std::mt19937 rng(dev()); std::mt19937 rng(dev());
@ -50,7 +48,7 @@ PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState
can.setAddress(i); can.setAddress(i);
can.setSrc(random_int(0, 3) + bus_offset); can.setSrc(random_int(0, 3) + bus_offset);
can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size())); can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size()));
total_pakets_size += CANPACKET_HEAD_SIZE + dat.size(); total_pakets_size += sizeof(can_header) + dat.size();
} }
can_data_list = can_list.asReader(); can_data_list = can_list.asReader();
@ -60,14 +58,11 @@ PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState
void PandaTest::test_can_send() { void PandaTest::test_can_send() {
std::vector<uint8_t> unpacked_data; std::vector<uint8_t> unpacked_data;
this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) { this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) {
int size_left = size; uint32_t magic;
for (int i = 0, counter = 0; i < size; i += USBPACKET_MAX_SIZE, counter++) { memcpy(&magic, &chunk[0], sizeof(uint32_t));
REQUIRE(chunk[i] == counter);
const int len = std::min(USBPACKET_MAX_SIZE, size_left); REQUIRE(magic == CAN_TRANSACTION_MAGIC);
unpacked_data.insert(unpacked_data.end(), &chunk[i + 1], &chunk[i + len]); unpacked_data.insert(unpacked_data.end(), &chunk[sizeof(uint32_t)], &chunk[size]);
size_left -= len;
}
}); });
REQUIRE(unpacked_data.size() == total_pakets_size); REQUIRE(unpacked_data.size() == total_pakets_size);
@ -75,9 +70,9 @@ void PandaTest::test_can_send() {
INFO("test can message integrity"); INFO("test can message integrity");
for (int pos = 0, pckt_len = 0; pos < unpacked_data.size(); pos += pckt_len) { for (int pos = 0, pckt_len = 0; pos < unpacked_data.size(); pos += pckt_len) {
can_header header; can_header header;
memcpy(&header, &unpacked_data[pos], CANPACKET_HEAD_SIZE); memcpy(&header, &unpacked_data[pos], sizeof(can_header));
const uint8_t data_len = dlc_to_len[header.data_len_code]; const uint8_t data_len = dlc_to_len[header.data_len_code];
pckt_len = CANPACKET_HEAD_SIZE + data_len; pckt_len = sizeof(can_header) + data_len;
REQUIRE(header.addr == cnt); REQUIRE(header.addr == cnt);
REQUIRE(test_data.find(data_len) != test_data.end()); REQUIRE(test_data.find(data_len) != test_data.end());

@ -2,16 +2,13 @@
import math import math
from cereal import car from cereal import car
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import scale_rot_inertia, scale_tire_stiffness, get_safety_config from selfdrive.car import get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.body.values import SPEED_FROM_RPM from selfdrive.car.body.values import SPEED_FROM_RPM
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=None, car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.notCar = True ret.notCar = True
ret.carName = "body" ret.carName = "body"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.body)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.body)]
@ -31,10 +28,6 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = True ret.openpilotLongitudinalControl = True
ret.steerControlType = car.CarParams.SteerControlType.angle ret.steerControlType = car.CarParams.SteerControlType.angle
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret return ret
def _update(self, c): def _update(self, c):

@ -26,6 +26,12 @@ CAR_INFO: Dict[str, CarInfo] = {
CAR.BODY: CarInfo("comma body", package="All"), CAR.BODY: CarInfo("comma body", package="All"),
} }
FINGERPRINTS = {
CAR.BODY: [{
513: 8, 516: 8, 514: 3, 515: 4,
}],
}
FW_QUERY_CONFIG = FwQueryConfig( FW_QUERY_CONFIG = FwQueryConfig(
requests=[ requests=[
Request( Request(
@ -40,10 +46,13 @@ FW_VERSIONS = {
CAR.BODY: { CAR.BODY: {
(Ecu.engine, 0x720, None): [ (Ecu.engine, 0x720, None): [
b'0.0.01', b'0.0.01',
b'02/27/2022' b'02/27/2022',
b'0.3.00a',
], ],
# git hash of the firmware used
(Ecu.debug, 0x721, None): [ (Ecu.debug, 0x721, None): [
b'166bd860' # git hash of the firmware used b'166bd860',
b'dc780f85',
], ],
}, },
} }

@ -1,21 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, get_safety_config
from selfdrive.car.chrysler.values import CAR, DBC, RAM_HD, RAM_DT from selfdrive.car.chrysler.values import CAR, DBC, RAM_HD, RAM_DT
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "chrysler" ret.carName = "chrysler"
ret.dashcamOnly = candidate in RAM_HD ret.dashcamOnly = candidate in RAM_HD
ret.radarOffCan = DBC[candidate]['radar'] is None ret.radarOffCan = DBC[candidate]['radar'] is None
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
@ -75,14 +72,6 @@ class CarInterface(CarInterfaceBase):
raise ValueError(f"Unsupported car: {candidate}") raise ValueError(f"Unsupported car: {candidate}")
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
# starting with reasonable value for civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
ret.enableBsm = 720 in fingerprint[0] ret.enableBsm = 720 in fingerprint[0]
return ret return ret

@ -40,6 +40,7 @@ def get_all_car_info() -> List[CarInfo]:
for _car_info in car_info: for _car_info in car_info:
if not hasattr(_car_info, "row"): if not hasattr(_car_info, "row"):
_car_info.init_make(CP)
_car_info.init(CP, footnotes) _car_info.init(CP, footnotes)
all_car_info.append(_car_info) all_car_info.append(_car_info)

@ -73,12 +73,11 @@ CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only"], default
class CommonFootnote(Enum): class CommonFootnote(Enum):
EXP_LONG_AVAIL = CarFootnote( EXP_LONG_AVAIL = CarFootnote(
"Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `master-ci`. " + "Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. ",
"Using openpilot longitudinal may disable Automatic Emergency Braking (AEB).",
Column.LONGITUDINAL, docs_only=True) Column.LONGITUDINAL, docs_only=True)
EXP_LONG_DSU = CarFootnote( EXP_LONG_DSU = CarFootnote(
"When the Driver Support Unit (DSU) is disconnected, openpilot Adaptive Cruise Control (ACC) will replace " + "By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace " +
"stock Adaptive Cruise Control (ACC). <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b>", "stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b>",
Column.LONGITUDINAL) Column.LONGITUDINAL)
@ -173,6 +172,9 @@ class CarInfo:
return self return self
def init_make(self, CP: car.CarParams):
"""CarInfo subclasses can add make-specific logic for harness selection, footnotes, etc."""
def get_detail_sentence(self, CP): def get_detail_sentence(self, CP):
if not CP.notCar: if not CP.notCar:
sentence_builder = "openpilot upgrades your <strong>{car_model}</strong> with automated lane centering{alc} and adaptive cruise control{acc}." sentence_builder = "openpilot upgrades your <strong>{car_model}</strong> with automated lane centering{alc} and adaptive cruise control{acc}."

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, get_safety_config
from selfdrive.car.ford.values import CAR, Ecu, TransmissionType, GearShifter from selfdrive.car.ford.values import CAR, Ecu, TransmissionType, GearShifter
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
@ -10,12 +10,7 @@ CarParams = car.CarParams
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
if car_fw is None:
car_fw = []
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "ford" ret.carName = "ford"
ret.dashcamOnly = True ret.dashcamOnly = True
ret.safetyConfigs = [get_safety_config(CarParams.SafetyModel.ford)] ret.safetyConfigs = [get_safety_config(CarParams.SafetyModel.ford)]
@ -24,7 +19,6 @@ class CarInterface(CarInterfaceBase):
ret.steerControlType = CarParams.SteerControlType.angle ret.steerControlType = CarParams.SteerControlType.angle
ret.steerActuatorDelay = 0.4 ret.steerActuatorDelay = 0.4
ret.steerLimitTimer = 1.0 ret.steerLimitTimer = 1.0
tire_stiffness_factor = 1.0
if candidate == CAR.ESCAPE_MK4: if candidate == CAR.ESCAPE_MK4:
ret.wheelbase = 2.71 ret.wheelbase = 2.71
@ -60,10 +54,7 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
ret.autoResumeSng = ret.minEnableSpeed == -1. ret.autoResumeSng = ret.minEnableSpeed == -1.
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor)
return ret return ret
def _update(self, c): def _update(self, c):

@ -100,6 +100,9 @@ class CarState(CarStateBase):
if self.CP.networkLocation == NetworkLocation.fwdCamera: if self.CP.networkLocation == NetworkLocation.fwdCamera:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0 ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
# openpilot controls nonAdaptive when not pcmCruise
if self.CP.pcmCruise:
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
return ret return ret
@ -112,6 +115,7 @@ class CarState(CarStateBase):
("AEBCmdActive", "AEBCmd"), ("AEBCmdActive", "AEBCmd"),
("RollingCounter", "ASCMLKASteeringCmd"), ("RollingCounter", "ASCMLKASteeringCmd"),
("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"), ("ACCSpeedSetpoint", "ASCMActiveCruiseControlStatus"),
("ACCCruiseState", "ASCMActiveCruiseControlStatus"),
] ]
checks += [ checks += [
("AEBCmd", 10), ("AEBCmd", 10),

@ -4,7 +4,7 @@ from math import fabs
from panda import Panda from panda import Panda
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car import STD_CARGO_KG, create_button_events, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, create_button_events, scale_tire_stiffness, get_safety_config
from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR from selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
@ -44,8 +44,7 @@ class CarInterface(CarInterfaceBase):
return CarInterfaceBase.get_steer_feedforward_default return CarInterfaceBase.get_steer_feedforward_default
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
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.autoResumeSng = False ret.autoResumeSng = False
@ -98,7 +97,7 @@ class CarInterface(CarInterfaceBase):
# 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
# added to selfdrive/car/tests/routes.py, we can remove it from this list. # added to selfdrive/car/tests/routes.py, we can remove it from this list.
ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX, CAR.BOLT_EV} ret.dashcamOnly = candidate in {CAR.CADILLAC_ATS, CAR.HOLDEN_ASTRA, CAR.MALIBU, CAR.BUICK_REGAL, CAR.EQUINOX}
# Start with a baseline 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.
# Some GMs need some tolerance above 10 kph to avoid a fault # Some GMs need some tolerance above 10 kph to avoid a fault
@ -171,7 +170,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.000045 ret.lateralTuning.pid.kf = 0.000045
tire_stiffness_factor = 1.0 tire_stiffness_factor = 1.0
elif candidate in (CAR.BOLT_EV, CAR.BOLT_EUV): elif candidate == CAR.BOLT_EUV:
ret.mass = 1669. + STD_CARGO_KG ret.mass = 1669. + STD_CARGO_KG
ret.wheelbase = 2.63779 ret.wheelbase = 2.63779
ret.steerRatio = 16.8 ret.steerRatio = 16.8
@ -195,10 +194,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.4 ret.centerToFront = ret.wheelbase * 0.4
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
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,
@ -210,18 +205,16 @@ class CarInterface(CarInterfaceBase):
def _update(self, c): def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam, self.cp_loopback) ret = self.CS.update(self.cp, self.cp_cam, self.cp_loopback)
button_events = [] ret.buttonEvents = create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT,
for be in create_button_events(self.CS.cruise_buttons, self.CS.prev_cruise_buttons, BUTTONS_DICT, unpressed_btn=CruiseButtons.UNPRESS, init_btn=CruiseButtons.INIT)
unpressed_btn=CruiseButtons.UNPRESS, init_btn=CruiseButtons.INIT):
# Suppress resume button if we're resuming from stop so we don't adjust speed.
if be.type == ButtonType.accelCruise and (ret.cruiseState.enabled and ret.standstill):
be.type = ButtonType.unknown
button_events.append(be)
ret.buttonEvents = button_events
# The ECM allows enabling on falling edge of set, but only rising edge of resume
events = self.create_common_events(ret, extra_gears=[GearShifter.sport, GearShifter.low, events = self.create_common_events(ret, extra_gears=[GearShifter.sport, GearShifter.low,
GearShifter.eco, GearShifter.manumatic], GearShifter.eco, GearShifter.manumatic],
pcm_enable=self.CP.pcmCruise) pcm_enable=self.CP.pcmCruise, enable_buttons=(ButtonType.decelCruise,))
if not self.CP.pcmCruise:
if any(b.type == ButtonType.accelCruise and b.pressed for b in ret.buttonEvents):
events.add(EventName.buttonEnable)
# Enabling at a standstill with brake is allowed # Enabling at a standstill with brake is allowed
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs # TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs

@ -1,5 +1,5 @@
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Dict, List, Union from typing import Dict, List, Union
@ -68,7 +68,6 @@ class CAR:
ACADIA = "GMC ACADIA DENALI 2018" ACADIA = "GMC ACADIA DENALI 2018"
BUICK_REGAL = "BUICK REGAL ESSENCE 2018" BUICK_REGAL = "BUICK REGAL ESSENCE 2018"
ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016" ESCALADE_ESV = "CADILLAC ESCALADE ESV 2016"
BOLT_EV = "CHEVROLET BOLT EV 2022"
BOLT_EUV = "CHEVROLET BOLT EUV 2022" BOLT_EUV = "CHEVROLET BOLT EUV 2022"
SILVERADO = "CHEVROLET SILVERADO 1500 2020" SILVERADO = "CHEVROLET SILVERADO 1500 2020"
EQUINOX = "CHEVROLET EQUINOX 2019" EQUINOX = "CHEVROLET EQUINOX 2019"
@ -84,25 +83,32 @@ class Footnote(Enum):
@dataclass @dataclass
class GMCarInfo(CarInfo): class GMCarInfo(CarInfo):
package: str = "Adaptive Cruise Control (ACC)" package: str = "Adaptive Cruise Control (ACC)"
harness: Enum = Harness.obd_ii
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.OBD_II]) def init_make(self, CP: car.CarParams):
if CP.networkLocation == car.CarParams.NetworkLocation.fwdCamera:
self.harness = Harness.gm
else:
self.harness = Harness.obd_ii
self.footnotes.append(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"), CAR.HOLDEN_ASTRA: GMCarInfo("Holden Astra 2017"),
CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0), CAR.VOLT: GMCarInfo("Chevrolet Volt 2017-18", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ"),
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"), CAR.MALIBU: GMCarInfo("Chevrolet Malibu Premier 2017"),
CAR.ACADIA: GMCarInfo("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo"), 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", "Adaptive Cruise Control (ACC) & LKAS"), CAR.ESCALADE_ESV: GMCarInfo("Cadillac Escalade ESV 2016", "Adaptive Cruise Control (ACC) & LKAS"),
CAR.BOLT_EV: GMCarInfo("Chevrolet Bolt EV 2022-23", footnotes=[], harness=Harness.gm), CAR.BOLT_EUV: [
CAR.BOLT_EUV: GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "https://youtu.be/xvwzGMUA210", footnotes=[], harness=Harness.gm), GMCarInfo("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", "https://youtu.be/xvwzGMUA210"),
GMCarInfo("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
],
CAR.SILVERADO: [ CAR.SILVERADO: [
GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II", footnotes=[], harness=Harness.gm), GMCarInfo("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", footnotes=[], harness=Harness.gm), GMCarInfo("GMC Sierra 1500 2020-21", "Driver Alert Package II", "https://youtu.be/5HbNoBLzRwE"),
], ],
CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22", footnotes=[], harness=Harness.gm), CAR.EQUINOX: GMCarInfo("Chevrolet Equinox 2019-22"),
} }
@ -188,9 +194,9 @@ FINGERPRINTS = {
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')) DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))
EV_CAR = {CAR.VOLT, CAR.BOLT_EV, CAR.BOLT_EUV} EV_CAR = {CAR.VOLT, CAR.BOLT_EUV}
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness) # We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
CAMERA_ACC_CAR = {CAR.BOLT_EV, CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX} CAMERA_ACC_CAR = {CAR.BOLT_EUV, CAR.SILVERADO, CAR.EQUINOX}
STEER_THRESHOLD = 1.0 STEER_THRESHOLD = 1.0

@ -99,6 +99,12 @@ HUDData = namedtuple("HUDData",
"lanes_visible", "fcw", "acc_alert", "steer_required"]) "lanes_visible", "fcw", "acc_alert", "steer_required"])
def rate_limit_steer(new_steer, last_steer):
# TODO just hardcoded ramp to min/max in 0.33s for all Honda
MAX_DELTA = 3 * DT_CTRL
return clip(new_steer, last_steer - MAX_DELTA, last_steer + MAX_DELTA)
class CarController: class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
@ -116,6 +122,7 @@ class CarController:
self.speed = 0.0 self.speed = 0.0
self.gas = 0.0 self.gas = 0.0
self.brake = 0.0 self.brake = 0.0
self.last_steer = 0.0
def update(self, CC, CS): def update(self, CC, CS):
actuators = CC.actuators actuators = CC.actuators
@ -130,6 +137,10 @@ class CarController:
accel = 0.0 accel = 0.0
gas, brake = 0.0, 0.0 gas, brake = 0.0, 0.0
# *** rate limit steer ***
limited_steer = rate_limit_steer(actuators.steer, self.last_steer)
self.last_steer = limited_steer
# *** apply brake hysteresis *** # *** apply brake hysteresis ***
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady, pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady,
CS.out.vEgo, self.CP.carFingerprint) CS.out.vEgo, self.CP.carFingerprint)
@ -143,7 +154,7 @@ class CarController:
# **** process the car messages **** # **** process the car messages ****
# steer torque is converted back to CAN reference (positive when steering right) # steer torque is converted back to CAN reference (positive when steering right)
apply_steer = int(interp(-actuators.steer * self.params.STEER_MAX, apply_steer = int(interp(-limited_steer * self.params.STEER_MAX,
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V)) self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
# Send CAN commands # Send CAN commands
@ -178,14 +189,14 @@ class CarController:
clip(CS.out.vEgo + 0.0, 0.0, 100.0), clip(CS.out.vEgo + 0.0, 0.0, 100.0),
clip(CS.out.vEgo + 5.0, 0.0, 100.0)] clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V) pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V)
pcm_accel = int(1.0 * 0xc6) pcm_accel = int(1.0 * self.params.NIDEC_GAS_MAX)
else: else:
pcm_speed_V = [0.0, pcm_speed_V = [0.0,
clip(CS.out.vEgo - 2.0, 0.0, 100.0), clip(CS.out.vEgo - 2.0, 0.0, 100.0),
clip(CS.out.vEgo + 2.0, 0.0, 100.0), clip(CS.out.vEgo + 2.0, 0.0, 100.0),
clip(CS.out.vEgo + 5.0, 0.0, 100.0)] clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V) pcm_speed = interp(gas - brake, pcm_speed_BP, pcm_speed_V)
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) * self.params.NIDEC_GAS_MAX)
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message if self.frame % 2 == 0 and self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS: # radarless cars don't have supplemental message
@ -243,13 +254,14 @@ class CarController:
self.speed = pcm_speed self.speed = pcm_speed
if not self.CP.enableGasInterceptor: if not self.CP.enableGasInterceptor:
self.gas = pcm_accel / 0xc6 self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
new_actuators = actuators.copy() new_actuators = actuators.copy()
new_actuators.speed = self.speed new_actuators.speed = self.speed
new_actuators.accel = self.accel new_actuators.accel = self.accel
new_actuators.gas = self.gas new_actuators.gas = self.gas
new_actuators.brake = self.brake new_actuators.brake = self.brake
new_actuators.steer = self.last_steer
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends

@ -4,7 +4,7 @@ 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, HONDA_BOSCH_RADARLESS 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_events, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_events, scale_tire_stiffness, 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
@ -29,8 +29,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.NIDEC_ACCEL_MIN, interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS) return CarControllerParams.NIDEC_ACCEL_MIN, interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], experimental_long=False): # pylint: disable=dangerous-default-value def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "honda" ret.carName = "honda"
if candidate in HONDA_BOSCH: if candidate in HONDA_BOSCH:
@ -291,10 +290,6 @@ class CarInterface(CarInterfaceBase):
stop_and_go = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor stop_and_go = candidate in (HONDA_BOSCH | {CAR.CIVIC}) or ret.enableGasInterceptor
ret.minEnableSpeed = -1. if stop_and_go else 25.5 * CV.MPH_TO_MS ret.minEnableSpeed = -1. if stop_and_go else 25.5 * CV.MPH_TO_MS
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
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,

@ -27,6 +27,7 @@ class CarControllerParams:
NIDEC_MAX_ACCEL_V = [0.5, 2.4, 1.4, 0.6] NIDEC_MAX_ACCEL_V = [0.5, 2.4, 1.4, 0.6]
NIDEC_MAX_ACCEL_BP = [0.0, 4.0, 10., 20.] NIDEC_MAX_ACCEL_BP = [0.0, 4.0, 10., 20.]
NIDEC_GAS_MAX = 198 # 0xc6
NIDEC_BRAKE_MAX = 1024 // 4 NIDEC_BRAKE_MAX = 1024 // 4
BOSCH_ACCEL_MIN = -3.5 # m/s^2 BOSCH_ACCEL_MIN = -3.5 # m/s^2
@ -106,40 +107,46 @@ class Footnote(Enum):
class HondaCarInfo(CarInfo): class HondaCarInfo(CarInfo):
package: str = "Honda Sensing" package: str = "Honda Sensing"
def init_make(self, CP: car.CarParams):
if CP.carFingerprint in HONDA_BOSCH:
self.harness = Harness.bosch_b if CP.carFingerprint in HONDA_BOSCH_RADARLESS else Harness.bosch_a
else:
self.harness = Harness.nidec
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-22", "All", "https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Accord 2018-22", "All", "https://www.youtube.com/watch?v=mrUwlj3Mi58", min_steer_speed=3. * CV.MPH_TO_MS),
HondaCarInfo("Honda Inspire 2018", "All", 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),
], ],
CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.ACCORDH: HondaCarInfo("Honda Accord Hybrid 2018-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec, video_link="https://youtu.be/-IkImTe1NYE"), CAR.CIVIC: HondaCarInfo("Honda Civic 2016-18", min_steer_speed=12. * CV.MPH_TO_MS, video_link="https://youtu.be/-IkImTe1NYE"),
CAR.CIVIC_BOSCH: [ CAR.CIVIC_BOSCH: [
HondaCarInfo("Honda Civic 2019-21", "All", "https://www.youtube.com/watch?v=4Iz1Mz5LGF8", [Footnote.CIVIC_DIESEL], 2. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Civic 2019-21", "All", "https://www.youtube.com/watch?v=4Iz1Mz5LGF8", [Footnote.CIVIC_DIESEL], 2. * CV.MPH_TO_MS),
HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a), HondaCarInfo("Honda Civic Hatchback 2017-21", min_steer_speed=12. * CV.MPH_TO_MS),
], ],
CAR.CIVIC_BOSCH_DIESEL: None, # same platform CAR.CIVIC_BOSCH_DIESEL: None, # same platform
CAR.CIVIC_2022: [ CAR.CIVIC_2022: [
HondaCarInfo("Honda Civic 2022", "All", harness=Harness.bosch_b), HondaCarInfo("Honda Civic 2022", "All"),
HondaCarInfo("Honda Civic Hatchback 2022", "All", harness=Harness.bosch_b), HondaCarInfo("Honda Civic Hatchback 2022", "All"),
], ],
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),
CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.CRV: HondaCarInfo("Honda CR-V 2015-16", "Touring Trim", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.CRV_5G: HondaCarInfo("Honda CR-V 2017-22", min_steer_speed=12. * CV.MPH_TO_MS),
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", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.CRV_HYBRID: HondaCarInfo("Honda CR-V Hybrid 2017-19", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.FIT: HondaCarInfo("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.FREED: HondaCarInfo("Honda Freed 2020", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.HRV: HondaCarInfo("Honda HR-V 2019-22", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20", harness=Harness.nidec), CAR.ODYSSEY: HondaCarInfo("Honda Odyssey 2018-20"),
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", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.ACURA_RDX: HondaCarInfo("Acura RDX 2016-18", "AcuraWatch Plus", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.PILOT: HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.PASSPORT: HondaCarInfo("Honda Passport 2019-21", "All", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", min_steer_speed=12. * CV.MPH_TO_MS, harness=Harness.nidec), CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-22", min_steer_speed=12. * CV.MPH_TO_MS),
CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS, harness=Harness.bosch_a), CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS),
CAR.HONDA_E: HondaCarInfo("Honda e 2020", "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),
} }
FW_QUERY_CONFIG = FwQueryConfig( FW_QUERY_CONFIG = FwQueryConfig(
@ -236,7 +243,7 @@ FW_VERSIONS = {
b'39990-TVA-A340\x00\x00', b'39990-TVA-A340\x00\x00',
b'39990-TVA-X030\x00\x00', b'39990-TVA-X030\x00\x00',
b'39990-TVA-X040\x00\x00', b'39990-TVA-X040\x00\x00',
b'39990-TVA,A150\x00\x00', b'39990-TVA,A150\x00\x00', # modified firmware
b'39990-TVE-H130\x00\x00', b'39990-TVE-H130\x00\x00',
], ],
(Ecu.unknown, 0x18da3af1, None): [ (Ecu.unknown, 0x18da3af1, None): [

@ -60,9 +60,6 @@ class CarController:
# steering torque # steering torque
steer = actuators.steer steer = actuators.steer
if self.CP.carFingerprint in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
# these cars have significantly more torque than most HKG; limit to 70% of max
steer = clip(steer, -0.7, 0.7)
new_steer = int(round(steer * self.params.STEER_MAX)) new_steer = int(round(steer * self.params.STEER_MAX))
apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params) apply_steer = apply_std_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
@ -85,7 +82,7 @@ class CarController:
# *** common hyundai stuff *** # *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled) # tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and self.CP.openpilotLongitudinalControl: if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and self.CP.openpilotLongitudinalControl:
addr, bus = 0x7d0, 0 addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value: if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, 5 addr, bus = 0x730, 5
@ -122,6 +119,7 @@ class CarController:
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CP, CC.enabled)) can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CP, CC.enabled))
if self.CP.openpilotLongitudinalControl: if self.CP.openpilotLongitudinalControl:
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.frame)) can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.frame))
if self.frame % 2 == 0: if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CP, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override, can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CP, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
@ -175,7 +173,7 @@ class CarController:
if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, if self.frame % 5 == 0 and self.car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.IONIQ, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, CAR.KONA_EV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_CEED, CAR.KIA_SELTOS, CAR.KONA_EV, CAR.KONA_EV_2022,
CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.SANTA_FE_2022,
CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022): CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.GENESIS_G70_2020, CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022):
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options # 5 Hz ACC options

@ -6,7 +6,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 HyundaiFlags, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, FEATURES, CAMERA_SCC_CAR, CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
PREV_BUTTON_SAMPLES = 8 PREV_BUTTON_SAMPLES = 8
@ -197,7 +197,9 @@ class CarState(CarStateBase):
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0 ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
ret.cruiseState.available = True ret.cruiseState.available = True
self.is_metric = cp.vl["CLUSTER_INFO"]["DISTANCE_UNIT"] != 1 cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
distance_unit_msg = cruise_btn_msg if self.CP.carFingerprint == CAR.KIA_SORENTO_PHEV_4TH_GEN else "CLUSTER_INFO"
self.is_metric = cp.vl[distance_unit_msg]["DISTANCE_UNIT"] != 1
if not self.CP.openpilotLongitudinalControl: if not self.CP.openpilotLongitudinalControl:
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
@ -206,7 +208,6 @@ class CarState(CarStateBase):
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2) ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"]) self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
cruise_btn_msg = "CRUISE_BUTTONS_ALT" if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else "CRUISE_BUTTONS"
self.prev_cruise_buttons = self.cruise_buttons[-1] self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"]) self.cruise_buttons.extend(cp.vl_all[cruise_btn_msg]["CRUISE_BUTTONS"])
self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"]) self.main_buttons.extend(cp.vl_all[cruise_btn_msg]["ADAPTIVE_CRUISE_MAIN_BTN"])
@ -446,6 +447,9 @@ class CarState(CarStateBase):
("DRIVER_SEATBELT_LATCHED", "DOORS_SEATBELTS"), ("DRIVER_SEATBELT_LATCHED", "DOORS_SEATBELTS"),
] ]
if CP.carFingerprint == CAR.KIA_SORENTO_PHEV_4TH_GEN:
signals.append(("DISTANCE_UNIT", cruise_btn_msg))
checks = [ checks = [
("WHEEL_SPEEDS", 100), ("WHEEL_SPEEDS", 100),
(gear_msg, 100), (gear_msg, 100),

@ -20,7 +20,8 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE, if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022,
CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022): CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022,
CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1) values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2 values["CF_Lkas_LdwsOpt_USM"] = 2

@ -2,9 +2,9 @@
from cereal import car 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 selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons, CarControllerParams from selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, Buttons
from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR from selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from selfdrive.car import STD_CARGO_KG, create_button_events, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, create_button_events, scale_tire_stiffness, 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
@ -18,13 +18,7 @@ BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: Bu
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], experimental_long=False): # pylint: disable=dangerous-default-value
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "hyundai" ret.carName = "hyundai"
ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None ret.radarOffCan = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
@ -120,8 +114,8 @@ class CarInterface(CarInterfaceBase):
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
elif candidate == CAR.TUCSON_HYBRID_4TH_GEN: elif candidate in (CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN):
ret.mass = 1680. + STD_CARGO_KG # average of all 3 trims ret.mass = 1630. + STD_CARGO_KG # average
ret.wheelbase = 2.756 ret.wheelbase = 2.756
ret.steerRatio = 16. ret.steerRatio = 16.
tire_stiffness_factor = 0.385 tire_stiffness_factor = 0.385
@ -158,7 +152,7 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.5 tire_stiffness_factor = 0.5
if candidate == CAR.KIA_OPTIMA_G4: if candidate == CAR.KIA_OPTIMA_G4:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.KIA_STINGER: elif candidate in (CAR.KIA_STINGER, CAR.KIA_STINGER_2022):
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
@ -191,6 +185,10 @@ class CarInterface(CarInterfaceBase):
ret.mass = 1767. + STD_CARGO_KG # SX Prestige trim support only ret.mass = 1767. + STD_CARGO_KG # SX Prestige trim support only
ret.wheelbase = 2.756 ret.wheelbase = 2.756
ret.steerRatio = 13.6 ret.steerRatio = 13.6
elif candidate == CAR.KIA_SORENTO_PHEV_4TH_GEN:
ret.mass = 4095.8 * CV.LB_TO_KG + STD_CARGO_KG # weight from EX and above trims, average of FWD and AWD versions (EX, X-Line EX AWD, SX, SX Pestige, X-Line SX Prestige AWD)
ret.wheelbase = 2.81
ret.steerRatio = 13.27 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sorento-phev/2022/specifications
# Genesis # Genesis
elif candidate == CAR.GENESIS_G70: elif candidate == CAR.GENESIS_G70:
@ -219,7 +217,7 @@ class CarInterface(CarInterfaceBase):
if candidate in CANFD_CAR: if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1] ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0] ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = bool(ret.flags & HyundaiFlags.CANFD_HDA2) ret.experimentalLongitudinalAvailable = candidate in (HYBRID_CAR | EV_CAR) and candidate not in CANFD_RADAR_SCC_CAR
else: else:
ret.longitudinalTuning.kpV = [0.5] ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0] ret.longitudinalTuning.kiV = [0.0]
@ -269,11 +267,11 @@ class CarInterface(CarInterfaceBase):
elif candidate in EV_CAR: elif candidate in EV_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS
ret.centerToFront = ret.wheelbase * 0.4 if candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
ret.flags |= HyundaiFlags.ALT_LIMITS.value
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_ALT_LIMITS
# TODO: get actual value, for now starting with reasonable value for ret.centerToFront = ret.wheelbase * 0.4
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
@ -283,7 +281,7 @@ class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def init(CP, logcan, sendcan): def init(CP, logcan, sendcan):
if CP.openpilotLongitudinalControl: if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value):
addr, bus = 0x7d0, 0 addr, bus = 0x7d0, 0
if CP.flags & HyundaiFlags.CANFD_HDA2.value: if CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, 5 addr, bus = 0x730, 5

@ -39,6 +39,12 @@ class CarControllerParams:
CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO): CAR.KIA_OPTIMA_H, CAR.KIA_SORENTO):
self.STEER_MAX = 255 self.STEER_MAX = 255
# these cars have significantly more torque than most HKG; limit to 70% of max
elif CP.flags & HyundaiFlags.ALT_LIMITS:
self.STEER_MAX = 270
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
# Default for most HKG # Default for most HKG
else: else:
self.STEER_MAX = 384 self.STEER_MAX = 384
@ -50,6 +56,8 @@ class HyundaiFlags(IntFlag):
CANFD_ALT_GEARS = 4 CANFD_ALT_GEARS = 4
CANFD_CAMERA_SCC = 8 CANFD_CAMERA_SCC = 8
ALT_LIMITS = 16
class CAR: class CAR:
# Hyundai # Hyundai
@ -78,6 +86,7 @@ class CAR:
VELOSTER = "HYUNDAI VELOSTER 2019" VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021" SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
IONIQ_5 = "HYUNDAI IONIQ 5 2022" IONIQ_5 = "HYUNDAI IONIQ 5 2022"
TUCSON_4TH_GEN = "HYUNDAI TUCSON 4TH GEN"
TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN" TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN"
SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN" SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN"
@ -93,8 +102,10 @@ class CAR:
KIA_SELTOS = "KIA SELTOS 2021" KIA_SELTOS = "KIA SELTOS 2021"
KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN" KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN"
KIA_SORENTO = "KIA SORENTO GT LINE 2018" KIA_SORENTO = "KIA SORENTO GT LINE 2018"
KIA_SORENTO_PHEV_4TH_GEN = "KIA SORENTO PLUG-IN HYBRID 4TH GEN"
KIA_SPORTAGE_HYBRID_5TH_GEN = "KIA SPORTAGE HYBRID 5TH GEN" KIA_SPORTAGE_HYBRID_5TH_GEN = "KIA SPORTAGE HYBRID 5TH GEN"
KIA_STINGER = "KIA STINGER GT2 2018" KIA_STINGER = "KIA STINGER GT2 2018"
KIA_STINGER_2022 = "KIA STINGER 2022"
KIA_CEED = "KIA CEED INTRO ED 2019" KIA_CEED = "KIA CEED INTRO ED 2019"
KIA_EV6 = "KIA EV6 2022" KIA_EV6 = "KIA EV6 2022"
@ -147,11 +158,15 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e), CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, harness=Harness.hyundai_e),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a), CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness=Harness.hyundai_a),
CAR.IONIQ_5: [ CAR.IONIQ_5: [
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23" , "Highway Driving Assist", harness=Harness.hyundai_k), HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", harness=Harness.hyundai_k),
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", harness=Harness.hyundai_q), HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", harness=Harness.hyundai_q),
], ],
CAR.TUCSON_4TH_GEN: [
HyundaiCarInfo("Hyundai Tucson 2022", harness=Harness.hyundai_n),
HyundaiCarInfo("Hyundai Tucson 2023", "All", harness=Harness.hyundai_n),
],
CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n), CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022", "All", harness=Harness.hyundai_n),
CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2021-22", "Smart Cruise Control (SCC)", harness=Harness.hyundai_n), CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2021-22", harness=Harness.hyundai_n),
# Kia # Kia
CAR.KIA_FORTE: HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g), CAR.KIA_FORTE: HyundaiCarInfo("Kia Forte 2019-21", harness=Harness.hyundai_g),
@ -174,13 +189,15 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
HyundaiCarInfo("Kia Optima Hybrid 2019"), HyundaiCarInfo("Kia Optima Hybrid 2019"),
], ],
CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", harness=Harness.hyundai_a), CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", harness=Harness.hyundai_a),
CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", "Smart Cruise Control (SCC)", harness=Harness.hyundai_n), CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", harness=Harness.hyundai_n),
CAR.KIA_SORENTO: [ CAR.KIA_SORENTO: [
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c), HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", "https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_c),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e), HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness=Harness.hyundai_e),
], ],
CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "Smart Cruise Control (SCC)", harness=Harness.hyundai_a),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness=Harness.hyundai_n),
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c), CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness=Harness.hyundai_c),
CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022", "All", harness=Harness.hyundai_k),
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: [ CAR.KIA_EV6: [
HyundaiCarInfo("Kia EV6 (without HDA II) 2022", "Highway Driving Assist", harness=Harness.hyundai_l), HyundaiCarInfo("Kia EV6 (without HDA II) 2022", "Highway Driving Assist", harness=Harness.hyundai_l),
@ -790,6 +807,23 @@ FW_VERSIONS = {
b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0', b'\xf1\x00bcsh8p54 E21\x00\x00\x00\x00\x00\x00\x00SCK0T33NB0\x88\xa2\xe6\xf0',
], ],
}, },
CAR.KIA_STINGER_2022: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC F-CUP 1.00 1.00 99110-J5500 ',
],
(Ecu.engine, 0x7e0, None): [
b'\xf1\x81640R0051\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5380 4C2VR503',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT AUS RHD 1.00 1.00 99211-J5500 210622',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x87VCNLF11383972DK1vffV\x99\x99\x89\x98\x86eUU\x88wg\x89vfff\x97fff\x99\x87o\xff"\xc1\xf1\x81E30\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E30\x00\x00\x00\x00\x00\x00\x00SCK0T33GH0\xbe`\xfb\xc6',
],
},
CAR.PALISADE: { CAR.PALISADE: {
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00LX2_ SCC F-CUP 1.00 1.04 99110-S8100 ', b'\xf1\x00LX2_ SCC F-CUP 1.00 1.04 99110-S8100 ',
@ -1002,11 +1036,13 @@ FW_VERSIONS = {
b'\xf1\x8799110L2000\xf1\000DL3_ SCC FHCUP 1.00 1.03 99110-L2000 ', b'\xf1\x8799110L2000\xf1\000DL3_ SCC FHCUP 1.00 1.03 99110-L2000 ',
b'\xf1\x8799110L2100\xf1\x00DL3_ SCC F-CUP 1.00 1.03 99110-L2100 ', b'\xf1\x8799110L2100\xf1\x00DL3_ SCC F-CUP 1.00 1.03 99110-L2100 ',
b'\xf1\x8799110L2100\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ', b'\xf1\x8799110L2100\xf1\x00DL3_ SCC FHCUP 1.00 1.03 99110-L2100 ',
b'\xf1\x00DL3_ SCC F-CUP 1.00 1.03 99110-L2100 ',
], ],
(Ecu.eps, 0x7D4, None): [ (Ecu.eps, 0x7D4, None): [
b'\xf1\x8756310-L3110\xf1\000DL3 MDPS C 1.00 1.01 56310-L3110 4DLAC101', b'\xf1\x8756310-L3110\xf1\000DL3 MDPS C 1.00 1.01 56310-L3110 4DLAC101',
b'\xf1\x8756310-L3220\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101', b'\xf1\x8756310-L3220\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101',
b'\xf1\x8757700-L3000\xf1\x00DL3 MDPS R 1.00 1.02 57700-L3000 4DLAP102', b'\xf1\x8757700-L3000\xf1\x00DL3 MDPS R 1.00 1.02 57700-L3000 4DLAP102',
b'\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101',
], ],
(Ecu.fwdCamera, 0x7C4, None): [ (Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.03 99210-L3000 200915', b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.03 99210-L3000 200915',
@ -1017,6 +1053,7 @@ FW_VERSIONS = {
b'\xf1\x8758910-L3200\xf1\000DL ESC \006 101 \004\002 58910-L3200', b'\xf1\x8758910-L3200\xf1\000DL ESC \006 101 \004\002 58910-L3200',
b'\xf1\x8758910-L3800\xf1\x00DL ESC \t 101 \x07\x02 58910-L3800', b'\xf1\x8758910-L3800\xf1\x00DL ESC \t 101 \x07\x02 58910-L3800',
b'\xf1\x8758910-L3600\xf1\x00DL ESC \x03 100 \x08\x02 58910-L3600', b'\xf1\x8758910-L3600\xf1\x00DL ESC \x03 100 \x08\x02 58910-L3600',
b'\xf1\x00DL ESC \t 100 \x06\x02 58910-L3800',
], ],
(Ecu.engine, 0x7E0, None): [ (Ecu.engine, 0x7E0, None): [
b'\xf1\x87391212MKT0', b'\xf1\x87391212MKT0',
@ -1029,6 +1066,7 @@ FW_VERSIONS = {
b'\xf1\x87SALFEA6046104GK2wvwgeTeFg\x88\x96xwwwwffvfe?\xfd\xff\x86fo\xff\x97A\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL2T16NB1ia\x0b\xb8', b'\xf1\x87SALFEA6046104GK2wvwgeTeFg\x88\x96xwwwwffvfe?\xfd\xff\x86fo\xff\x97A\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL2T16NB1ia\x0b\xb8',
b'\xf1\x87SCMSAA8572454GK1\x87x\x87\x88Vf\x86hgwvwvwwgvwwgT?\xfb\xff\x97fo\xffH\xb8\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18', b'\xf1\x87SCMSAA8572454GK1\x87x\x87\x88Vf\x86hgwvwvwwgvwwgT?\xfb\xff\x97fo\xffH\xb8\xf1\x81U913\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18',
b'\xf1\x87954A02N300\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 WDL3T25XXX730NS2b\x1f\xb8%', b'\xf1\x87954A02N300\x00\x00\x00\x00\x00\xf1\x81T02730A1 \xf1\x00T02601BL T02730A1 WDL3T25XXX730NS2b\x1f\xb8%',
b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00TDL4T16NB05\x94t\x18',
], ],
}, },
CAR.KONA_EV: { CAR.KONA_EV: {
@ -1371,6 +1409,14 @@ FW_VERSIONS = {
b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00' b'\xf1\x81640F0051\x00\x00\x00\x00\x00\x00\x00\x00'
], ],
}, },
CAR.KIA_SORENTO_PHEV_4TH_GEN: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.06 99110-P4000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
]
},
CAR.KIA_EV6: { CAR.KIA_EV6: {
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ', b'\xf1\x00CV1_ RDR ----- 1.00 1.01 99110-CV000 ',
@ -1394,10 +1440,19 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614',
], ],
}, },
CAR.TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ',
],
},
CAR.TUCSON_HYBRID_4TH_GEN: { CAR.TUCSON_HYBRID_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [ (Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q', b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9240 14Q',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9220 14K', b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9220 14K',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9100 14A',
], ],
(Ecu.fwdRadar, 0x7d0, None): [ (Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ', b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
@ -1451,18 +1506,18 @@ FEATURES = {
"use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022}, "use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022},
# these cars use the FCA11 message for the AEB and FCW signals, all others use SCC12 # 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.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022}, "use_fca": {CAR.SONATA, CAR.SONATA_HYBRID, CAR.ELANTRA, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021, CAR.KIA_STINGER, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KONA_EV, CAR.KIA_FORTE, CAR.KIA_NIRO_EV, CAR.PALISADE, CAR.GENESIS_G70, CAR.GENESIS_G70_2020, CAR.KONA, CAR.SANTA_FE, CAR.KIA_SELTOS, CAR.KONA_HEV, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.TUCSON, CAR.KONA_EV_2022, CAR.KIA_STINGER_2022},
} }
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN} CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN}
# The radar does SCC on these cars when HDA I, rather than the camera # The radar does SCC on these cars when HDA I, rather than the camera
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, } CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN}
# The camera does SCC on these cars, rather than the radar # The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, } CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN} # these cars use a different gas signal HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN} # these cars use a different gas signal
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5} EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5}
# these cars require a special panda safety mode due to missing counters and checksums in the messages # these cars require a special panda safety mode due to missing counters and checksums in the messages
@ -1496,6 +1551,7 @@ DBC = {
CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None), CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None), CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_STINGER_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KONA: dbc_dict('hyundai_kia_generic', None), CAR.KONA: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None), CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None), CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None),
@ -1512,10 +1568,12 @@ DBC = {
CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None), CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_EV6: dbc_dict('hyundai_canfd', None), CAR.KIA_EV6: dbc_dict('hyundai_canfd', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'), CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.TUCSON_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None), CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None), CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None), CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None), CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: dbc_dict('hyundai_canfd', None), CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None), CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_PHEV_4TH_GEN: dbc_dict('hyundai_canfd', None),
} }

@ -10,8 +10,8 @@ from common.conversions import Conversions as CV
from common.kalman.simple_kalman import KF1D from common.kalman.simple_kalman import KF1D
from common.numpy_fast import interp from common.numpy_fast import interp
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.car import apply_hysteresis, gen_empty_fingerprint from selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness
from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, apply_deadzone from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, apply_center_deadzone
from selfdrive.controls.lib.events import Events from selfdrive.controls.lib.events import Events
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
@ -88,10 +88,34 @@ class CarInterfaceBase(ABC):
def get_pid_accel_limits(CP, current_speed, cruise_speed): def get_pid_accel_limits(CP, current_speed, cruise_speed):
return ACCEL_MIN, ACCEL_MAX return ACCEL_MIN, ACCEL_MAX
@classmethod
def get_params(cls, candidate: str, fingerprint: Optional[Dict[int, Dict[int, int]]] = None, car_fw: Optional[List[car.CarParams.CarFw]] = None, experimental_long: bool = False):
if fingerprint is None:
fingerprint = gen_empty_fingerprint()
if car_fw is None:
car_fw = list()
ret = CarInterfaceBase.get_std_params(candidate)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long)
# Set common params using fields set by the car interface
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: some car interfaces set stiffness factor
if ret.tireStiffnessFront == 0 or ret.tireStiffnessRear == 0:
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret
@staticmethod @staticmethod
@abstractmethod @abstractmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret: car.CarParams, candidate: str, fingerprint: Dict[int, Dict[int, int]], car_fw: List[car.CarParams.CarFw], experimental_long: bool):
pass raise NotImplementedError
@staticmethod @staticmethod
def init(CP, logcan, sendcan): def init(CP, logcan, sendcan):
@ -110,7 +134,7 @@ class CarInterfaceBase(ABC):
def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation): def torque_from_lateral_accel_linear(lateral_accel_value, torque_params, lateral_accel_error, lateral_accel_deadzone, friction_compensation):
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction) # The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
friction_interp = interp( friction_interp = interp(
apply_deadzone(lateral_accel_error, lateral_accel_deadzone), apply_center_deadzone(lateral_accel_error, lateral_accel_deadzone),
[-FRICTION_THRESHOLD, FRICTION_THRESHOLD], [-FRICTION_THRESHOLD, FRICTION_THRESHOLD],
[-torque_params.friction, torque_params.friction] [-torque_params.friction, torque_params.friction]
) )
@ -122,7 +146,7 @@ class CarInterfaceBase(ABC):
# returns a set of default params to avoid repetition in car specific params # returns a set of default params to avoid repetition in car specific params
@staticmethod @staticmethod
def get_std_params(candidate, fingerprint): def get_std_params(candidate):
ret = car.CarParams.new_message() ret = car.CarParams.new_message()
ret.carFingerprint = candidate ret.carFingerprint = candidate
@ -251,13 +275,8 @@ class CarInterfaceBase(ABC):
# Enable OP long on falling edge of enable buttons (defaults to accelCruise and decelCruise, overridable per-port) # Enable OP long on falling edge of enable buttons (defaults to accelCruise and decelCruise, overridable per-port)
if not self.CP.pcmCruise and (b.type in enable_buttons and not b.pressed): if not self.CP.pcmCruise and (b.type in enable_buttons and not b.pressed):
events.add(EventName.buttonEnable) events.add(EventName.buttonEnable)
# Disable on rising and falling edge of cancel for both stock and OP long
# Disable while cancel is depressed on rising edge of cancel for both stock and OP long
if b.type == ButtonType.cancel: if b.type == ButtonType.cancel:
self.cancel_depressed = b.pressed
# noEntry while cancel is depressed, matches panda
if self.cancel_depressed:
events.add(EventName.buttonCancel) events.add(EventName.buttonCancel)
# Handle permanent and temporary steering faults # Handle permanent and temporary steering faults

@ -137,17 +137,17 @@ class IsoTpParallelQuery:
else: else:
response_timeouts[tx_addr] = 0 response_timeouts[tx_addr] = 0
request_done[tx_addr] = True request_done[tx_addr] = True
cloudlog.warning(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}") cloudlog.error(f"iso-tp query bad response: {tx_addr} - 0x{dat.hex()}")
cur_time = time.monotonic() cur_time = time.monotonic()
if cur_time - max(response_timeouts.values()) > 0: 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.error(f"iso-tp query timeout after receiving response: {tx_addr}")
break break
if cur_time - start_time > total_timeout: if cur_time - start_time > total_timeout:
cloudlog.warning("iso-tp query timeout while receiving data") cloudlog.error("iso-tp query timeout while receiving data")
break break
return results return results

@ -2,7 +2,7 @@
from cereal import car from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from selfdrive.car.mazda.values import CAR, LKAS_LIMITS from selfdrive.car.mazda.values import CAR, LKAS_LIMITS
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_tire_stiffness, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
@ -11,9 +11,7 @@ EventName = car.CarEvent.EventName
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "mazda" ret.carName = "mazda"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.mazda)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.mazda)]
ret.radarOffCan = True ret.radarOffCan = True
@ -48,10 +46,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.41 ret.centerToFront = ret.wheelbase * 0.41
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
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,

@ -1,39 +1,28 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import math
from cereal import car from cereal import car
from common.conversions import Conversions as CV
from system.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 get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
# mocked car interface to work with chffrplus
TS = 0.01 # 100Hz
YAW_FR = 0.2 # ~0.8s time constant on yaw rate filter
# low pass gain
LPG = 2 * math.pi * YAW_FR * TS / (1 + 2 * math.pi * YAW_FR * TS)
# mocked car interface to work with chffrplus
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
def __init__(self, CP, CarController, CarState): def __init__(self, CP, CarController, CarState):
super().__init__(CP, CarController, CarState) super().__init__(CP, CarController, CarState)
cloudlog.debug("Using Mock Car Interface") cloudlog.debug("Using Mock Car Interface")
self.sm = messaging.SubMaster(['gyroscope', 'gpsLocation', 'gpsLocationExternal']) self.sm = messaging.SubMaster(['gpsLocation', 'gpsLocationExternal'])
self.speed = 0. self.speed = 0.
self.prev_speed = 0. self.prev_speed = 0.
self.yaw_rate = 0.
self.yaw_rate_meas = 0.
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "mock" ret.carName = "mock"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput)]
ret.mass = 1700. ret.mass = 1700.
ret.rotationalInertia = 2500.
ret.wheelbase = 2.70 ret.wheelbase = 2.70
ret.centerToFront = ret.wheelbase * 0.5 ret.centerToFront = ret.wheelbase * 0.5
ret.steerRatio = 13. # reasonable ret.steerRatio = 13. # reasonable
@ -45,11 +34,6 @@ class CarInterface(CarInterfaceBase):
# returns a car.CarState # returns a car.CarState
def _update(self, c): def _update(self, c):
self.sm.update(0) self.sm.update(0)
# get basic data from phone and gps since CAN isn't connected
if self.sm.updated['gyroscope']:
self.yaw_rate_meas = -self.sm['gyroscope'].gyroUncalibrated.v[0]
gps_sock = 'gpsLocationExternal' if self.sm.rcv_frame['gpsLocationExternal'] > 1 else 'gpsLocation' gps_sock = 'gpsLocationExternal' if self.sm.rcv_frame['gpsLocationExternal'] > 1 else 'gpsLocation'
if self.sm.updated[gps_sock]: if self.sm.updated[gps_sock]:
self.prev_speed = self.speed self.prev_speed = self.speed
@ -61,10 +45,9 @@ class CarInterface(CarInterfaceBase):
# speeds # speeds
ret.vEgo = self.speed ret.vEgo = self.speed
ret.vEgoRaw = self.speed ret.vEgoRaw = self.speed
a = self.speed - self.prev_speed
ret.aEgo = a ret.aEgo = self.speed - self.prev_speed
ret.brakePressed = a < -0.5 ret.brakePressed = ret.aEgo < -0.5
ret.standstill = self.speed < 0.01 ret.standstill = self.speed < 0.01
ret.wheelSpeeds.fl = self.speed ret.wheelSpeeds.fl = self.speed
@ -72,10 +55,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelSpeeds.rl = self.speed ret.wheelSpeeds.rl = self.speed
ret.wheelSpeeds.rr = self.speed ret.wheelSpeeds.rr = self.speed
self.yawRate = LPG * self.yaw_rate_meas + (1. - LPG) * self.yaw_rate
curvature = self.yaw_rate / max(self.speed, 1.)
ret.steeringAngleDeg = curvature * self.CP.steerRatio * self.CP.wheelbase * CV.RAD_TO_DEG
return ret return ret
def apply(self, c): def apply(self, c):

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal 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, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.nissan.values import CAR from selfdrive.car.nissan.values import CAR
@ -8,9 +8,7 @@ from selfdrive.car.nissan.values import CAR
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "nissan" ret.carName = "nissan"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.nissan)]
ret.autoResumeSng = False ret.autoResumeSng = False
@ -20,6 +18,9 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerRatio = 17 ret.steerRatio = 17
ret.steerControlType = car.CarParams.SteerControlType.angle
ret.radarOffCan = True
if candidate in (CAR.ROGUE, CAR.XTRAIL): if candidate in (CAR.ROGUE, CAR.XTRAIL):
ret.mass = 1610 + STD_CARGO_KG ret.mass = 1610 + STD_CARGO_KG
ret.wheelbase = 2.705 ret.wheelbase = 2.705
@ -35,17 +36,6 @@ class CarInterface(CarInterfaceBase):
ret.wheelbase = 2.824 ret.wheelbase = 2.824
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
ret.steerControlType = car.CarParams.SteerControlType.angle
ret.radarOffCan = True
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret return ret
# returns a car.CarState # returns a car.CarState

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, gen_empty_fingerprint, get_safety_config from selfdrive.car import STD_CARGO_KG, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.subaru.values import CAR, GLOBAL_GEN2, PREGLOBAL_CARS from selfdrive.car.subaru.values import CAR, GLOBAL_GEN2, PREGLOBAL_CARS
@ -9,9 +9,7 @@ from selfdrive.car.subaru.values import CAR, GLOBAL_GEN2, PREGLOBAL_CARS
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "subaru" ret.carName = "subaru"
ret.radarOffCan = True ret.radarOffCan = True
ret.dashcamOnly = candidate in PREGLOBAL_CARS ret.dashcamOnly = candidate in PREGLOBAL_CARS
@ -103,14 +101,6 @@ class CarInterface(CarInterfaceBase):
else: else:
raise ValueError(f"unknown car: {candidate}") raise ValueError(f"unknown car: {candidate}")
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret return ret
# returns a car.CarState # returns a car.CarState

@ -63,7 +63,7 @@ CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
], ],
CAR.IMPREZA_2020: [ CAR.IMPREZA_2020: [
SubaruCarInfo("Subaru Impreza 2020-22"), SubaruCarInfo("Subaru Impreza 2020-22"),
SubaruCarInfo("Subaru Crosstrek 2020-21"), SubaruCarInfo("Subaru Crosstrek 2020-23"),
SubaruCarInfo("Subaru XV 2020-21"), SubaruCarInfo("Subaru XV 2020-21"),
], ],
CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"), CAR.FORESTER: SubaruCarInfo("Subaru Forester 2019-21", "All"),
@ -235,12 +235,14 @@ FW_VERSIONS = {
b'\x9a\xc0\000\000', b'\x9a\xc0\000\000',
b'\n\xc0\004\000', b'\n\xc0\004\000',
b'\x9a\xc0\x04\x00', b'\x9a\xc0\x04\x00',
b'\n\xc0\x04\x01',
], ],
(Ecu.fwdCamera, 0x787, None): [ (Ecu.fwdCamera, 0x787, None): [
b'\000\000eb\037@ \"', b'\000\000eb\037@ \"',
b'\000\000e\x8f\037@ )', b'\000\000e\x8f\037@ )',
b'\x00\x00eq\x1f@ "', b'\x00\x00eq\x1f@ "',
b'\x00\x00eq\x00\x00\x00\x00', b'\x00\x00eq\x00\x00\x00\x00',
b'\x00\x00e\x8f\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xca!ap\a', b'\xca!ap\a',
@ -250,6 +252,7 @@ FW_VERSIONS = {
b'\xcc!fp\a', b'\xcc!fp\a',
b'\xca!f@\x07', b'\xca!f@\x07',
b'\xca!fp\x07', b'\xca!fp\x07',
b'\xf3"f@\x07',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xe6\xf5\004\000\000', b'\xe6\xf5\004\000\000',
@ -258,6 +261,7 @@ FW_VERSIONS = {
b'\xe7\xf5D0\000', b'\xe7\xf5D0\000',
b'\xf1\x00\xd7\x10@', b'\xf1\x00\xd7\x10@',
b'\xe6\xf5D0\x00', b'\xe6\xf5D0\x00',
b'\xe9\xf6F0\x00',
], ],
}, },
CAR.FORESTER: { CAR.FORESTER: {
@ -461,6 +465,7 @@ FW_VERSIONS = {
b'\xa1 \x08\x02', b'\xa1 \x08\x02',
b'\xa1 \x06\x02', b'\xa1 \x06\x02',
b'\xa1 \x08\x00', b'\xa1 \x08\x00',
b'\xa1 "\t\x00',
], ],
(Ecu.eps, 0x746, None): [ (Ecu.eps, 0x746, None): [
b'\x9b\xc0\x10\x00', b'\x9b\xc0\x10\x00',
@ -482,6 +487,7 @@ FW_VERSIONS = {
b'\xe2"`p\x07', b'\xe2"`p\x07',
b'\xf1\x82\xe2,\xa0@\x07', b'\xf1\x82\xe2,\xa0@\x07',
b'\xbc"`q\x07', b'\xbc"`q\x07',
b'\xe3,\xa0@\x07',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xa5\xfe\xf7@\x00', b'\xa5\xfe\xf7@\x00',

@ -2,14 +2,13 @@
from cereal import car from cereal import car
from panda import Panda from panda import Panda
from selfdrive.car.tesla.values import CANBUS, CAR from selfdrive.car.tesla.values import CANBUS, CAR
from selfdrive.car import STD_CARGO_KG, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, get_safety_config from selfdrive.car import STD_CARGO_KG, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
class CarInterface(CarInterfaceBase): class CarInterface(CarInterfaceBase):
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "tesla" ret.carName = "tesla"
# There is no safe way to do steer blending with user torque, # There is no safe way to do steer blending with user torque,
@ -51,9 +50,6 @@ class CarInterface(CarInterfaceBase):
else: else:
raise ValueError(f"Unsupported car: {candidate}") raise ValueError(f"Unsupported car: {candidate}")
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront)
return ret return ret
def _update(self, c): def _update(self, c):

@ -99,8 +99,8 @@ BUTTONS = [
Button(car.CarState.ButtonEvent.Type.rightBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [2]), Button(car.CarState.ButtonEvent.Type.rightBlinker, "STW_ACTN_RQ", "TurnIndLvr_Stat", [2]),
Button(car.CarState.ButtonEvent.Type.accelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [4, 16]), Button(car.CarState.ButtonEvent.Type.accelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [4, 16]),
Button(car.CarState.ButtonEvent.Type.decelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [8, 32]), Button(car.CarState.ButtonEvent.Type.decelCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [8, 32]),
Button(car.CarState.ButtonEvent.Type.cancel, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [2]), Button(car.CarState.ButtonEvent.Type.cancel, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [1]),
Button(car.CarState.ButtonEvent.Type.resumeCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [1]), Button(car.CarState.ButtonEvent.Type.resumeCruise, "STW_ACTN_RQ", "SpdCtrlLvr_Stat", [2]),
] ]
class CarControllerParams: class CarControllerParams:

@ -22,7 +22,6 @@ non_tested_cars = [
GM.HOLDEN_ASTRA, GM.HOLDEN_ASTRA,
GM.MALIBU, GM.MALIBU,
GM.EQUINOX, GM.EQUINOX,
GM.BOLT_EV,
HYUNDAI.GENESIS_G90, HYUNDAI.GENESIS_G90,
HYUNDAI.KIA_OPTIMA_H, HYUNDAI.KIA_OPTIMA_H,
HONDA.ODYSSEY_CHN, HONDA.ODYSSEY_CHN,
@ -51,6 +50,7 @@ routes = [
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV), CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.ESCALADE_ESV),
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT), CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.VOLT),
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1), CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.BOLT_EUV, segment=1),
CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.BOLT_EUV, segment=14), # Bolt EV
CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.SILVERADO), CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.SILVERADO),
CarTestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G), CarTestRoute("0e7a2ba168465df5|2020-10-18--14-14-22", HONDA.ACURA_RDX_3G),
@ -96,8 +96,10 @@ routes = [
CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA), CarTestRoute("5b7c365c50084530|2020-04-15--16-13-24", HYUNDAI.SONATA),
CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF), CarTestRoute("b2a38c712dcf90bd|2020-05-18--18-12-48", HYUNDAI.SONATA_LF),
CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON), CarTestRoute("fb3fd42f0baaa2f8|2022-03-30--15-25-05", HYUNDAI.TUCSON),
CarTestRoute("db68bbe12250812c|2022-12-05--00-54-12", HYUNDAI.TUCSON_4TH_GEN), # 2023
CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN), CarTestRoute("36e10531feea61a4|2022-07-25--13-37-42", HYUNDAI.TUCSON_HYBRID_4TH_GEN),
CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO), CarTestRoute("5875672fc1d4bf57|2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
CarTestRoute("628935d7d3e5f4f7|2022-11-30--01-12-46", HYUNDAI.KIA_SORENTO_PHEV_4TH_GEN),
CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE), CarTestRoute("9c917ba0d42ffe78|2020-04-17--12-43-19", HYUNDAI.PALISADE),
CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.IONIQ_5), # HDA2 CarTestRoute("05a8f0197fdac372|2022-10-19--14-14-09", HYUNDAI.IONIQ_5), # HDA2
CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019), CarTestRoute("3f29334d6134fcd4|2022-03-30--22-00-50", HYUNDAI.IONIQ_PHEV_2019),
@ -111,6 +113,7 @@ routes = [
CarTestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11), CarTestRoute("ff973b941a69366f|2022-07-28--22-01-19", HYUNDAI.KONA_EV_2022, segment=11),
CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV), CarTestRoute("49f3c13141b6bc87|2021-07-28--08-05-13", HYUNDAI.KONA_HEV),
CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER), CarTestRoute("5dddcbca6eb66c62|2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
CarTestRoute("5b50b883a4259afb|2022-11-09--15-00-42", HYUNDAI.KIA_STINGER_2022),
CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER), CarTestRoute("d624b3d19adce635|2020-08-01--14-59-12", HYUNDAI.VELOSTER),
CarTestRoute("d545129f3ca90f28|2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # HDA2 CarTestRoute("d545129f3ca90f28|2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # HDA2
CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1 CarTestRoute("68d6a96e703c00c9|2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # HDA1

@ -31,6 +31,8 @@ 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.wheelbase, 0)
self.assertGreater(car_params.centerToFront, 0)
self.assertGreater(car_params.maxLateralAccel, 0) self.assertGreater(car_params.maxLateralAccel, 0)
if car_params.steerControlType != car.CarParams.SteerControlType.angle: if car_params.steerControlType != car.CarParams.SteerControlType.angle:

@ -9,7 +9,6 @@ from parameterized import parameterized_class
from cereal import log, car from cereal import log, car
from common.realtime import DT_CTRL from common.realtime import DT_CTRL
from selfdrive.boardd.boardd import can_capnp_to_can_list
from selfdrive.car.fingerprints import all_known_cars from selfdrive.car.fingerprints import all_known_cars
from selfdrive.car.car_helpers import interfaces from selfdrive.car.car_helpers import interfaces
from selfdrive.car.gm.values import CAR as GM from selfdrive.car.gm.values import CAR as GM
@ -20,8 +19,7 @@ from selfdrive.test.openpilotci import get_url
from tools.lib.logreader import LogReader from tools.lib.logreader import LogReader
from tools.lib.route import Route from tools.lib.route import Route
from panda.tests.safety import libpandasafety_py from panda.tests.libpanda import libpanda_py
from panda.tests.safety.common import package_can_msg
PandaType = log.PandaState.PandaType PandaType = log.PandaState.PandaType
@ -118,7 +116,7 @@ class TestCarModelBase(unittest.TestCase):
assert self.CI assert self.CI
# TODO: check safetyModel is in release panda build # TODO: check safetyModel is in release panda build
self.safety = libpandasafety_py.libpandasafety self.safety = libpanda_py.libpanda
cfg = self.CP.safetyConfigs[-1] cfg = self.CP.safetyConfigs[-1]
set_status = self.safety.set_safety_hooks(cfg.safetyModel.raw, cfg.safetyParam) set_status = self.safety.set_safety_hooks(cfg.safetyModel.raw, cfg.safetyParam)
@ -192,7 +190,7 @@ class TestCarModelBase(unittest.TestCase):
if msg.src >= 64: if msg.src >= 64:
continue continue
to_send = package_can_msg([msg.address, 0, msg.dat, msg.src % 4]) to_send = libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
if self.safety.safety_rx_hook(to_send) != 1: if self.safety.safety_rx_hook(to_send) != 1:
failed_addrs[hex(msg.address)] += 1 failed_addrs[hex(msg.address)] += 1
@ -215,8 +213,8 @@ class TestCarModelBase(unittest.TestCase):
# warm up pass, as initial states may be different # warm up pass, as initial states may be different
for can in self.can_msgs[:300]: for can in self.can_msgs[:300]:
self.CI.update(CC, (can.as_builder().to_bytes(), )) self.CI.update(CC, (can.as_builder().to_bytes(), ))
for msg in can_capnp_to_can_list(can.can, src_filter=range(64)): for msg in filter(lambda m: m.src in range(64), can.can):
to_send = package_can_msg(msg) to_send = libpanda_py.make_CANPacket(msg.address, msg.src, msg.dat)
self.safety.safety_rx_hook(to_send) self.safety.safety_rx_hook(to_send)
controls_allowed_prev = False controls_allowed_prev = False
@ -224,10 +222,8 @@ class TestCarModelBase(unittest.TestCase):
checks = defaultdict(lambda: 0) checks = defaultdict(lambda: 0)
for idx, can in enumerate(self.can_msgs): for idx, can in enumerate(self.can_msgs):
CS = self.CI.update(CC, (can.as_builder().to_bytes(), )) CS = self.CI.update(CC, (can.as_builder().to_bytes(), ))
for msg in can_capnp_to_can_list(can.can, src_filter=range(64)): for msg in filter(lambda m: m.src in range(64), can.can):
msg = list(msg) to_send = libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
msg[3] %= 4
to_send = package_can_msg(msg)
ret = self.safety.safety_rx_hook(to_send) ret = self.safety.safety_rx_hook(to_send)
self.assertEqual(1, ret, f"safety rx failed ({ret=}): {to_send}") self.assertEqual(1, ret, f"safety rx failed ({ret=}): {to_send}")

@ -30,11 +30,11 @@ CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05] CHEVROLET EQUINOX 2019: [2.0, 2.0, 0.05]
VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1] VOLKSWAGEN PASSAT NMS: [2.5, 2.5, 0.1]
VOLKSWAGEN SHARAN 2ND GEN: [2.5, 2.5, 0.1] VOLKSWAGEN SHARAN 2ND GEN: [2.5, 2.5, 0.1]
HYUNDAI TUCSON HYBRID 4TH GEN: [2.5, 2.5, 0.0] HYUNDAI SANTA CRUZ 1ST GEN: [2.7, 2.7, 0.1]
HYUNDAI SANTA CRUZ 1ST GEN: [2.7, 2.7, 0.0] KIA SPORTAGE 5TH GEN: [2.7, 2.7, 0.1]
KIA SPORTAGE 5TH GEN: [2.7, 2.7, 0.0] KIA SPORTAGE HYBRID 5TH GEN: [2.5, 2.5, 0.1]
KIA SPORTAGE HYBRID 5TH GEN: [2.5, 2.5, 0.0] GENESIS GV70 1ST GEN: [2.42, 2.42, 0.1]
GENESIS GV70 1ST GEN: [2.42, 2.42, 0.01] KIA SORENTO PLUG-IN HYBRID 4TH GEN: [2.5, 2.5, 0.1]
# Dashcam or fallback configured as ideal car # Dashcam or fallback configured as ideal car
mock: [10.0, 10, 0.0] mock: [10.0, 10, 0.0]

@ -27,10 +27,11 @@ HONDA PILOT 2017: [1.7262026201812795, 0.9470005614967523, 0.21351430733218763]
HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328] HONDA RIDGELINE 2017: [1.4146525028237624, 0.7356572861629564, 0.23307177552211328]
HYUNDAI ELANTRA 2021: [3.169, 2.1259108157250735, 0.0819] HYUNDAI ELANTRA 2021: [3.169, 2.1259108157250735, 0.0819]
HYUNDAI GENESIS 2015-2016: [1.8466226943929824, 1.5552063647830634, 0.0984484465421171] HYUNDAI GENESIS 2015-2016: [1.8466226943929824, 1.5552063647830634, 0.0984484465421171]
HYUNDAI IONIQ 5 2022: [3.172929, 3.0, 0.096019]
HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276] HYUNDAI IONIQ ELECTRIC LIMITED 2019: [1.7662975472852054, 1.613755614526594, 0.17087579756306276]
HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778] HYUNDAI IONIQ PHEV 2020: [3.2928700076638537, 2.1193482926455656, 0.12463700961468778]
HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482] HYUNDAI IONIQ PLUG-IN HYBRID 2019: [2.970807902012267, 1.6312321830002083, 0.1088964990357482]
HYUNDAI KONA ELECTRIC 2019: [4.398306735170212, 3.2961956260770484, 0.08651833437845884] HYUNDAI KONA ELECTRIC 2019: [3.078814714619148, 3.2961956260770484, 0.08651833437845884]
HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651] HYUNDAI PALISADE 2020: [2.544642494803999, 1.8721703683337008, 0.1301424599248651]
HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945] HYUNDAI SANTA FE 2019: [3.0787027729757632, 2.6173437483495565, 0.1207019341823945]
HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963] HYUNDAI SANTA FE HYBRID 2022: [3.501877602644835, 2.729064118456137, 0.10384068104538963]
@ -38,6 +39,7 @@ HYUNDAI SANTA FE PlUG-IN HYBRID 2022: [1.6953050513611045, 1.5837614296206861, 0
HYUNDAI SONATA 2019: [2.2200457811703953, 1.2967330275895228, 0.14039920986586393] HYUNDAI SONATA 2019: [2.2200457811703953, 1.2967330275895228, 0.14039920986586393]
HYUNDAI SONATA 2020: [2.9638737459977467, 2.1259108157250735, 0.07813665616927593] HYUNDAI SONATA 2020: [2.9638737459977467, 2.1259108157250735, 0.07813665616927593]
HYUNDAI SONATA HYBRID 2021: [2.8990264092395734, 2.061410192222139, 0.0899805488717382] HYUNDAI SONATA HYBRID 2021: [2.8990264092395734, 2.061410192222139, 0.0899805488717382]
HYUNDAI TUCSON HYBRID 4TH GEN: [2.035545, 2.035545, 0.110272]
JEEP GRAND CHEROKEE 2019: [1.7321233388827006, 1.289689569171081, 0.15046331002097185] JEEP GRAND CHEROKEE 2019: [1.7321233388827006, 1.289689569171081, 0.15046331002097185]
JEEP GRAND CHEROKEE V6 2018: [1.8776598027756923, 1.4057367824262523, 0.11725947414922003] JEEP GRAND CHEROKEE V6 2018: [1.8776598027756923, 1.4057367824262523, 0.11725947414922003]
KIA EV6 2022: [3.2, 3.0, 0.05] KIA EV6 2022: [3.2, 3.0, 0.05]
@ -76,7 +78,7 @@ TOYOTA HIGHLANDER 2020: [2.022340166827233, 1.6183134804881791, 0.14592306380054
TOYOTA HIGHLANDER HYBRID 2018: [1.752033, 1.6433903296845025, 0.144600] TOYOTA HIGHLANDER HYBRID 2018: [1.752033, 1.6433903296845025, 0.144600]
TOYOTA HIGHLANDER HYBRID 2020: [1.901174, 2.104015182965606, 0.14447040132184993] TOYOTA HIGHLANDER HYBRID 2020: [1.901174, 2.104015182965606, 0.14447040132184993]
TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565] TOYOTA MIRAI 2021: [2.506899832157829, 1.7417213930750164, 0.20182618449440565]
TOYOTA PRIUS 2017: [1.746445, 1.5023147650693636, 0.151515] TOYOTA PRIUS 2017: [1.60, 1.5023147650693636, 0.151515]
TOYOTA PRIUS TSS2 2021: [1.972600, 1.9104337425537743, 0.170968] TOYOTA PRIUS TSS2 2021: [1.972600, 1.9104337425537743, 0.170968]
TOYOTA RAV4 2017: [2.085695074355425, 2.2142832316984733, 0.13339165270103975] TOYOTA RAV4 2017: [2.085695074355425, 2.2142832316984733, 0.13339165270103975]
TOYOTA RAV4 2019: [2.331293, 2.0993589721530252, 0.129822] TOYOTA RAV4 2019: [2.331293, 2.0993589721530252, 0.129822]

@ -29,14 +29,15 @@ HYUNDAI VELOSTER 2019: HYUNDAI SONATA 2019
HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA 2020: HYUNDAI KONA ELECTRIC 2019
HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA HYBRID 2020: HYUNDAI KONA ELECTRIC 2019
HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019 HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019
HYUNDAI IONIQ 5 2022: KIA EV6 2022
HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019 HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI IONIQ HYBRID 2020-2022: 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 IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019
HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019 HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019
HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020 HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020
HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019 HYUNDAI TUCSON 2019: HYUNDAI SANTA FE 2019
HYUNDAI TUCSON 4TH GEN: HYUNDAI TUCSON HYBRID 4TH GEN
HYUNDAI SANTA FE 2022: HYUNDAI SANTA FE HYBRID 2022 HYUNDAI SANTA FE 2022: HYUNDAI SANTA FE HYBRID 2022
KIA STINGER 2022: KIA STINGER GT2 2018
GENESIS G90 2017: GENESIS G70 2018 GENESIS G90 2017: GENESIS G70 2018
GENESIS G80 2017: GENESIS G70 2018 GENESIS G80 2017: GENESIS G70 2018
GENESIS G70 2020: HYUNDAI SONATA 2020 GENESIS G70 2020: HYUNDAI SONATA 2020

@ -6,7 +6,7 @@ from common.realtime import DT_CTRL
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.toyota.values import ToyotaFlags, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR from selfdrive.car.toyota.values import ToyotaFlags, DBC, STEER_THRESHOLD, TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR
class CarState(CarStateBase): class CarState(CarStateBase):
@ -85,16 +85,20 @@ class CarState(CarStateBase):
ret.steeringTorqueEps = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_EPS"] * self.eps_torque_scale ret.steeringTorqueEps = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_EPS"] * self.eps_torque_scale
# we could use the override bit from dbc, but it's triggered at too high torque values # we could use the override bit from dbc, but it's triggered at too high torque values
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
# steer rate fault, goes to 21 or 25 for 1 frame, then 9 for ~2 seconds # steer rate fault: goes to 21 or 25 for 1 frame, then 9 for 2 seconds
ret.steerFaultTemporary = cp.vl["EPS_STATUS"]["LKA_STATE"] in (0, 9, 21, 25) # lka msg drop out: goes to 9 then 11 for a combined total of 2 seconds
ret.steerFaultTemporary = cp.vl["EPS_STATUS"]["LKA_STATE"] in (0, 9, 11, 21, 25)
# 17 is a fault from a prolonged high torque delta between cmd and user # 17 is a fault from a prolonged high torque delta between cmd and user
ret.steerFaultPermanent = cp.vl["EPS_STATUS"]["LKA_STATE"] == 17 # 3 is a fault from the lka command message not being received by the EPS
ret.steerFaultPermanent = cp.vl["EPS_STATUS"]["LKA_STATE"] in (3, 17)
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR: if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
# TODO: find the bit likely in DSU_CRUISE that describes an ACC fault. one may also exist in CLUTCH
ret.cruiseState.available = cp.vl["DSU_CRUISE"]["MAIN_ON"] != 0 ret.cruiseState.available = cp.vl["DSU_CRUISE"]["MAIN_ON"] != 0
ret.cruiseState.speed = cp.vl["DSU_CRUISE"]["SET_SPEED"] * CV.KPH_TO_MS ret.cruiseState.speed = cp.vl["DSU_CRUISE"]["SET_SPEED"] * CV.KPH_TO_MS
cluster_set_speed = cp.vl["PCM_CRUISE_ALT"]["UI_SET_SPEED"] cluster_set_speed = cp.vl["PCM_CRUISE_ALT"]["UI_SET_SPEED"]
else: else:
ret.accFaulted = cp.vl["PCM_CRUISE_2"]["ACC_FAULTED"] != 0
ret.cruiseState.available = cp.vl["PCM_CRUISE_2"]["MAIN_ON"] != 0 ret.cruiseState.available = cp.vl["PCM_CRUISE_2"]["MAIN_ON"] != 0
ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]["SET_SPEED"] * CV.KPH_TO_MS ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]["SET_SPEED"] * CV.KPH_TO_MS
cluster_set_speed = cp.vl["PCM_CRUISE_SM"]["UI_SET_SPEED"] cluster_set_speed = cp.vl["PCM_CRUISE_SM"]["UI_SET_SPEED"]
@ -120,11 +124,6 @@ class CarState(CarStateBase):
self.low_speed_lockout = cp.vl["PCM_CRUISE_2"]["LOW_SPEED_LOCKOUT"] == 2 self.low_speed_lockout = cp.vl["PCM_CRUISE_2"]["LOW_SPEED_LOCKOUT"] == 2
self.pcm_acc_status = cp.vl["PCM_CRUISE"]["CRUISE_STATE"] self.pcm_acc_status = cp.vl["PCM_CRUISE"]["CRUISE_STATE"]
if self.CP.carFingerprint in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor:
# ignore standstill in hybrid vehicles, since pcm allows to restart without
# receiving any special command. Also if interceptor is detected
ret.cruiseState.standstill = False
else:
ret.cruiseState.standstill = self.pcm_acc_status == 7 ret.cruiseState.standstill = self.pcm_acc_status == 7
ret.cruiseState.enabled = bool(cp.vl["PCM_CRUISE"]["CRUISE_ACTIVE"]) ret.cruiseState.enabled = bool(cp.vl["PCM_CRUISE"]["CRUISE_ACTIVE"])
ret.cruiseState.nonAdaptive = cp.vl["PCM_CRUISE"]["CRUISE_STATE"] in (1, 2, 3, 4, 5, 6) ret.cruiseState.nonAdaptive = cp.vl["PCM_CRUISE"]["CRUISE_STATE"] in (1, 2, 3, 4, 5, 6)
@ -208,6 +207,7 @@ class CarState(CarStateBase):
else: else:
signals.append(("MAIN_ON", "PCM_CRUISE_2")) signals.append(("MAIN_ON", "PCM_CRUISE_2"))
signals.append(("SET_SPEED", "PCM_CRUISE_2")) signals.append(("SET_SPEED", "PCM_CRUISE_2"))
signals.append(("ACC_FAULTED", "PCM_CRUISE_2"))
signals.append(("LOW_SPEED_LOCKOUT", "PCM_CRUISE_2")) signals.append(("LOW_SPEED_LOCKOUT", "PCM_CRUISE_2"))
checks.append(("PCM_CRUISE_2", 33)) checks.append(("PCM_CRUISE_2", 33))

@ -3,7 +3,7 @@ from cereal import car
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from panda import Panda from panda import Panda
from selfdrive.car.toyota.values import Ecu, CAR, ToyotaFlags, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, MIN_ACC_SPEED, EPS_SCALE, EV_HYBRID_CAR, UNSUPPORTED_DSU_CAR, CarControllerParams, NO_STOP_TIMER_CAR from selfdrive.car.toyota.values import Ecu, CAR, ToyotaFlags, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, MIN_ACC_SPEED, EPS_SCALE, EV_HYBRID_CAR, UNSUPPORTED_DSU_CAR, CarControllerParams, NO_STOP_TIMER_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_tire_stiffness, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
@ -15,9 +15,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=[], experimental_long=False): # pylint: disable=dangerous-default-value def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "toyota" ret.carName = "toyota"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)] ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate] ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
@ -42,7 +40,8 @@ class CarInterface(CarInterfaceBase):
# Only give steer angle deadzone to for bad angle sensor prius # Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw: for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00': if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
steering_angle_deadzone_deg = 1.0 steering_angle_deadzone_deg = 0.2
ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg) CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
elif candidate == CAR.PRIUS_V: elif candidate == CAR.PRIUS_V:
@ -51,7 +50,6 @@ 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
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg)
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
@ -190,10 +188,6 @@ class CarInterface(CarInterfaceBase):
ret.centerToFront = ret.wheelbase * 0.44 ret.centerToFront = ret.wheelbase * 0.44
# TODO: get actual value, for now starting with reasonable value for
# civic and scaling by mass and wheelbase
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
# TODO: start from empirically derived lateral slip stiffness for the civic and scale by # TODO: start from empirically derived lateral slip stiffness for the civic and scale by
# mass and CG position, so all cars will have approximately similar dyn behaviors # mass and CG position, so all cars will have approximately similar dyn behaviors
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,
@ -247,9 +241,12 @@ class CarInterface(CarInterfaceBase):
# events # events
events = self.create_common_events(ret) events = self.create_common_events(ret)
if self.CS.low_speed_lockout and self.CP.openpilotLongitudinalControl: if self.CP.openpilotLongitudinalControl:
if ret.cruiseState.standstill and not ret.brakePressed and not self.CP.enableGasInterceptor:
events.add(EventName.resumeRequired)
if self.CS.low_speed_lockout:
events.add(EventName.lowSpeedLockout) events.add(EventName.lowSpeedLockout)
if ret.vEgo < self.CP.minEnableSpeed and self.CP.openpilotLongitudinalControl: if ret.vEgo < self.CP.minEnableSpeed:
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
if c.actuators.accel > 0.3: if c.actuators.accel > 0.3:
# some margin on the actuator to not false trigger cancellation while stopping # some margin on the actuator to not false trigger cancellation while stopping

@ -113,7 +113,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]), CAR.CAMRY: ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"), CAR.CAMRYH: ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]), CAR.CAMRY_TSS2: ToyotaCarInfo("Toyota Camry 2021-22", footnotes=[Footnote.CAMRY]),
CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-22"), CAR.CAMRYH_TSS2: ToyotaCarInfo("Toyota Camry Hybrid 2021-23"),
CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-21"), CAR.CHR: ToyotaCarInfo("Toyota C-HR 2017-21"),
CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"), CAR.CHRH: ToyotaCarInfo("Toyota C-HR Hybrid 2017-19"),
CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"), CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"),
@ -209,16 +209,19 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request( Request(
[StdQueries.SHORT_TESTER_PRESENT_REQUEST, TOYOTA_VERSION_REQUEST], [StdQueries.SHORT_TESTER_PRESENT_REQUEST, TOYOTA_VERSION_REQUEST],
[StdQueries.SHORT_TESTER_PRESENT_RESPONSE, TOYOTA_VERSION_RESPONSE], [StdQueries.SHORT_TESTER_PRESENT_RESPONSE, TOYOTA_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.dsu, Ecu.abs, Ecu.eps],
bus=0, bus=0,
), ),
Request( Request(
[StdQueries.SHORT_TESTER_PRESENT_REQUEST, StdQueries.OBD_VERSION_REQUEST], [StdQueries.SHORT_TESTER_PRESENT_REQUEST, StdQueries.OBD_VERSION_REQUEST],
[StdQueries.SHORT_TESTER_PRESENT_RESPONSE, StdQueries.OBD_VERSION_RESPONSE], [StdQueries.SHORT_TESTER_PRESENT_RESPONSE, StdQueries.OBD_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine],
bus=0, bus=0,
), ),
Request( Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.DEFAULT_DIAGNOSTIC_REQUEST, StdQueries.EXTENDED_DIAGNOSTIC_REQUEST, StdQueries.UDS_VERSION_REQUEST], [StdQueries.TESTER_PRESENT_REQUEST, StdQueries.DEFAULT_DIAGNOSTIC_REQUEST, StdQueries.EXTENDED_DIAGNOSTIC_REQUEST, StdQueries.UDS_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.DEFAULT_DIAGNOSTIC_RESPONSE, StdQueries.EXTENDED_DIAGNOSTIC_RESPONSE, StdQueries.UDS_VERSION_RESPONSE], [StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.DEFAULT_DIAGNOSTIC_RESPONSE, StdQueries.EXTENDED_DIAGNOSTIC_RESPONSE, StdQueries.UDS_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.abs, Ecu.eps],
bus=0, bus=0,
), ),
], ],
@ -548,10 +551,12 @@ FW_VERSIONS = {
(Ecu.abs, 0x7b0, None): [ (Ecu.abs, 0x7b0, None): [
b'F152633D00\x00\x00\x00\x00\x00\x00', b'F152633D00\x00\x00\x00\x00\x00\x00',
b'F152633D60\x00\x00\x00\x00\x00\x00', b'F152633D60\x00\x00\x00\x00\x00\x00',
b'F152633310\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x018966306Q6000\x00\x00\x00\x00', b'\x018966306Q6000\x00\x00\x00\x00',
b'\x018966306Q7000\x00\x00\x00\x00', b'\x018966306Q7000\x00\x00\x00\x00',
b'\x018966306V1000\x00\x00\x00\x00',
b'\x01896633T20000\x00\x00\x00\x00', b'\x01896633T20000\x00\x00\x00\x00',
], ],
(Ecu.fwdRadar, 0x750, 15): [ (Ecu.fwdRadar, 0x750, 15): [
@ -562,6 +567,7 @@ FW_VERSIONS = {
b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305200\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00', b'\x028646F3305300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
b'\x028646F3305300\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', b'\x028646F3305300\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
b'\x028646F3305500\x00\x00\x00\x008646G3304000\x00\x00\x00\x00',
], ],
}, },
CAR.CHR: { CAR.CHR: {
@ -742,6 +748,7 @@ FW_VERSIONS = {
b'\x018966312S7000\x00\x00\x00\x00', b'\x018966312S7000\x00\x00\x00\x00',
b'\x018966312W3000\x00\x00\x00\x00', b'\x018966312W3000\x00\x00\x00\x00',
b'\x018966312W9000\x00\x00\x00\x00', b'\x018966312W9000\x00\x00\x00\x00',
b'\x01896637644000\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\x0230A10000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0230A10000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00',
@ -769,6 +776,7 @@ FW_VERSIONS = {
b'\x018965B1255000\x00\x00\x00\x00', b'\x018965B1255000\x00\x00\x00\x00',
b'8965B12361\x00\x00\x00\x00\x00\x00', b'8965B12361\x00\x00\x00\x00\x00\x00',
b'8965B16011\x00\x00\x00\x00\x00\x00', b'8965B16011\x00\x00\x00\x00\x00\x00',
b'8965B76012\x00\x00\x00\x00\x00\x00',
b'\x018965B12510\x00\x00\x00\x00\x00\x00', b'\x018965B12510\x00\x00\x00\x00\x00\x00',
b'\x018965B1256000\x00\x00\x00\x00', b'\x018965B1256000\x00\x00\x00\x00',
], ],
@ -794,6 +802,7 @@ FW_VERSIONS = {
b'\x01F152612862\x00\x00\x00\x00\x00\x00', b'\x01F152612862\x00\x00\x00\x00\x00\x00',
b'\x01F152612B91\x00\x00\x00\x00\x00\x00', b'\x01F152612B91\x00\x00\x00\x00\x00\x00',
b'\x01F15260A070\x00\x00\x00\x00\x00\x00', b'\x01F15260A070\x00\x00\x00\x00\x00\x00',
b'\x01F152676250\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F3301100\x00\x00\x00\x00', b'\x018821F3301100\x00\x00\x00\x00',
@ -821,6 +830,7 @@ FW_VERSIONS = {
b'\x01896637621000\x00\x00\x00\x00', b'\x01896637621000\x00\x00\x00\x00',
b'\x01896637624000\x00\x00\x00\x00', b'\x01896637624000\x00\x00\x00\x00',
b'\x01896637626000\x00\x00\x00\x00', b'\x01896637626000\x00\x00\x00\x00',
b'\x01896637639000\x00\x00\x00\x00',
b'\x01896637648000\x00\x00\x00\x00', b'\x01896637648000\x00\x00\x00\x00',
b'\x01896637643000\x00\x00\x00\x00', b'\x01896637643000\x00\x00\x00\x00',
b'\x02896630A07000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00', b'\x02896630A07000\x00\x00\x00\x008966A4703000\x00\x00\x00\x00',
@ -1014,6 +1024,7 @@ FW_VERSIONS = {
b'\x01F15264873500\x00\x00\x00\x00', b'\x01F15264873500\x00\x00\x00\x00',
b'\x01F152648C6300\x00\x00\x00\x00', b'\x01F152648C6300\x00\x00\x00\x00',
b'\x01F152648J4000\x00\x00\x00\x00', b'\x01F152648J4000\x00\x00\x00\x00',
b'\x01F152648J5000\x00\x00\x00\x00',
b'\x01F152648J6000\x00\x00\x00\x00', b'\x01F152648J6000\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
@ -1021,6 +1032,7 @@ FW_VERSIONS = {
b'\x01896630EA1000\x00\x00\x00\x00', b'\x01896630EA1000\x00\x00\x00\x00',
b'\x01896630EE4000\x00\x00\x00\x00', b'\x01896630EE4000\x00\x00\x00\x00',
b'\x01896630EE4100\x00\x00\x00\x00', b'\x01896630EE4100\x00\x00\x00\x00',
b'\x01896630EE5000\x00\x00\x00\x00',
b'\x01896630EE6000\x00\x00\x00\x00', b'\x01896630EE6000\x00\x00\x00\x00',
b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
@ -1320,6 +1332,7 @@ FW_VERSIONS = {
b'\x01F15260R302\x00\x00\x00\x00\x00\x00', b'\x01F15260R302\x00\x00\x00\x00\x00\x00',
b'\x01F152642551\x00\x00\x00\x00\x00\x00', b'\x01F152642551\x00\x00\x00\x00\x00\x00',
b'\x01F152642561\x00\x00\x00\x00\x00\x00', b'\x01F152642561\x00\x00\x00\x00\x00\x00',
b'\x01F152642601\x00\x00\x00\x00\x00\x00',
b'\x01F152642700\x00\x00\x00\x00\x00\x00', b'\x01F152642700\x00\x00\x00\x00\x00\x00',
b'\x01F152642701\x00\x00\x00\x00\x00\x00', b'\x01F152642701\x00\x00\x00\x00\x00\x00',
b'\x01F152642710\x00\x00\x00\x00\x00\x00', b'\x01F152642710\x00\x00\x00\x00\x00\x00',

@ -62,7 +62,9 @@ class CarState(CarStateBase):
ret.gas = pt_cp.vl["Motor_20"]["MO_Fahrpedalrohwert_01"] / 100.0 ret.gas = pt_cp.vl["Motor_20"]["MO_Fahrpedalrohwert_01"] / 100.0
ret.gasPressed = ret.gas > 0 ret.gasPressed = ret.gas > 0
ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
ret.brakePressed = bool(pt_cp.vl["ESP_05"]["ESP_Fahrer_bremst"]) brake_pedal_pressed = bool(pt_cp.vl["Motor_14"]["MO_Fahrer_bremst"])
brake_pressure_detected = bool(pt_cp.vl["ESP_05"]["ESP_Fahrer_bremst"])
ret.brakePressed = brake_pedal_pressed or brake_pressure_detected
ret.parkingBrake = bool(pt_cp.vl["Kombi_01"]["KBI_Handbremse"]) # FIXME: need to include an EPB check as well ret.parkingBrake = bool(pt_cp.vl["Kombi_01"]["KBI_Handbremse"]) # FIXME: need to include an EPB check as well
# Update gear and/or clutch position data. # Update gear and/or clutch position data.
@ -218,12 +220,13 @@ class CarState(CarStateBase):
ret.stockAeb = False ret.stockAeb = False
# Update ACC radar status. # Update ACC radar status.
self.acc_type = 0 # TODO: this is ACC "basic" with nonzero min speed, support FtS (1) later self.acc_type = ext_cp.vl["ACC_System"]["ACS_Typ_ACC"]
ret.cruiseState.available = bool(pt_cp.vl["Motor_5"]["GRA_Hauptschalter"]) ret.cruiseState.available = bool(pt_cp.vl["Motor_5"]["GRA_Hauptschalter"])
ret.cruiseState.enabled = bool(pt_cp.vl["Motor_2"]["GRA_Status"]) ret.cruiseState.enabled = pt_cp.vl["Motor_2"]["GRA_Status"] in (1, 2)
if self.CP.pcmCruise: if self.CP.pcmCruise:
ret.accFaulted = ext_cp.vl["ACC_GRA_Anziege"]["ACA_StaACC"] in (6, 7) ret.accFaulted = ext_cp.vl["ACC_GRA_Anziege"]["ACA_StaACC"] in (6, 7)
# TODO: update opendbc with PQ TSK state for OP long accFaulted else:
ret.accFaulted = pt_cp.vl["Motor_2"]["GRA_Status"] == 3
# Update ACC setpoint. When the setpoint reads as 255, the driver has not # Update ACC setpoint. When the setpoint reads as 255, the driver has not
# yet established an ACC setpoint, so treat it as zero. # yet established an ACC setpoint, so treat it as zero.
@ -268,8 +271,9 @@ class CarState(CarStateBase):
("Comfort_Signal_Right", "Blinkmodi_02"), # Right turn signal including comfort blink interval ("Comfort_Signal_Right", "Blinkmodi_02"), # Right turn signal including comfort blink interval
("AB_Gurtschloss_FA", "Airbag_02"), # Seatbelt status, driver ("AB_Gurtschloss_FA", "Airbag_02"), # Seatbelt status, driver
("AB_Gurtschloss_BF", "Airbag_02"), # Seatbelt status, passenger ("AB_Gurtschloss_BF", "Airbag_02"), # Seatbelt status, passenger
("ESP_Fahrer_bremst", "ESP_05"), # Brake pedal pressed ("ESP_Fahrer_bremst", "ESP_05"), # Driver applied brake pressure over threshold
("ESP_Bremsdruck", "ESP_05"), # Brake pressure applied ("MO_Fahrer_bremst", "Motor_14"), # Brake pedal switch
("ESP_Bremsdruck", "ESP_05"), # Brake pressure
("MO_Fahrpedalrohwert_01", "Motor_20"), # Accelerator pedal value ("MO_Fahrpedalrohwert_01", "Motor_20"), # Accelerator pedal value
("EPS_Lenkmoment", "LH_EPS_03"), # Absolute driver torque input ("EPS_Lenkmoment", "LH_EPS_03"), # Absolute driver torque input
("EPS_VZ_Lenkmoment", "LH_EPS_03"), # Driver torque input sign ("EPS_VZ_Lenkmoment", "LH_EPS_03"), # Driver torque input sign
@ -304,6 +308,7 @@ class CarState(CarStateBase):
("ESP_02", 50), # From J104 ABS/ESP controller ("ESP_02", 50), # From J104 ABS/ESP controller
("GRA_ACC_01", 33), # From J533 CAN gateway (via LIN from steering wheel controls) ("GRA_ACC_01", 33), # From J533 CAN gateway (via LIN from steering wheel controls)
("Gateway_72", 10), # From J533 CAN gateway (aggregated data) ("Gateway_72", 10), # From J533 CAN gateway (aggregated data)
("Motor_14", 10), # From J623 Engine control module
("Airbag_02", 5), # From J234 Airbag control module ("Airbag_02", 5), # From J234 Airbag control module
("Kombi_01", 2), # From J285 Instrument cluster ("Kombi_01", 2), # From J285 Instrument cluster
("Blinkmodi_02", 1), # From J519 BCM (sent at 1Hz when no lights active, 50Hz when active) ("Blinkmodi_02", 1), # From J519 BCM (sent at 1Hz when no lights active, 50Hz when active)
@ -318,7 +323,6 @@ class CarState(CarStateBase):
elif CP.transmissionType == TransmissionType.manual: elif CP.transmissionType == TransmissionType.manual:
signals += [("MO_Kuppl_schalter", "Motor_14"), # Clutch switch signals += [("MO_Kuppl_schalter", "Motor_14"), # Clutch switch
("BCM1_Rueckfahrlicht_Schalter", "Gateway_72")] # Reverse light from BCM ("BCM1_Rueckfahrlicht_Schalter", "Gateway_72")] # Reverse light from BCM
checks.append(("Motor_14", 10)) # From J623 Engine control module
if CP.networkLocation == NetworkLocation.fwdCamera: if CP.networkLocation == NetworkLocation.fwdCamera:
# Radars are here on CANBUS.pt # Radars are here on CANBUS.pt
@ -482,7 +486,7 @@ class MqbExtraSignals:
# Additional signal and message lists for optional or bus-portable controllers # Additional signal and message lists for optional or bus-portable controllers
fwd_radar_signals = [ fwd_radar_signals = [
("ACC_Wunschgeschw_02", "ACC_02"), # ACC set speed ("ACC_Wunschgeschw_02", "ACC_02"), # ACC set speed
("ACC_Typ", "ACC_06"), # Basic vs F2S vs SNG ("ACC_Typ", "ACC_06"), # Basic vs FtS vs SnG
("AWV2_Freigabe", "ACC_10"), # FCW brake jerk release ("AWV2_Freigabe", "ACC_10"), # FCW brake jerk release
("ANB_Teilbremsung_Freigabe", "ACC_10"), # AEB partial braking release ("ANB_Teilbremsung_Freigabe", "ACC_10"), # AEB partial braking release
("ANB_Zielbremsung_Freigabe", "ACC_10"), # AEB target braking release ("ANB_Zielbremsung_Freigabe", "ACC_10"), # AEB target braking release
@ -505,17 +509,19 @@ class MqbExtraSignals:
class PqExtraSignals: class PqExtraSignals:
# Additional signal and message lists for optional or bus-portable controllers # Additional signal and message lists for optional or bus-portable controllers
fwd_radar_signals = [ fwd_radar_signals = [
("ACA_StaACC", "ACC_GRA_Anziege", 0), # ACC drivetrain coordinator status ("ACS_Typ_ACC", "ACC_System"), # Basic vs FtS (no SnG support on PQ)
("ACA_V_Wunsch", "ACC_GRA_Anziege", 0), # ACC set speed ("ACA_StaACC", "ACC_GRA_Anziege"), # ACC drivetrain coordinator status
("ACA_V_Wunsch", "ACC_GRA_Anziege"), # ACC set speed
] ]
fwd_radar_checks = [ fwd_radar_checks = [
("ACC_System", 50), # From J428 ACC radar control module
("ACC_GRA_Anziege", 25), # From J428 ACC radar control module ("ACC_GRA_Anziege", 25), # From J428 ACC radar control module
] ]
bsm_radar_signals = [ bsm_radar_signals = [
("SWA_Infostufe_SWA_li", "SWA_1", 0), # Blind spot object info, left ("SWA_Infostufe_SWA_li", "SWA_1"), # Blind spot object info, left
("SWA_Warnung_SWA_li", "SWA_1", 0), # Blind spot object warning, left ("SWA_Warnung_SWA_li", "SWA_1"), # Blind spot object warning, left
("SWA_Infostufe_SWA_re", "SWA_1", 0), # Blind spot object info, right ("SWA_Infostufe_SWA_re", "SWA_1"), # Blind spot object info, right
("SWA_Warnung_SWA_re", "SWA_1", 0), # Blind spot object warning, right ("SWA_Warnung_SWA_re", "SWA_1"), # Blind spot object warning, right
] ]
bsm_radar_checks = [ bsm_radar_checks = [
("SWA_1", 20), # From J1086 Lane Change Assist ("SWA_1", 20), # From J1086 Lane Change Assist

@ -1,8 +1,7 @@
from cereal import car 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 selfdrive.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness, \ from selfdrive.car import STD_CARGO_KG, get_safety_config
gen_empty_fingerprint, get_safety_config
from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.interfaces import CarInterfaceBase
from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter from selfdrive.car.volkswagen.values import CAR, PQ_CARS, CANBUS, NetworkLocation, TransmissionType, GearShifter
@ -22,8 +21,7 @@ class CarInterface(CarInterfaceBase):
self.cp_ext = self.cp_cam self.cp_ext = self.cp_cam
@staticmethod @staticmethod
def get_params(candidate, fingerprint=gen_empty_fingerprint(), car_fw=None, experimental_long=False): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long):
ret = CarInterfaceBase.get_std_params(candidate, fingerprint)
ret.carName = "volkswagen" ret.carName = "volkswagen"
ret.radarOffCan = True ret.radarOffCan = True
@ -74,7 +72,6 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1 ret.steerActuatorDelay = 0.1
ret.steerLimitTimer = 0.4 ret.steerLimitTimer = 0.4
ret.steerRatio = 15.6 # Let the params learner figure this out ret.steerRatio = 15.6 # Let the params learner figure this out
tire_stiffness_factor = 1.0 # Let the params learner figure this out
ret.lateralTuning.pid.kpBP = [0.] ret.lateralTuning.pid.kpBP = [0.]
ret.lateralTuning.pid.kiBP = [0.] ret.lateralTuning.pid.kiBP = [0.]
ret.lateralTuning.pid.kf = 0.00006 ret.lateralTuning.pid.kf = 0.00006
@ -137,7 +134,6 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.SHARAN_MK2: elif candidate == CAR.SHARAN_MK2:
ret.mass = 1639 + STD_CARGO_KG ret.mass = 1639 + STD_CARGO_KG
ret.wheelbase = 2.92 ret.wheelbase = 2.92
ret.minEnableSpeed = 30 * CV.KPH_TO_MS
ret.minSteerSpeed = 50 * CV.KPH_TO_MS ret.minSteerSpeed = 50 * CV.KPH_TO_MS
ret.steerActuatorDelay = 0.2 ret.steerActuatorDelay = 0.2
@ -214,10 +210,7 @@ class CarInterface(CarInterfaceBase):
raise ValueError(f"unsupported car {candidate}") raise ValueError(f"unsupported car {candidate}")
ret.autoResumeSng = ret.minEnableSpeed == -1 ret.autoResumeSng = ret.minEnableSpeed == -1
ret.rotationalInertia = scale_rot_inertia(ret.mass, ret.wheelbase)
ret.centerToFront = ret.wheelbase * 0.45 ret.centerToFront = ret.wheelbase * 0.45
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront,
tire_stiffness_factor=tire_stiffness_factor)
return ret return ret
# returns a car.CarState # returns a car.CarState
@ -237,7 +230,7 @@ class CarInterface(CarInterfaceBase):
events.add(EventName.belowSteerSpeed) events.add(EventName.belowSteerSpeed)
if self.CS.CP.openpilotLongitudinalControl: if self.CS.CP.openpilotLongitudinalControl:
if ret.vEgo < self.CP.minEnableSpeed + 2.: if ret.vEgo < self.CP.minEnableSpeed + 0.5:
events.add(EventName.belowEngageSpeed) events.add(EventName.belowEngageSpeed)
if c.enabled and ret.vEgo < self.CP.minEnableSpeed: if c.enabled and ret.vEgo < self.CP.minEnableSpeed:
events.add(EventName.speedTooLow) events.add(EventName.speedTooLow)

@ -66,6 +66,7 @@ def create_acc_accel_control(packer, bus, acc_type, enabled, accel, acc_control,
"ACS_Sta_ADR": acc_control, "ACS_Sta_ADR": acc_control,
"ACS_StSt_Info": acc_control != 1, "ACS_StSt_Info": acc_control != 1,
"ACS_Typ_ACC": acc_type, "ACS_Typ_ACC": acc_type,
"ACS_Anhaltewunsch": acc_type == 1 and stopping,
"ACS_Sollbeschl": accel if acc_control == 1 else 3.01, "ACS_Sollbeschl": accel if acc_control == 1 else 3.01,
"ACS_zul_Regelabw": 0.2 if acc_control == 1 else 1.27, "ACS_zul_Regelabw": 0.2 if acc_control == 1 else 1.27,
"ACS_max_AendGrad": 3.0 if acc_control == 1 else 5.08, "ACS_max_AendGrad": 3.0 if acc_control == 1 else 5.08,

@ -1,5 +1,5 @@
from collections import defaultdict, namedtuple from collections import defaultdict, namedtuple
from dataclasses import dataclass, field from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Dict, List, Union from typing import Dict, List, Union
@ -165,7 +165,9 @@ class Footnote(Enum):
class VWCarInfo(CarInfo): class VWCarInfo(CarInfo):
package: str = "Adaptive Cruise Control (ACC) & Lane Assist" package: str = "Adaptive Cruise Control (ACC) & Lane Assist"
harness: Enum = Harness.j533 harness: Enum = Harness.j533
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.VW_EXP_LONG])
def init_make(self, CP: car.CarParams):
self.footnotes.insert(0, Footnote.VW_EXP_LONG)
CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
@ -197,28 +199,28 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
VWCarInfo("Volkswagen Jetta GLI 2021-22"), VWCarInfo("Volkswagen Jetta GLI 2021-22"),
], ],
CAR.PASSAT_MK8: [ CAR.PASSAT_MK8: [
VWCarInfo("Volkswagen Passat 2015-22", footnotes=[Footnote.VW_EXP_LONG, Footnote.PASSAT]), VWCarInfo("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]),
VWCarInfo("Volkswagen Passat Alltrack 2015-22"), VWCarInfo("Volkswagen Passat Alltrack 2015-22"),
VWCarInfo("Volkswagen Passat GTE 2015-22"), VWCarInfo("Volkswagen Passat GTE 2015-22"),
], ],
CAR.PASSAT_NMS: VWCarInfo("Volkswagen Passat NMS 2017-22"), CAR.PASSAT_NMS: VWCarInfo("Volkswagen Passat NMS 2017-22"),
CAR.POLO_MK6: [ CAR.POLO_MK6: [
VWCarInfo("Volkswagen Polo 2020-22", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0]), VWCarInfo("Volkswagen Polo 2020-22", footnotes=[Footnote.VW_MQB_A0]),
VWCarInfo("Volkswagen Polo GTI 2020-22", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0]), VWCarInfo("Volkswagen Polo GTI 2020-22", footnotes=[Footnote.VW_MQB_A0]),
], ],
CAR.SHARAN_MK2: [ CAR.SHARAN_MK2: [
VWCarInfo("Volkswagen Sharan 2018-22"), VWCarInfo("Volkswagen Sharan 2018-22"),
VWCarInfo("SEAT Alhambra 2018-20"), VWCarInfo("SEAT Alhambra 2018-20"),
], ],
CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022"), CAR.TAOS_MK1: VWCarInfo("Volkswagen Taos 2022"),
CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0]), CAR.TCROSS_MK1: VWCarInfo("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0]),
CAR.TIGUAN_MK2: VWCarInfo("Volkswagen Tiguan 2019-22"), CAR.TIGUAN_MK2: VWCarInfo("Volkswagen Tiguan 2019-22"),
CAR.TOURAN_MK2: VWCarInfo("Volkswagen Touran 2017"), CAR.TOURAN_MK2: VWCarInfo("Volkswagen Touran 2017"),
CAR.TRANSPORTER_T61: [ CAR.TRANSPORTER_T61: [
VWCarInfo("Volkswagen Caravelle 2020"), VWCarInfo("Volkswagen Caravelle 2020"),
VWCarInfo("Volkswagen California 2021"), VWCarInfo("Volkswagen California 2021"),
], ],
CAR.TROC_MK1: VWCarInfo("Volkswagen T-Roc 2021", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0]), CAR.TROC_MK1: VWCarInfo("Volkswagen T-Roc 2021", footnotes=[Footnote.VW_MQB_A0]),
CAR.AUDI_A3_MK3: [ CAR.AUDI_A3_MK3: [
VWCarInfo("Audi A3 2014-19"), VWCarInfo("Audi A3 2014-19"),
VWCarInfo("Audi A3 Sportback e-tron 2017-18"), VWCarInfo("Audi A3 Sportback e-tron 2017-18"),
@ -229,11 +231,11 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = {
CAR.AUDI_Q3_MK2: VWCarInfo("Audi Q3 2019-23"), CAR.AUDI_Q3_MK2: VWCarInfo("Audi Q3 2019-23"),
CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"), CAR.SEAT_ATECA_MK1: VWCarInfo("SEAT Ateca 2018"),
CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"), CAR.SEAT_LEON_MK3: VWCarInfo("SEAT Leon 2014-20"),
CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0, Footnote.KAMIQ]), CAR.SKODA_KAMIQ_MK1: VWCarInfo("Škoda Kamiq 2021", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]),
CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21"), CAR.SKODA_KAROQ_MK1: VWCarInfo("Škoda Karoq 2019-21"),
CAR.SKODA_KODIAQ_MK1: VWCarInfo("Škoda Kodiaq 2018-19"), CAR.SKODA_KODIAQ_MK1: VWCarInfo("Škoda Kodiaq 2018-19"),
CAR.SKODA_SCALA_MK1: VWCarInfo("Škoda Scala 2020", footnotes=[Footnote.VW_EXP_LONG, Footnote.VW_MQB_A0]), CAR.SKODA_SCALA_MK1: VWCarInfo("Škoda Scala 2020", footnotes=[Footnote.VW_MQB_A0]),
CAR.SKODA_SUPERB_MK3: VWCarInfo("Škoda Superb 2015-18"), CAR.SKODA_SUPERB_MK3: VWCarInfo("Škoda Superb 2015-22"),
CAR.SKODA_OCTAVIA_MK3: [ CAR.SKODA_OCTAVIA_MK3: [
VWCarInfo("Škoda Octavia 2015, 2018-19"), VWCarInfo("Škoda Octavia 2015, 2018-19"),
VWCarInfo("Škoda Octavia RS 2016"), VWCarInfo("Škoda Octavia RS 2016"),
@ -278,12 +280,14 @@ FW_VERSIONS = {
CAR.ARTEON_MK1: { CAR.ARTEON_MK1: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x873G0906259F \xf1\x890004', b'\xf1\x873G0906259F \xf1\x890004',
b'\xf1\x873G0906259N \xf1\x890004',
b'\xf1\x873G0906259P \xf1\x890001', b'\xf1\x873G0906259P \xf1\x890001',
b'\xf1\x875NA907115H \xf1\x890002', b'\xf1\x875NA907115H \xf1\x890002',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158L \xf1\x893611', b'\xf1\x8709G927158L \xf1\x893611',
b'\xf1\x870GC300011L \xf1\x891401', b'\xf1\x870GC300011L \xf1\x891401',
b'\xf1\x870GC300014M \xf1\x892802',
b'\xf1\x870GC300040P \xf1\x891401', b'\xf1\x870GC300040P \xf1\x891401',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
@ -295,8 +299,10 @@ FW_VERSIONS = {
b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571B41815A1', b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571B41815A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571B00817A1', b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571B00817A1',
b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\00567B0020800', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\00567B0020800',
b'\xf1\x875WA907145M \xf1\x891051\xf1\x82\x002MB4092M7N',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x872Q0907572AA\xf1\x890396',
b'\xf1\x872Q0907572T \xf1\x890383', b'\xf1\x872Q0907572T \xf1\x890383',
b'\xf1\x875Q0907572J \xf1\x890654', b'\xf1\x875Q0907572J \xf1\x890654',
], ],
@ -314,11 +320,13 @@ FW_VERSIONS = {
b'\xf1\x8703H906026J \xf1\x899971', b'\xf1\x8703H906026J \xf1\x899971',
b'\xf1\x8703H906026S \xf1\x896693', b'\xf1\x8703H906026S \xf1\x896693',
b'\xf1\x8703H906026S \xf1\x899970', b'\xf1\x8703H906026S \xf1\x899970',
b'\xf1\x873CN906259 \xf1\x890005',
], ],
(Ecu.transmission, 0x7e1, None): [ (Ecu.transmission, 0x7e1, None): [
b'\xf1\x8709G927158A \xf1\x893387', b'\xf1\x8709G927158A \xf1\x893387',
b'\xf1\x8709G927158DR\xf1\x893536', b'\xf1\x8709G927158DR\xf1\x893536',
b'\xf1\x8709G927158DR\xf1\x893742', b'\xf1\x8709G927158DR\xf1\x893742',
b'\xf1\x8709G927158F \xf1\x893489',
b'\xf1\x8709G927158FT\xf1\x893835', b'\xf1\x8709G927158FT\xf1\x893835',
b'\xf1\x8709G927158GL\xf1\x893939', b'\xf1\x8709G927158GL\xf1\x893939',
], ],
@ -332,6 +340,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x712, None): [ (Ecu.eps, 0x712, None): [
b'\xf1\x873QF909144B \xf1\x891582\xf1\x82\00571B60924A1', b'\xf1\x873QF909144B \xf1\x891582\xf1\x82\00571B60924A1',
b'\xf1\x873QF909144B \xf1\x891582\xf1\x82\x0571B6G920A1', b'\xf1\x873QF909144B \xf1\x891582\xf1\x82\x0571B6G920A1',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820528B6080105',
b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820528B6090105', b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820528B6090105',
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
@ -371,6 +380,7 @@ FW_VERSIONS = {
b'\xf1\x870EA906016Q \xf1\x895993', b'\xf1\x870EA906016Q \xf1\x895993',
b'\xf1\x870EA906016S \xf1\x897207', b'\xf1\x870EA906016S \xf1\x897207',
b'\xf1\x875G0906259 \xf1\x890007', b'\xf1\x875G0906259 \xf1\x890007',
b'\xf1\x875G0906259D \xf1\x890002',
b'\xf1\x875G0906259J \xf1\x890002', b'\xf1\x875G0906259J \xf1\x890002',
b'\xf1\x875G0906259L \xf1\x890002', b'\xf1\x875G0906259L \xf1\x890002',
b'\xf1\x875G0906259N \xf1\x890003', b'\xf1\x875G0906259N \xf1\x890003',
@ -406,6 +416,7 @@ FW_VERSIONS = {
b'\xf1\x870D9300012 \xf1\x895045', b'\xf1\x870D9300012 \xf1\x895045',
b'\xf1\x870D9300014M \xf1\x895004', b'\xf1\x870D9300014M \xf1\x895004',
b'\xf1\x870D9300014Q \xf1\x895006', b'\xf1\x870D9300014Q \xf1\x895006',
b'\xf1\x870D9300020J \xf1\x894902',
b'\xf1\x870D9300020Q \xf1\x895201', b'\xf1\x870D9300020Q \xf1\x895201',
b'\xf1\x870D9300020S \xf1\x895201', b'\xf1\x870D9300020S \xf1\x895201',
b'\xf1\x870D9300040A \xf1\x893613', b'\xf1\x870D9300040A \xf1\x893613',
@ -453,6 +464,7 @@ FW_VERSIONS = {
b'\xf1\x873Q0909144F \xf1\x895043\xf1\x82\x0561A01612A0', b'\xf1\x873Q0909144F \xf1\x895043\xf1\x82\x0561A01612A0',
b'\xf1\x873Q0909144H \xf1\x895061\xf1\x82\x0566A0J612A1', b'\xf1\x873Q0909144H \xf1\x895061\xf1\x82\x0566A0J612A1',
b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A00514A1', b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A00514A1',
b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A01613A1',
b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A0J712A1', b'\xf1\x873Q0909144J \xf1\x895063\xf1\x82\x0566A0J712A1',
b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571A0J714A1', b'\xf1\x873Q0909144K \xf1\x895072\xf1\x82\x0571A0J714A1',
b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A0JA15A1', b'\xf1\x873Q0909144L \xf1\x895081\xf1\x82\x0571A0JA15A1',
@ -1064,6 +1076,7 @@ FW_VERSIONS = {
}, },
CAR.SKODA_SUPERB_MK3: { CAR.SKODA_SUPERB_MK3: {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906026ET\xf1\x891343',
b'\xf1\x8704L906026FP\xf1\x891196', b'\xf1\x8704L906026FP\xf1\x891196',
b'\xf1\x8704L906026KB\xf1\x894071', b'\xf1\x8704L906026KB\xf1\x894071',
b'\xf1\x8704L906026KD\xf1\x894798', b'\xf1\x8704L906026KD\xf1\x894798',
@ -1074,9 +1087,11 @@ FW_VERSIONS = {
b'\xf1\x870CW300042H \xf1\x891601', b'\xf1\x870CW300042H \xf1\x891601',
b'\xf1\x870D9300011T \xf1\x894801', b'\xf1\x870D9300011T \xf1\x894801',
b'\xf1\x870D9300012 \xf1\x894940', b'\xf1\x870D9300012 \xf1\x894940',
b'\xf1\x870D9300041H \xf1\x894905',
b'\xf1\x870GC300043 \xf1\x892301', b'\xf1\x870GC300043 \xf1\x892301',
], ],
(Ecu.srs, 0x715, None): [ (Ecu.srs, 0x715, None): [
b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\x12111200111121001121110012211292221111',
b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\022111200111121001121118112231292221111', b'\xf1\x875Q0959655AE\xf1\x890130\xf1\x82\022111200111121001121118112231292221111',
b'\xf1\x875Q0959655AK\xf1\x890130\xf1\x82\022111200111121001121110012211292221111', b'\xf1\x875Q0959655AK\xf1\x890130\xf1\x82\022111200111121001121110012211292221111',
b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\02331310031313100313131013141319331413100', b'\xf1\x875Q0959655BH\xf1\x890336\xf1\x82\02331310031313100313131013141319331413100',

@ -12,12 +12,11 @@ import cereal.messaging as messaging
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from panda import ALTERNATIVE_EXPERIENCE from panda import ALTERNATIVE_EXPERIENCE
from system.swaglog import cloudlog from system.swaglog import cloudlog
from system.version import is_tested_branch, get_short_branch from system.version import is_release_branch, get_short_branch
from selfdrive.boardd.boardd import can_list_to_can_capnp from selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can from selfdrive.car.car_helpers import get_car, get_startup_event, get_one_can
from selfdrive.controls.lib.lateral_planner import CAMERA_OFFSET from selfdrive.controls.lib.lateral_planner import CAMERA_OFFSET
from selfdrive.controls.lib.drive_helpers import V_CRUISE_INITIAL, update_v_cruise, initialize_v_cruise from selfdrive.controls.lib.drive_helpers import VCruiseHelper, get_lag_adjusted_curvature
from selfdrive.controls.lib.drive_helpers import get_lag_adjusted_curvature
from selfdrive.controls.lib.latcontrol import LatControl from selfdrive.controls.lib.latcontrol import LatControl
from selfdrive.controls.lib.longcontrol import LongControl from selfdrive.controls.lib.longcontrol import LongControl
from selfdrive.controls.lib.latcontrol_pid import LatControlPID from selfdrive.controls.lib.latcontrol_pid import LatControlPID
@ -49,7 +48,6 @@ Desire = log.LateralPlan.Desire
LaneChangeState = log.LateralPlan.LaneChangeState LaneChangeState = log.LateralPlan.LaneChangeState
LaneChangeDirection = log.LateralPlan.LaneChangeDirection LaneChangeDirection = log.LateralPlan.LaneChangeDirection
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
ButtonEvent = car.CarState.ButtonEvent
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
SafetyModel = car.CarParams.SafetyModel SafetyModel = car.CarParams.SafetyModel
@ -134,7 +132,7 @@ class Controls:
safety_config.safetyModel = car.CarParams.SafetyModel.noOutput safety_config.safetyModel = car.CarParams.SafetyModel.noOutput
self.CP.safetyConfigs = [safety_config] self.CP.safetyConfigs = [safety_config]
if is_tested_branch(): if is_release_branch():
self.CP.experimentalLongitudinalAvailable = False self.CP.experimentalLongitudinalAvailable = False
# Write CarParams for radard # Write CarParams for radard
@ -173,9 +171,6 @@ class Controls:
self.active = False self.active = False
self.can_rcv_timeout = False self.can_rcv_timeout = False
self.soft_disable_timer = 0 self.soft_disable_timer = 0
self.v_cruise_kph = V_CRUISE_INITIAL
self.v_cruise_cluster_kph = V_CRUISE_INITIAL
self.v_cruise_kph_last = 0
self.mismatch_counter = 0 self.mismatch_counter = 0
self.cruise_mismatch_counter = 0 self.cruise_mismatch_counter = 0
self.can_rcv_timeout_counter = 0 self.can_rcv_timeout_counter = 0
@ -185,11 +180,11 @@ class Controls:
self.events_prev = [] self.events_prev = []
self.current_alert_types = [ET.PERMANENT] self.current_alert_types = [ET.PERMANENT]
self.logged_comm_issue = None self.logged_comm_issue = None
self.button_timers = {ButtonEvent.Type.decelCruise: 0, ButtonEvent.Type.accelCruise: 0}
self.last_actuators = car.CarControl.Actuators.new_message() self.last_actuators = car.CarControl.Actuators.new_message()
self.steer_limited = False self.steer_limited = False
self.desired_curvature = 0.0 self.desired_curvature = 0.0
self.desired_curvature_rate = 0.0 self.desired_curvature_rate = 0.0
self.v_cruise_helper = VCruiseHelper(self.CP)
# TODO: no longer necessary, aside from process replay # TODO: no longer necessary, aside from process replay
self.sm['liveParameters'].valid = True self.sm['liveParameters'].valid = True
@ -219,7 +214,7 @@ class Controls:
controls_state = Params().get("ReplayControlsState") controls_state = Params().get("ReplayControlsState")
if controls_state is not None: if controls_state is not None:
controls_state = log.ControlsState.from_bytes(controls_state) controls_state = log.ControlsState.from_bytes(controls_state)
self.v_cruise_kph = controls_state.vCruise self.v_cruise_helper.v_cruise_kph = controls_state.vCruise
if any(ps.controlsAllowed for ps in self.sm['pandaStates']): if any(ps.controlsAllowed for ps in self.sm['pandaStates']):
self.state = State.enabled self.state = State.enabled
@ -245,7 +240,7 @@ class Controls:
# Block resume if cruise never previously enabled # Block resume if cruise never previously enabled
resume_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in CS.buttonEvents) resume_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in CS.buttonEvents)
if not self.CP.pcmCruise and self.v_cruise_kph == V_CRUISE_INITIAL and resume_pressed: if not self.CP.pcmCruise and not self.v_cruise_helper.v_cruise_initialized and resume_pressed:
self.events.add(EventName.resumeBlocked) self.events.add(EventName.resumeBlocked)
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0 # Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
@ -254,6 +249,9 @@ class Controls:
(CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)): (CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)):
self.events.add(EventName.pedalPressed) self.events.add(EventName.pedalPressed)
if CS.brakePressed and CS.standstill:
self.events.add(EventName.preEnableStandstill)
if CS.gasPressed: if CS.gasPressed:
self.events.add(EventName.gasPressedOverride) self.events.add(EventName.gasPressedOverride)
@ -280,7 +278,7 @@ class Controls:
# self.events.add(EventName.highCpuUsage) # self.events.add(EventName.highCpuUsage)
# Alert if fan isn't spinning for 5 seconds # Alert if fan isn't spinning for 5 seconds
if self.sm['peripheralState'].pandaType == PandaType.dos: if self.sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown:
if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50: if self.sm['peripheralState'].fanSpeedRpm == 0 and self.sm['deviceState'].fanSpeedPercentDesired > 50:
if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0: if (self.sm.frame - self.last_functional_fan_frame) * DT_CTRL > 5.0:
self.events.add(EventName.fanMalfunction) self.events.add(EventName.fanMalfunction)
@ -422,16 +420,6 @@ class Controls:
if self.sm['liveLocationKalman'].excessiveResets: if self.sm['liveLocationKalman'].excessiveResets:
self.events.add(EventName.localizerMalfunction) self.events.add(EventName.localizerMalfunction)
# Only allow engagement with brake pressed when stopped behind another stopped car
speeds = self.sm['longitudinalPlan'].speeds
if len(speeds) > 1:
v_future = speeds[-1]
else:
v_future = 100.0
if CS.brakePressed and v_future >= self.CP.vEgoStarting \
and self.CP.openpilotLongitudinalControl and CS.vEgo < 0.3:
self.events.add(EventName.noTarget)
def data_sample(self): def data_sample(self):
"""Receive data from sockets and update carState""" """Receive data from sockets and update carState"""
@ -478,20 +466,7 @@ class Controls:
def state_transition(self, CS): def state_transition(self, CS):
"""Compute conditional state transitions and execute actions on state transitions""" """Compute conditional state transitions and execute actions on state transitions"""
self.v_cruise_kph_last = self.v_cruise_kph self.v_cruise_helper.update_v_cruise(CS, self.enabled, self.is_metric)
if CS.cruiseState.available:
# if stock cruise is completely disabled, then we can use our own set speed logic
if not self.CP.pcmCruise:
self.v_cruise_kph = update_v_cruise(self.v_cruise_kph, CS.vEgo, CS.gasPressed, CS.buttonEvents,
self.button_timers, self.enabled, self.is_metric)
self.v_cruise_cluster_kph = self.v_cruise_kph
else:
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH
else:
self.v_cruise_kph = V_CRUISE_INITIAL
self.v_cruise_cluster_kph = V_CRUISE_INITIAL
# decrement the soft disable timer at every step, as it's reset on # decrement the soft disable timer at every step, as it's reset on
# entrance in SOFT_DISABLING state # entrance in SOFT_DISABLING state
@ -536,10 +511,7 @@ class Controls:
# PRE ENABLING # PRE ENABLING
elif self.state == State.preEnabled: elif self.state == State.preEnabled:
if self.events.any(ET.NO_ENTRY): if not self.events.any(ET.PRE_ENABLE):
self.state = State.disabled
self.current_alert_types.append(ET.NO_ENTRY)
elif not self.events.any(ET.PRE_ENABLE):
self.state = State.enabled self.state = State.enabled
else: else:
self.current_alert_types.append(ET.PRE_ENABLE) self.current_alert_types.append(ET.PRE_ENABLE)
@ -569,9 +541,7 @@ class Controls:
else: else:
self.state = State.enabled self.state = State.enabled
self.current_alert_types.append(ET.ENABLE) self.current_alert_types.append(ET.ENABLE)
if not self.CP.pcmCruise: self.v_cruise_helper.initialize_v_cruise(CS)
self.v_cruise_kph = initialize_v_cruise(CS.vEgo, CS.buttonEvents, self.v_cruise_kph_last)
self.v_cruise_cluster_kph = self.v_cruise_kph
# Check if openpilot is engaged and actuators are enabled # Check if openpilot is engaged and actuators are enabled
self.enabled = self.state in ENABLED_STATES self.enabled = self.state in ENABLED_STATES
@ -602,7 +572,7 @@ class Controls:
# Check which actuators can be enabled # Check which actuators can be enabled
CC.latActive = self.active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and \ CC.latActive = self.active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and \
CS.vEgo > self.CP.minSteerSpeed and not CS.standstill CS.vEgo > self.CP.minSteerSpeed and not CS.standstill
CC.longActive = self.active and not self.events.any(ET.OVERRIDE_LONGITUDINAL) and self.CP.openpilotLongitudinalControl CC.longActive = self.enabled and not self.events.any(ET.OVERRIDE_LONGITUDINAL) and self.CP.openpilotLongitudinalControl
actuators = CC.actuators actuators = CC.actuators
actuators.longControlState = self.LoC.long_control_state actuators.longControlState = self.LoC.long_control_state
@ -619,7 +589,7 @@ class Controls:
if not self.joystick_mode: if not self.joystick_mode:
# accel PID loop # accel PID loop
pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, self.v_cruise_kph * CV.KPH_TO_MS) pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, self.v_cruise_helper.v_cruise_kph * CV.KPH_TO_MS)
t_since_plan = (self.sm.frame - self.sm.rcv_frame['longitudinalPlan']) * DT_CTRL t_since_plan = (self.sm.frame - self.sm.rcv_frame['longitudinalPlan']) * DT_CTRL
actuators.accel = self.LoC.update(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan) actuators.accel = self.LoC.update(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan)
@ -683,16 +653,6 @@ class Controls:
return CC, lac_log return CC, lac_log
def update_button_timers(self, buttonEvents):
# increment timer for buttons still pressed
for k in self.button_timers:
if self.button_timers[k] > 0:
self.button_timers[k] += 1
for b in buttonEvents:
if b.type.raw in self.button_timers:
self.button_timers[b.type.raw] = 1 if b.pressed else 0
def publish_logs(self, CS, start_time, CC, lac_log): def publish_logs(self, CS, start_time, CC, lac_log):
"""Send actuators and hud commands to the car, send controlsstate and MPC logging""" """Send actuators and hud commands to the car, send controlsstate and MPC logging"""
@ -715,7 +675,7 @@ class Controls:
CC.cruiseControl.resume = self.enabled and CS.cruiseState.standstill and speeds[-1] > 0.1 CC.cruiseControl.resume = self.enabled and CS.cruiseState.standstill and speeds[-1] > 0.1
hudControl = CC.hudControl hudControl = CC.hudControl
hudControl.setSpeed = float(self.v_cruise_cluster_kph * CV.KPH_TO_MS) hudControl.setSpeed = float(self.v_cruise_helper.v_cruise_cluster_kph * CV.KPH_TO_MS)
hudControl.speedVisible = self.enabled hudControl.speedVisible = self.enabled
hudControl.lanesVisible = self.enabled hudControl.lanesVisible = self.enabled
hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead
@ -798,8 +758,8 @@ class Controls:
controlsState.engageable = not self.events.any(ET.NO_ENTRY) controlsState.engageable = not self.events.any(ET.NO_ENTRY)
controlsState.longControlState = self.LoC.long_control_state controlsState.longControlState = self.LoC.long_control_state
controlsState.vPid = float(self.LoC.v_pid) controlsState.vPid = float(self.LoC.v_pid)
controlsState.vCruise = float(self.v_cruise_kph) controlsState.vCruise = float(self.v_cruise_helper.v_cruise_kph)
controlsState.vCruiseCluster = float(self.v_cruise_cluster_kph) controlsState.vCruiseCluster = float(self.v_cruise_helper.v_cruise_cluster_kph)
controlsState.upAccelCmd = float(self.LoC.pid.p) controlsState.upAccelCmd = float(self.LoC.pid.p)
controlsState.uiAccelCmd = float(self.LoC.pid.i) controlsState.uiAccelCmd = float(self.LoC.pid.i)
controlsState.ufAccelCmd = float(self.LoC.pid.f) controlsState.ufAccelCmd = float(self.LoC.pid.f)
@ -807,6 +767,7 @@ class Controls:
controlsState.startMonoTime = int(start_time * 1e9) controlsState.startMonoTime = int(start_time * 1e9)
controlsState.forceDecel = bool(force_decel) controlsState.forceDecel = bool(force_decel)
controlsState.canErrorCounter = self.can_rcv_timeout_counter controlsState.canErrorCounter = self.can_rcv_timeout_counter
controlsState.experimentalMode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl
lat_tuning = self.CP.lateralTuning.which() lat_tuning = self.CP.lateralTuning.which()
if self.joystick_mode: if self.joystick_mode:
@ -880,7 +841,6 @@ class Controls:
self.publish_logs(CS, start_time, CC, lac_log) self.publish_logs(CS, start_time, CC, lac_log)
self.prof.checkpoint("Sent") self.prof.checkpoint("Sent")
self.update_button_timers(CS.buttonEvents)
self.CS_prev = CS self.CS_prev = CS
def controlsd_thread(self): def controlsd_thread(self):
@ -889,6 +849,7 @@ class Controls:
self.rk.monitor_time() self.rk.monitor_time()
self.prof.display() self.prof.display()
def main(sm=None, pm=None, logcan=None): def main(sm=None, pm=None, logcan=None):
controls = Controls(sm, pm, logcan) controls = Controls(sm, pm, logcan)
controls.controlsd_thread() controls.controlsd_thread()

@ -12,6 +12,7 @@ V_CRUISE_MAX = 145 # kph
V_CRUISE_MIN = 8 # kph V_CRUISE_MIN = 8 # kph
V_CRUISE_ENABLE_MIN = 40 # kph V_CRUISE_ENABLE_MIN = 40 # kph
V_CRUISE_INITIAL = 255 # kph V_CRUISE_INITIAL = 255 # kph
IMPERIAL_INCREMENT = 1.6 # should be CV.MPH_TO_KPH, but this causes rounding errors
MIN_SPEED = 1.0 MIN_SPEED = 1.0
LAT_MPC_N = 16 LAT_MPC_N = 16
@ -22,6 +23,7 @@ CAR_ROTATION_RADIUS = 0.0
# EU guidelines # EU guidelines
MAX_LATERAL_JERK = 5.0 MAX_LATERAL_JERK = 5.0
ButtonEvent = car.CarState.ButtonEvent
ButtonType = car.CarState.ButtonEvent.Type ButtonType = car.CarState.ButtonEvent.Type
CRUISE_LONG_PRESS = 50 CRUISE_LONG_PRESS = 50
CRUISE_NEAREST_FUNC = { CRUISE_NEAREST_FUNC = {
@ -34,68 +36,127 @@ CRUISE_INTERVAL_SIGN = {
} }
def apply_deadzone(error, deadzone): class VCruiseHelper:
if error > deadzone: def __init__(self, CP):
error -= deadzone self.CP = CP
elif error < - deadzone: self.v_cruise_kph = V_CRUISE_INITIAL
error += deadzone self.v_cruise_cluster_kph = V_CRUISE_INITIAL
self.v_cruise_kph_last = 0
self.button_timers = {ButtonType.decelCruise: 0, ButtonType.accelCruise: 0}
self.button_change_states = {btn: {"standstill": False, "enabled": False} for btn in self.button_timers}
@property
def v_cruise_initialized(self):
return self.v_cruise_kph != V_CRUISE_INITIAL
def update_v_cruise(self, CS, enabled, is_metric):
self.v_cruise_kph_last = self.v_cruise_kph
if CS.cruiseState.available:
if not self.CP.pcmCruise:
# if stock cruise is completely disabled, then we can use our own set speed logic
self._update_v_cruise_non_pcm(CS, enabled, is_metric)
self.v_cruise_cluster_kph = self.v_cruise_kph
self.update_button_timers(CS, enabled)
else: else:
error = 0. self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
return error self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH
else:
self.v_cruise_kph = V_CRUISE_INITIAL
def rate_limit(new_value, last_value, dw_step, up_step): self.v_cruise_cluster_kph = V_CRUISE_INITIAL
return clip(new_value, last_value + dw_step, last_value + up_step)
def update_v_cruise(v_cruise_kph, v_ego, gas_pressed, buttonEvents, button_timers, enabled, metric): def _update_v_cruise_non_pcm(self, CS, enabled, is_metric):
# handle button presses. TODO: this should be in state_control, but a decelCruise press # handle button presses. TODO: this should be in state_control, but a decelCruise press
# would have the effect of both enabling and changing speed is checked after the state transition # would have the effect of both enabling and changing speed is checked after the state transition
if not enabled: if not enabled:
return v_cruise_kph return
long_press = False long_press = False
button_type = None button_type = None
# should be CV.MPH_TO_KPH, but this causes rounding errors v_cruise_delta = 1. if is_metric else IMPERIAL_INCREMENT
v_cruise_delta = 1. if metric else 1.6
for b in buttonEvents: for b in CS.buttonEvents:
if b.type.raw in button_timers and not b.pressed: if b.type.raw in self.button_timers and not b.pressed:
if button_timers[b.type.raw] > CRUISE_LONG_PRESS: if self.button_timers[b.type.raw] > CRUISE_LONG_PRESS:
return v_cruise_kph # end long press return # end long press
button_type = b.type.raw button_type = b.type.raw
break break
else: else:
for k in button_timers.keys(): for k in self.button_timers.keys():
if button_timers[k] and button_timers[k] % CRUISE_LONG_PRESS == 0: if self.button_timers[k] and self.button_timers[k] % CRUISE_LONG_PRESS == 0:
button_type = k button_type = k
long_press = True long_press = True
break break
if button_type: if button_type is None:
return
# Don't adjust speed when pressing resume to exit standstill
cruise_standstill = self.button_change_states[button_type]["standstill"] or CS.cruiseState.standstill
if button_type == ButtonType.accelCruise and cruise_standstill:
return
# Don't adjust speed if we've enabled since the button was depressed (some ports enable on rising edge)
if not self.button_change_states[button_type]["enabled"]:
return
v_cruise_delta = v_cruise_delta * (5 if long_press else 1) v_cruise_delta = v_cruise_delta * (5 if long_press else 1)
if long_press and v_cruise_kph % v_cruise_delta != 0: # partial interval if long_press and self.v_cruise_kph % v_cruise_delta != 0: # partial interval
v_cruise_kph = CRUISE_NEAREST_FUNC[button_type](v_cruise_kph / v_cruise_delta) * v_cruise_delta self.v_cruise_kph = CRUISE_NEAREST_FUNC[button_type](self.v_cruise_kph / v_cruise_delta) * v_cruise_delta
else: else:
v_cruise_kph += v_cruise_delta * CRUISE_INTERVAL_SIGN[button_type] self.v_cruise_kph += v_cruise_delta * CRUISE_INTERVAL_SIGN[button_type]
# If set is pressed while overriding, clip cruise speed to minimum of vEgo # If set is pressed while overriding, clip cruise speed to minimum of vEgo
if gas_pressed and button_type in (ButtonType.decelCruise, ButtonType.setCruise): if CS.gasPressed and button_type in (ButtonType.decelCruise, ButtonType.setCruise):
v_cruise_kph = max(v_cruise_kph, v_ego * CV.MS_TO_KPH) self.v_cruise_kph = max(self.v_cruise_kph, CS.vEgo * CV.MS_TO_KPH)
v_cruise_kph = clip(round(v_cruise_kph, 1), V_CRUISE_MIN, V_CRUISE_MAX) self.v_cruise_kph = clip(round(self.v_cruise_kph, 1), V_CRUISE_MIN, V_CRUISE_MAX)
return v_cruise_kph def update_button_timers(self, CS, enabled):
# increment timer for buttons still pressed
for k in self.button_timers:
if self.button_timers[k] > 0:
self.button_timers[k] += 1
for b in CS.buttonEvents:
if b.type.raw in self.button_timers:
# Start/end timer and store current state on change of button pressed
self.button_timers[b.type.raw] = 1 if b.pressed else 0
self.button_change_states[b.type.raw] = {"standstill": CS.cruiseState.standstill, "enabled": enabled}
def initialize_v_cruise(self, CS):
# initializing is handled by the PCM
if self.CP.pcmCruise:
return
def initialize_v_cruise(v_ego, buttonEvents, v_cruise_last):
for b in buttonEvents:
# 250kph or above probably means we never had a set speed # 250kph or above probably means we never had a set speed
if b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) and v_cruise_last < 250: if any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents) and self.v_cruise_kph_last < 250:
return v_cruise_last self.v_cruise_kph = self.v_cruise_kph_last
else:
self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, V_CRUISE_ENABLE_MIN, V_CRUISE_MAX)))
return int(round(clip(v_ego * CV.MS_TO_KPH, V_CRUISE_ENABLE_MIN, V_CRUISE_MAX))) self.v_cruise_cluster_kph = self.v_cruise_kph
def apply_deadzone(error, deadzone):
if error > deadzone:
error -= deadzone
elif error < - deadzone:
error += deadzone
else:
error = 0.
return error
def apply_center_deadzone(error, deadzone):
if (error > - deadzone) and (error < deadzone):
error = 0.
return error
def rate_limit(new_value, last_value, dw_step, up_step):
return clip(new_value, last_value + dw_step, last_value + up_step)
def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates): def get_lag_adjusted_curvature(CP, v_ego, psis, curvatures, curvature_rates):

@ -250,10 +250,9 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag
def no_gps_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: def no_gps_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
gps_integrated = sm['peripheralState'].pandaType in (log.PandaState.PandaType.uno, log.PandaState.PandaType.dos)
return Alert( return Alert(
"Poor GPS reception", "Poor GPS reception",
"Hardware malfunctioning if sky is visible" if gps_integrated else "Check GPS antenna placement", "Hardware malfunctioning if sky is visible",
AlertStatus.normal, AlertSize.mid, AlertStatus.normal, AlertSize.mid,
Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=300.) Priority.LOWER, VisualAlert.none, AudibleAlert.none, .2, creation_delay=300.)
@ -502,7 +501,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
EventName.resumeRequired: { EventName.resumeRequired: {
ET.WARNING: Alert( ET.WARNING: Alert(
"STOPPED", "STOPPED",
"Press Resume to Go", "Press Resume to Exit Standstill",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, .2), Priority.LOW, VisualAlert.none, AudibleAlert.none, .2),
}, },
@ -616,9 +615,9 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
visual_alert=VisualAlert.brakePressed), visual_alert=VisualAlert.brakePressed),
}, },
EventName.pedalPressedPreEnable: { EventName.preEnableStandstill: {
ET.PRE_ENABLE: Alert( ET.PRE_ENABLE: Alert(
"Release Pedal to Engage", "Release Brake to Engage",
"", "",
AlertStatus.normal, AlertSize.small, AlertStatus.normal, AlertSize.small,
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1, creation_delay=1.), Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .1, creation_delay=1.),
@ -917,15 +916,6 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
ET.NO_ENTRY: NoEntryAlert("Harness Relay Malfunction"), ET.NO_ENTRY: NoEntryAlert("Harness Relay Malfunction"),
}, },
EventName.noTarget: {
ET.IMMEDIATE_DISABLE: Alert(
"openpilot Canceled",
"No close lead car",
AlertStatus.normal, AlertSize.mid,
Priority.HIGH, VisualAlert.none, AudibleAlert.disengage, 3.),
ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"),
},
EventName.speedTooLow: { EventName.speedTooLow: {
ET.IMMEDIATE_DISABLE: Alert( ET.IMMEDIATE_DISABLE: Alert(
"openpilot Canceled", "openpilot Canceled",

@ -10,6 +10,8 @@ LongCtrlState = car.CarControl.Actuators.LongControlState
def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, def long_control_state_trans(CP, active, long_control_state, v_ego, v_target,
v_target_1sec, brake_pressed, cruise_standstill): v_target_1sec, brake_pressed, cruise_standstill):
# Ignore cruise standstill if car has a gas interceptor
cruise_standstill = cruise_standstill and not CP.enableGasInterceptor
accelerating = v_target_1sec > v_target accelerating = v_target_1sec > v_target
planned_stop = (v_target < CP.vEgoStopping and planned_stop = (v_target < CP.vEgoStopping and
v_target_1sec < CP.vEgoStopping and v_target_1sec < CP.vEgoStopping and

@ -352,7 +352,7 @@ class LongitudinalMpc:
x_and_cruise = np.column_stack([x, cruise_target]) x_and_cruise = np.column_stack([x, cruise_target])
x = np.min(x_and_cruise, axis=1) x = np.min(x_and_cruise, axis=1)
self.source = 'e2e' if x_and_cruise[0,0] < x_and_cruise[0,1] else 'cruise' self.source = 'e2e' if x_and_cruise[1,0] < x_and_cruise[1,1] else 'cruise'
else: else:
raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner update') raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner update')

@ -6,7 +6,6 @@ from common.numpy_fast import clip, interp
import cereal.messaging as messaging import cereal.messaging as messaging
from common.conversions import Conversions as CV from common.conversions import Conversions as CV
from common.filter_simple import FirstOrderFilter from common.filter_simple import FirstOrderFilter
from common.params import Params
from common.realtime import DT_MDL from common.realtime import DT_MDL
from selfdrive.modeld.constants import T_IDXS from selfdrive.modeld.constants import T_IDXS
from selfdrive.controls.lib.longcontrol import LongCtrlState from selfdrive.controls.lib.longcontrol import LongCtrlState
@ -48,12 +47,7 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
class LongitudinalPlanner: class LongitudinalPlanner:
def __init__(self, CP, init_v=0.0, init_a=0.0): def __init__(self, CP, init_v=0.0, init_a=0.0):
self.CP = CP self.CP = CP
self.params = Params()
self.param_read_counter = 0
self.mpc = LongitudinalMpc() self.mpc = LongitudinalMpc()
self.read_param()
self.fcw = False self.fcw = False
self.a_desired = init_a self.a_desired = init_a
@ -65,10 +59,6 @@ class LongitudinalPlanner:
self.j_desired_trajectory = np.zeros(CONTROL_N) self.j_desired_trajectory = np.zeros(CONTROL_N)
self.solverExecutionTime = 0.0 self.solverExecutionTime = 0.0
def read_param(self):
e2e = self.params.get_bool('ExperimentalMode') and self.CP.openpilotLongitudinalControl
self.mpc.mode = 'blended' if e2e else 'acc'
@staticmethod @staticmethod
def parse_model(model_msg, model_error): def parse_model(model_msg, model_error):
if (len(model_msg.position.x) == 33 and if (len(model_msg.position.x) == 33 and
@ -85,10 +75,8 @@ class LongitudinalPlanner:
j = np.zeros(len(T_IDXS_MPC)) j = np.zeros(len(T_IDXS_MPC))
return x, v, a, j return x, v, a, j
def update(self, sm, read=True): def update(self, sm):
if self.param_read_counter % 50 == 0 and read: self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc'
self.read_param()
self.param_read_counter += 1
v_ego = sm['carState'].vEgo v_ego = sm['carState'].vEgo
v_cruise_kph = sm['controlsState'].vCruise v_cruise_kph = sm['controlsState'].vCruise

@ -1,12 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest
import numpy as np import numpy as np
from common.params import Params from parameterized import parameterized_class
import unittest
from selfdrive.controls.lib.drive_helpers import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_ENABLE_MIN, IMPERIAL_INCREMENT
from cereal import car
from common.conversions import Conversions as CV
from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver
def run_cruise_simulation(cruise, t_end=20.): ButtonEvent = car.CarState.ButtonEvent
ButtonType = car.CarState.ButtonEvent.Type
def run_cruise_simulation(cruise, e2e, t_end=20.):
man = Maneuver( man = Maneuver(
'', '',
duration=t_end, duration=t_end,
@ -16,24 +22,129 @@ def run_cruise_simulation(cruise, t_end=20.):
cruise_values=[cruise], cruise_values=[cruise],
prob_lead_values=[0.0], prob_lead_values=[0.0],
breakpoints=[0.], breakpoints=[0.],
e2e=e2e,
) )
valid, output = man.evaluate() valid, output = man.evaluate()
assert valid assert valid
return output[-1,3] return output[-1, 3]
class TestCruiseSpeed(unittest.TestCase): class TestCruiseSpeed(unittest.TestCase):
def test_cruise_speed(self): def test_cruise_speed(self):
params = Params()
for e2e in [False, True]: for e2e in [False, True]:
params.put_bool("ExperimentalMode", e2e)
for speed in np.arange(5, 40, 5): for speed in np.arange(5, 40, 5):
print(f'Testing {speed} m/s') print(f'Testing {speed} m/s')
cruise_speed = float(speed) cruise_speed = float(speed)
simulation_steady_state = run_cruise_simulation(cruise_speed) simulation_steady_state = run_cruise_simulation(cruise_speed, e2e)
self.assertAlmostEqual(simulation_steady_state, cruise_speed, delta=.01, msg=f'Did not reach {speed} m/s') self.assertAlmostEqual(simulation_steady_state, cruise_speed, delta=.01, msg=f'Did not reach {speed} m/s')
# TODO: test pcmCruise
@parameterized_class(('pcm_cruise',), [(False,)])
class TestVCruiseHelper(unittest.TestCase):
def setUp(self):
self.CP = car.CarParams(pcmCruise=self.pcm_cruise) # pylint: disable=E1101
self.v_cruise_helper = VCruiseHelper(self.CP)
self.reset_cruise_speed_state()
def reset_cruise_speed_state(self):
# Two resets previous cruise speed
for _ in range(2):
self.v_cruise_helper.update_v_cruise(car.CarState(cruiseState={"available": False}), enabled=False, is_metric=False)
def enable(self, v_ego):
# Simulates user pressing set with a current speed
self.v_cruise_helper.initialize_v_cruise(car.CarState(vEgo=v_ego))
def test_adjust_speed(self):
"""
Asserts speed changes on falling edges of buttons.
"""
self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS)
for btn in (ButtonType.accelCruise, ButtonType.decelCruise):
for pressed in (True, False):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=btn, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(CS, enabled=True, is_metric=False)
self.assertEqual(pressed, self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last)
def test_rising_edge_enable(self):
"""
Some car interfaces may enable on rising edge of a button,
ensure we don't adjust speed if enabled changes mid-press.
"""
# NOTE: enabled is always one frame behind the result from button press in controlsd
for enabled, pressed in ((False, False),
(False, True),
(True, False)):
CS = car.CarState(cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.decelCruise, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(CS, enabled=enabled, is_metric=False)
if pressed:
self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS)
# Expected diff on enabling. Speed should not change on falling edge of pressed
self.assertEqual(not pressed, self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last)
def test_resume_in_standstill(self):
"""
Asserts we don't increment set speed if user presses resume/accel to exit cruise standstill.
"""
self.enable(0)
for standstill in (True, False):
for pressed in (True, False):
CS = car.CarState(cruiseState={"available": True, "standstill": standstill})
CS.buttonEvents = [ButtonEvent(type=ButtonType.accelCruise, pressed=pressed)]
self.v_cruise_helper.update_v_cruise(CS, enabled=True, is_metric=False)
# speed should only update if not at standstill and button falling edge
should_equal = standstill or pressed
self.assertEqual(should_equal, self.v_cruise_helper.v_cruise_kph == self.v_cruise_helper.v_cruise_kph_last)
def test_set_gas_pressed(self):
"""
Asserts pressing set while enabled with gas pressed sets
the speed to the maximum of vEgo and current cruise speed.
"""
for v_ego in np.linspace(0, 100, 101):
self.reset_cruise_speed_state()
self.enable(V_CRUISE_ENABLE_MIN * CV.KPH_TO_MS)
# first decrement speed, then perform gas pressed logic
expected_v_cruise_kph = self.v_cruise_helper.v_cruise_kph - IMPERIAL_INCREMENT
expected_v_cruise_kph = max(expected_v_cruise_kph, v_ego * CV.MS_TO_KPH) # clip to min of vEgo
expected_v_cruise_kph = float(np.clip(round(expected_v_cruise_kph, 1), V_CRUISE_MIN, V_CRUISE_MAX))
CS = car.CarState(vEgo=float(v_ego), gasPressed=True, cruiseState={"available": True})
CS.buttonEvents = [ButtonEvent(type=ButtonType.decelCruise, pressed=False)]
self.v_cruise_helper.update_v_cruise(CS, enabled=True, is_metric=False)
# TODO: fix skipping first run due to enabled on rising edge exception
if v_ego == 0.0:
continue
self.assertEqual(expected_v_cruise_kph, self.v_cruise_helper.v_cruise_kph)
def test_initialize_v_cruise(self):
"""
Asserts allowed cruise speeds on enabling with SET.
"""
for v_ego in np.linspace(0, 100, 101):
self.reset_cruise_speed_state()
self.assertFalse(self.v_cruise_helper.v_cruise_initialized)
self.enable(float(v_ego))
self.assertTrue(V_CRUISE_ENABLE_MIN <= self.v_cruise_helper.v_cruise_kph <= V_CRUISE_MAX)
self.assertTrue(self.v_cruise_helper.v_cruise_initialized)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

@ -1,12 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import unittest import unittest
import numpy as np import numpy as np
from common.params import Params
from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import desired_follow_distance from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import desired_follow_distance
from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver from selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver
def run_following_distance_simulation(v_lead, t_end=100.0, e2e=False): def run_following_distance_simulation(v_lead, t_end=100.0, e2e=False):
man = Maneuver( man = Maneuver(
'', '',
@ -16,7 +15,7 @@ def run_following_distance_simulation(v_lead, t_end=100.0, e2e=False):
initial_distance_lead=100, initial_distance_lead=100,
speed_lead_values=[v_lead], speed_lead_values=[v_lead],
breakpoints=[0.], breakpoints=[0.],
e2e=e2e e2e=e2e,
) )
valid, output = man.evaluate() valid, output = man.evaluate()
assert valid assert valid
@ -25,13 +24,11 @@ def run_following_distance_simulation(v_lead, t_end=100.0, e2e=False):
class TestFollowingDistance(unittest.TestCase): class TestFollowingDistance(unittest.TestCase):
def test_following_distance(self): def test_following_distance(self):
params = Params()
for e2e in [False, True]: for e2e in [False, True]:
params.put_bool("ExperimentalMode", e2e)
for speed in np.arange(0, 40, 5): for speed in np.arange(0, 40, 5):
print(f'Testing {speed} m/s') print(f'Testing {speed} m/s')
v_lead = float(speed) v_lead = float(speed)
simulation_steady_state = run_following_distance_simulation(v_lead) simulation_steady_state = run_following_distance_simulation(v_lead, e2e=e2e)
correct_steady_state = desired_follow_distance(v_lead, v_lead) correct_steady_state = desired_follow_distance(v_lead, v_lead)
err_ratio = 0.2 if e2e else 0.1 err_ratio = 0.2 if e2e else 0.1
self.assertAlmostEqual(simulation_steady_state, correct_steady_state, delta=(err_ratio * correct_steady_state + .5)) self.assertAlmostEqual(simulation_steady_state, correct_steady_state, delta=(err_ratio * correct_steady_state + .5))

@ -79,7 +79,7 @@ class TestStateMachine(unittest.TestCase):
self.assertEqual(self.controlsd.state, State.disabled) self.assertEqual(self.controlsd.state, State.disabled)
def test_no_entry(self): def test_no_entry(self):
# disabled with enable events # Make sure noEntry keeps us disabled
for et in ENABLE_EVENT_TYPES: for et in ENABLE_EVENT_TYPES:
self.controlsd.events.add(make_event([ET.NO_ENTRY, et])) self.controlsd.events.add(make_event([ET.NO_ENTRY, et]))
self.controlsd.state_transition(self.CS) self.controlsd.state_transition(self.CS)
@ -87,11 +87,11 @@ class TestStateMachine(unittest.TestCase):
self.controlsd.events.clear() self.controlsd.events.clear()
def test_no_entry_pre_enable(self): def test_no_entry_pre_enable(self):
# preEnabled with preEnabled event # preEnabled with noEntry event
self.controlsd.state = State.preEnabled self.controlsd.state = State.preEnabled
self.controlsd.events.add(make_event([ET.NO_ENTRY, ET.PRE_ENABLE])) self.controlsd.events.add(make_event([ET.NO_ENTRY, ET.PRE_ENABLE]))
self.controlsd.state_transition(self.CS) self.controlsd.state_transition(self.CS)
self.assertEqual(self.controlsd.state, State.disabled) self.assertEqual(self.controlsd.state, State.preEnabled)
def test_maintain_states(self): def test_maintain_states(self):
# Given current state's event type, we should maintain state # Given current state's event type, we should maintain state

@ -68,7 +68,7 @@ if __name__ == "__main__":
CP = None CP = None
for msg in lr: for msg in lr:
if msg.which() == "pandaStates": if msg.which() == "pandaStates":
if msg.pandaStates[0].pandaType not in ('uno', 'blackPanda', 'dos'): if msg.pandaStates[0].pandaType in ('unknown', 'whitePanda', 'greyPanda', 'pedal'):
print("wrong panda type") print("wrong panda type")
break break

@ -187,6 +187,7 @@ class Laikad:
"gpsTimeOfWeek": tow, "gpsTimeOfWeek": tow,
"positionECEF": measurement_msg(value=ecef_pos.tolist(), std=pos_std.tolist(), valid=kf_valid), "positionECEF": measurement_msg(value=ecef_pos.tolist(), std=pos_std.tolist(), valid=kf_valid),
"velocityECEF": measurement_msg(value=ecef_vel.tolist(), std=vel_std.tolist(), valid=kf_valid), "velocityECEF": measurement_msg(value=ecef_vel.tolist(), std=vel_std.tolist(), valid=kf_valid),
# TODO std is incorrectly the dimension of the measurements and not 3D
"positionFixECEF": measurement_msg(value=self.last_pos_fix, std=self.last_pos_residual, valid=self.last_pos_fix_t == t), "positionFixECEF": measurement_msg(value=self.last_pos_fix, std=self.last_pos_residual, valid=self.last_pos_fix_t == t),
"ubloxMonoTime": gnss_mono_time, "ubloxMonoTime": gnss_mono_time,
"correctedMeasurements": meas_msgs "correctedMeasurements": meas_msgs

@ -26,4 +26,16 @@ extern "C" {
memcpy(std_buff, stdev.data(), sizeof(double) * stdev.size()); memcpy(std_buff, stdev.data(), sizeof(double) * stdev.size());
} }
bool is_gps_ok(Localizer *localizer){
return localizer->is_gps_ok();
}
bool are_inputs_ok(Localizer *localizer){
return localizer->are_inputs_ok();
}
void observation_timings_invalid_reset(Localizer *localizer){
localizer->observation_timings_invalid_reset();
}
} }

@ -19,6 +19,9 @@ const double VALID_TIME_SINCE_RESET = 1.0; // s
const double VALID_POS_STD = 50.0; // m const double VALID_POS_STD = 50.0; // m
const double MAX_RESET_TRACKER = 5.0; const double MAX_RESET_TRACKER = 5.0;
const double SANE_GPS_UNCERTAINTY = 1500.0; // m const double SANE_GPS_UNCERTAINTY = 1500.0; // m
const double INPUT_INVALID_THRESHOLD = 5.0; // same as reset tracker
const double DECAY = 0.99995; // same as reset tracker
const double MAX_FILTER_REWIND_TIME = 0.8; // s
// TODO: GPS sensor time offsets are empirically calculated // TODO: GPS sensor time offsets are empirically calculated
// They should be replaced with synced time from a real clock // They should be replaced with synced time from a real clock
@ -200,6 +203,14 @@ VectorXd Localizer::get_stdev() {
return this->kf->get_P().diagonal().array().sqrt(); return this->kf->get_P().diagonal().array().sqrt();
} }
bool Localizer::are_inputs_ok() {
return this->critical_services_valid(this->observation_values_invalid) && !this->observation_timings_invalid;
}
void Localizer::observation_timings_invalid_reset(){
this->observation_timings_invalid = false;
}
void Localizer::handle_sensor(double current_time, const cereal::SensorEventData::Reader& log) { void Localizer::handle_sensor(double current_time, const cereal::SensorEventData::Reader& log) {
// TODO does not yet account for double sensor readings in the log // TODO does not yet account for double sensor readings in the log
@ -213,6 +224,11 @@ void Localizer::handle_sensor(double current_time, const cereal::SensorEventData
// sensor time and log time should be close // sensor time and log time should be close
if (std::abs(current_time - sensor_time) > 0.1) { if (std::abs(current_time - sensor_time) > 0.1) {
LOGE("Sensor reading ignored, sensor timestamp more than 100ms off from log time"); LOGE("Sensor reading ignored, sensor timestamp more than 100ms off from log time");
this->observation_timings_invalid = true;
return;
}
else if (!this->is_timestamp_valid(sensor_time)) {
this->observation_timings_invalid = true;
return; return;
} }
@ -227,6 +243,10 @@ void Localizer::handle_sensor(double current_time, const cereal::SensorEventData
auto meas = Vector3d(-v[2], -v[1], -v[0]); auto meas = Vector3d(-v[2], -v[1], -v[0]);
if (meas.norm() < ROTATION_SANITY_CHECK) { if (meas.norm() < ROTATION_SANITY_CHECK) {
this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_GYRO, { meas }); this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_GYRO, { meas });
this->observation_values_invalid["gyroscope"] *= DECAY;
}
else{
this->observation_values_invalid["gyroscope"] += 1.0;
} }
} }
@ -242,6 +262,10 @@ void Localizer::handle_sensor(double current_time, const cereal::SensorEventData
auto meas = Vector3d(-v[2], -v[1], -v[0]); auto meas = Vector3d(-v[2], -v[1], -v[0]);
if (meas.norm() < ACCEL_SANITY_CHECK) { if (meas.norm() < ACCEL_SANITY_CHECK) {
this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_ACCEL, { meas }); this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_ACCEL, { meas });
this->observation_values_invalid["accelerometer"] *= DECAY;
}
else{
this->observation_values_invalid["accelerometer"] += 1.0;
} }
} }
} }
@ -336,7 +360,13 @@ void Localizer::handle_cam_odo(double current_time, const cereal::CameraOdometry
VectorXd rot_device = this->device_from_calib * floatlist2vector(log.getRot()); VectorXd rot_device = this->device_from_calib * floatlist2vector(log.getRot());
VectorXd trans_device = this->device_from_calib * floatlist2vector(log.getTrans()); VectorXd trans_device = this->device_from_calib * floatlist2vector(log.getTrans());
if (!this->is_timestamp_valid(current_time)) {
this->observation_timings_invalid = true;
return;
}
if ((rot_device.norm() > ROTATION_SANITY_CHECK) || (trans_device.norm() > TRANS_SANITY_CHECK)) { if ((rot_device.norm() > ROTATION_SANITY_CHECK) || (trans_device.norm() > TRANS_SANITY_CHECK)) {
this->observation_values_invalid["cameraOdometry"] += 1.0;
return; return;
} }
@ -344,10 +374,12 @@ void Localizer::handle_cam_odo(double current_time, const cereal::CameraOdometry
VectorXd trans_calib_std = floatlist2vector(log.getTransStd()); VectorXd trans_calib_std = floatlist2vector(log.getTransStd());
if ((rot_calib_std.minCoeff() <= MIN_STD_SANITY_CHECK) || (trans_calib_std.minCoeff() <= MIN_STD_SANITY_CHECK)) { if ((rot_calib_std.minCoeff() <= MIN_STD_SANITY_CHECK) || (trans_calib_std.minCoeff() <= MIN_STD_SANITY_CHECK)) {
this->observation_values_invalid["cameraOdometry"] += 1.0;
return; return;
} }
if ((rot_calib_std.norm() > 10 * ROTATION_SANITY_CHECK) || (trans_calib_std.norm() > 10 * TRANS_SANITY_CHECK)) { if ((rot_calib_std.norm() > 10 * ROTATION_SANITY_CHECK) || (trans_calib_std.norm() > 10 * TRANS_SANITY_CHECK)) {
this->observation_values_invalid["cameraOdometry"] += 1.0;
return; return;
} }
@ -363,12 +395,19 @@ void Localizer::handle_cam_odo(double current_time, const cereal::CameraOdometry
{ rot_device }, { rot_device_cov }); { rot_device }, { rot_device_cov });
this->kf->predict_and_observe(current_time, OBSERVATION_CAMERA_ODO_TRANSLATION, this->kf->predict_and_observe(current_time, OBSERVATION_CAMERA_ODO_TRANSLATION,
{ trans_device }, { trans_device_cov }); { trans_device }, { trans_device_cov });
this->observation_values_invalid["cameraOdometry"] *= DECAY;
} }
void Localizer::handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log) { void Localizer::handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log) {
if (!this->is_timestamp_valid(current_time)) {
this->observation_timings_invalid = true;
return;
}
if (log.getRpyCalib().size() > 0) { if (log.getRpyCalib().size() > 0) {
auto live_calib = floatlist2vector(log.getRpyCalib()); auto live_calib = floatlist2vector(log.getRpyCalib());
if ((live_calib.minCoeff() < -CALIB_RPY_SANITY_CHECK) || (live_calib.maxCoeff() > CALIB_RPY_SANITY_CHECK)) { if ((live_calib.minCoeff() < -CALIB_RPY_SANITY_CHECK) || (live_calib.maxCoeff() > CALIB_RPY_SANITY_CHECK)) {
this->observation_values_invalid["liveCalibration"] += 1.0;
return; return;
} }
@ -376,6 +415,7 @@ void Localizer::handle_live_calib(double current_time, const cereal::LiveCalibra
this->device_from_calib = euler2rot(this->calib); this->device_from_calib = euler2rot(this->calib);
this->calib_from_device = this->device_from_calib.transpose(); this->calib_from_device = this->device_from_calib.transpose();
this->calibrated = log.getCalStatus() == 1; this->calibrated = log.getCalStatus() == 1;
this->observation_values_invalid["liveCalibration"] *= DECAY;
} }
} }
@ -407,8 +447,8 @@ void Localizer::time_check(double current_time) {
void Localizer::update_reset_tracker() { void Localizer::update_reset_tracker() {
// reset tracker is tuned to trigger when over 1reset/10s over 2min period // reset tracker is tuned to trigger when over 1reset/10s over 2min period
if (this->isGpsOK()) { if (this->is_gps_ok()) {
this->reset_tracker *= .99995; this->reset_tracker *= DECAY;
} else { } else {
this->reset_tracker = 0.0; this->reset_tracker = 0.0;
} }
@ -483,10 +523,28 @@ kj::ArrayPtr<capnp::byte> Localizer::get_message_bytes(MessageBuilder& msg_build
return msg_builder.toBytes(); return msg_builder.toBytes();
} }
bool Localizer::isGpsOK() { bool Localizer::is_gps_ok() {
return this->gps_valid; return this->gps_valid;
} }
bool Localizer::critical_services_valid(std::map<std::string, double> critical_services) {
for (auto &kv : critical_services){
if (kv.second >= INPUT_INVALID_THRESHOLD){
return false;
}
}
return true;
}
bool Localizer::is_timestamp_valid(double current_time) {
double filter_time = this->kf->get_filter_time();
if (!std::isnan(filter_time) && ((filter_time - current_time) > MAX_FILTER_REWIND_TIME)) {
LOGE("Observation timestamp is older than the max rewind threshold of the filter");
return false;
}
return true;
}
void Localizer::determine_gps_mode(double current_time) { void Localizer::determine_gps_mode(double current_time) {
// 1. If the pos_std is greater than what's not acceptable and localizer is in gps-mode, reset to no-gps-mode // 1. If the pos_std is greater than what's not acceptable and localizer is in gps-mode, reset to no-gps-mode
// 2. If the pos_std is greater than what's not acceptable and localizer is in no-gps-mode, fake obs // 2. If the pos_std is greater than what's not acceptable and localizer is in no-gps-mode, fake obs
@ -521,10 +579,15 @@ int Localizer::locationd_thread() {
uint64_t cnt = 0; uint64_t cnt = 0;
bool filterInitialized = false; bool filterInitialized = false;
const std::vector<std::string> critical_input_services = {"cameraOdometry", "liveCalibration", "accelerometer", "gyroscope"};
for (std::string service : critical_input_services) {
this->observation_values_invalid.insert({service, 0.0});
}
while (!do_exit) { while (!do_exit) {
sm.update(); sm.update();
if (filterInitialized){ if (filterInitialized){
this->observation_timings_invalid_reset();
for (const char* service : service_list) { for (const char* service : service_list) {
if (sm.updated(service) && sm.valid(service)){ if (sm.updated(service) && sm.valid(service)){
const cereal::Event::Reader log = sm[service]; const cereal::Event::Reader log = sm[service];
@ -538,8 +601,8 @@ int Localizer::locationd_thread() {
// 100Hz publish for notcars, 20Hz for cars // 100Hz publish for notcars, 20Hz for cars
const char* trigger_msg = sm["carParams"].getCarParams().getNotCar() ? "accelerometer" : "cameraOdometry"; const char* trigger_msg = sm["carParams"].getCarParams().getNotCar() ? "accelerometer" : "cameraOdometry";
if (sm.updated(trigger_msg)) { if (sm.updated(trigger_msg)) {
bool inputsOK = sm.allAliveAndValid(); bool inputsOK = sm.allAliveAndValid() && this->are_inputs_ok();
bool gpsOK = this->isGpsOK(); bool gpsOK = this->is_gps_ok();
bool sensorsOK = sm.allAliveAndValid({"accelerometer", "gyroscope"}); bool sensorsOK = sm.allAliveAndValid({"accelerometer", "gyroscope"});
MessageBuilder msg_builder; MessageBuilder msg_builder;

@ -3,6 +3,7 @@
#include <eigen3/Eigen/Dense> #include <eigen3/Eigen/Dense>
#include <fstream> #include <fstream>
#include <memory> #include <memory>
#include <map>
#include <string> #include <string>
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
@ -32,8 +33,12 @@ public:
void finite_check(double current_time = NAN); void finite_check(double current_time = NAN);
void time_check(double current_time = NAN); void time_check(double current_time = NAN);
void update_reset_tracker(); void update_reset_tracker();
bool isGpsOK(); bool is_gps_ok();
bool critical_services_valid(std::map<std::string, double> critical_services);
bool is_timestamp_valid(double current_time);
void determine_gps_mode(double current_time); void determine_gps_mode(double current_time);
bool are_inputs_ok();
void observation_timings_invalid_reset();
kj::ArrayPtr<capnp::byte> get_message_bytes(MessageBuilder& msg_builder, kj::ArrayPtr<capnp::byte> get_message_bytes(MessageBuilder& msg_builder,
bool inputsOK, bool sensorsOK, bool gpsOK, bool msgValid); bool inputsOK, bool sensorsOK, bool gpsOK, bool msgValid);
@ -73,4 +78,6 @@ private:
bool gps_mode = false; bool gps_mode = false;
bool gps_valid = false; bool gps_valid = false;
bool ublox_available = true; bool ublox_available = true;
bool observation_timings_invalid = false;
std::map<std::string, double> observation_values_invalid;
}; };

@ -16,9 +16,9 @@ from selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
HISTORY = 5 # secs HISTORY = 5 # secs
POINTS_PER_BUCKET = 1500 POINTS_PER_BUCKET = 1500
MIN_POINTS_TOTAL = 4000 MIN_POINTS_TOTAL = 4000
MIN_POINTS_TOTAL_QLOG = 800 MIN_POINTS_TOTAL_QLOG = 600
FIT_POINTS_TOTAL = 2000 FIT_POINTS_TOTAL = 2000
FIT_POINTS_TOTAL_QLOG = 800 FIT_POINTS_TOTAL_QLOG = 600
MIN_VEL = 15 # m/s MIN_VEL = 15 # m/s
FRICTION_FACTOR = 1.5 # ~85% of data coverage FRICTION_FACTOR = 1.5 # ~85% of data coverage
FACTOR_SANITY = 0.3 FACTOR_SANITY = 0.3

@ -10,6 +10,7 @@
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"
#include "cereal/services.h" #include "cereal/services.h"

@ -4,6 +4,7 @@
#include <condition_variable> #include <condition_variable>
#include <sstream> #include <sstream>
#include <thread> #include <thread>
#include <utility>
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "cereal/messaging/messaging.h" #include "cereal/messaging/messaging.h"

@ -24,6 +24,7 @@ procs = [
NativeProcess("logcatd", "system/logcatd", ["./logcatd"]), NativeProcess("logcatd", "system/logcatd", ["./logcatd"]),
NativeProcess("proclogd", "system/proclogd", ["./proclogd"]), NativeProcess("proclogd", "system/proclogd", ["./proclogd"]),
PythonProcess("logmessaged", "system.logmessaged", offroad=True), PythonProcess("logmessaged", "system.logmessaged", offroad=True),
PythonProcess("micd", "system.micd"),
PythonProcess("timezoned", "system.timezoned", enabled=not PC, offroad=True), PythonProcess("timezoned", "system.timezoned", enabled=not PC, offroad=True),
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
@ -31,7 +32,8 @@ procs = [
NativeProcess("encoderd", "selfdrive/loggerd", ["./encoderd"]), NativeProcess("encoderd", "selfdrive/loggerd", ["./encoderd"]),
NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging), NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging),
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]), NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]),
# NativeProcess("mapsd", "selfdrive/navd", ["./map_renderer"]), NativeProcess("mapsd", "selfdrive/navd", ["./map_renderer"], enabled=False),
NativeProcess("navmodeld", "selfdrive/modeld", ["./navmodeld"], enabled=False),
NativeProcess("sensord", "selfdrive/sensord", ["./sensord"], enabled=not PC), NativeProcess("sensord", "selfdrive/sensord", ["./sensord"], enabled=not PC),
NativeProcess("ubloxd", "selfdrive/locationd", ["./ubloxd"], enabled=(not PC or WEBCAM)), NativeProcess("ubloxd", "selfdrive/locationd", ["./ubloxd"], enabled=(not PC or WEBCAM)),
NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)), NativeProcess("ui", "selfdrive/ui", ["./ui"], offroad=True, watchdog_max_dt=(5 if not PC else None)),

@ -4,6 +4,7 @@ import signal
import time import time
import unittest import unittest
from common.params import Params
import selfdrive.manager.manager as manager import selfdrive.manager.manager as manager
from selfdrive.manager.process import DaemonProcess from selfdrive.manager.process import DaemonProcess
from selfdrive.manager.process_config import managed_processes from selfdrive.manager.process_config import managed_processes
@ -20,6 +21,10 @@ class TestManager(unittest.TestCase):
os.environ['PASSIVE'] = '0' os.environ['PASSIVE'] = '0'
HARDWARE.set_power_save(False) HARDWARE.set_power_save(False)
# ensure clean CarParams
params = Params()
params.clear_all()
def tearDown(self): def tearDown(self):
manager.manager_cleanup() manager.manager_cleanup()
@ -40,6 +45,7 @@ class TestManager(unittest.TestCase):
Ensure all processes exit cleanly when stopped. Ensure all processes exit cleanly when stopped.
""" """
HARDWARE.set_power_save(False) HARDWARE.set_power_save(False)
manager.manager_init()
manager.manager_prepare() manager.manager_prepare()
for p in ALL_PROCESSES: for p in ALL_PROCESSES:
managed_processes[p].start() managed_processes[p].start()

@ -112,3 +112,8 @@ llenv.Program('_modeld', [
"modeld.cc", "modeld.cc",
"models/driving.cc", "models/driving.cc",
]+common_model, LIBS=libs + transformations) ]+common_model, LIBS=libs + transformations)
lenv.Program('_navmodeld', [
"navmodeld.cc",
"models/nav.cc",
]+common_model, LIBS=libs + transformations)

@ -13,6 +13,7 @@
#endif #endif
#include "common/mat.h" #include "common/mat.h"
#include "cereal/messaging/messaging.h"
#include "selfdrive/modeld/transforms/loadyuv.h" #include "selfdrive/modeld/transforms/loadyuv.h"
#include "selfdrive/modeld/transforms/transform.h" #include "selfdrive/modeld/transforms/transform.h"
@ -21,6 +22,11 @@ const bool send_raw_pred = getenv("SEND_RAW_PRED") != NULL;
void softmax(const float* input, float* output, size_t len); void softmax(const float* input, float* output, size_t len);
float sigmoid(float input); float sigmoid(float input);
template<class T, size_t size>
constexpr const kj::ArrayPtr<const T> to_kj_array_ptr(const std::array<T, size> &arr) {
return kj::ArrayPtr(arr.data(), arr.size());
}
class ModelFrame { class ModelFrame {
public: public:
ModelFrame(cl_device_id device_id, cl_context context); ModelFrame(cl_device_id device_id, cl_context context);

@ -22,11 +22,6 @@ std::array<float, 3> prev_brake_3ms2_probs = {0,0,0};
// #define DUMP_YUV // #define DUMP_YUV
template<class T, size_t size>
constexpr const kj::ArrayPtr<const T> to_kj_array_ptr(const std::array<T, size> &arr) {
return kj::ArrayPtr(arr.data(), arr.size());
}
void model_init(ModelState* s, cl_device_id device_id, cl_context context) { void model_init(ModelState* s, cl_device_id device_id, cl_context context) {
s->frame = new ModelFrame(device_id, context); s->frame = new ModelFrame(device_id, context);
s->wide_frame = new ModelFrame(device_id, context); s->wide_frame = new ModelFrame(device_id, context);

@ -0,0 +1,66 @@
#include "selfdrive/modeld/models/nav.h"
#include <cstdio>
#include <cstring>
#include "common/mat.h"
#include "common/modeldata.h"
#include "common/timing.h"
void navmodel_init(NavModelState* s) {
#ifdef USE_ONNX_MODEL
s->m = new ONNXModel("models/navmodel.onnx", &s->output[0], NAV_NET_OUTPUT_SIZE, USE_DSP_RUNTIME, false, true);
#else
s->m = new SNPEModel("models/navmodel_q.dlc", &s->output[0], NAV_NET_OUTPUT_SIZE, USE_DSP_RUNTIME, false, true);
#endif
}
NavModelResult* navmodel_eval_frame(NavModelState* s, VisionBuf* buf) {
memcpy(s->net_input_buf, buf->addr, NAV_INPUT_SIZE);
double t1 = millis_since_boot();
s->m->addImage((float*)s->net_input_buf, NAV_INPUT_SIZE/sizeof(float));
s->m->execute();
double t2 = millis_since_boot();
NavModelResult *model_res = (NavModelResult*)&s->output;
model_res->dsp_execution_time = (t2 - t1) / 1000.;
return model_res;
}
void fill_plan(cereal::NavModelData::Builder &framed, const NavModelOutputPlan &plan) {
std::array<float, TRAJECTORY_SIZE> pos_x, pos_y;
std::array<float, TRAJECTORY_SIZE> pos_x_std, pos_y_std;
for (int i=0; i<TRAJECTORY_SIZE; i++) {
pos_x[i] = plan.mean[i].x;
pos_y[i] = plan.mean[i].y;
pos_x_std[i] = exp(plan.std[i].x);
pos_y_std[i] = exp(plan.std[i].y);
}
auto position = framed.initPosition();
position.setX(to_kj_array_ptr(pos_x));
position.setY(to_kj_array_ptr(pos_y));
position.setXStd(to_kj_array_ptr(pos_x_std));
position.setYStd(to_kj_array_ptr(pos_y_std));
}
void navmodel_publish(PubMaster &pm, uint32_t frame_id, const NavModelResult &model_res, float execution_time) {
// make msg
MessageBuilder msg;
auto framed = msg.initEvent().initNavModel();
framed.setFrameId(frame_id);
framed.setModelExecutionTime(execution_time);
framed.setDspExecutionTime(model_res.dsp_execution_time);
framed.setFeatures(to_kj_array_ptr(model_res.features.values));
framed.setDesirePrediction(to_kj_array_ptr(model_res.desire_pred.values));
fill_plan(framed, model_res.plans.get_best_prediction());
pm.send("navModel", msg);
}
void navmodel_free(NavModelState* s) {
delete s->m;
}

@ -0,0 +1,73 @@
#pragma once
#include "cereal/messaging/messaging.h"
#include "cereal/visionipc/visionipc_client.h"
#include "common/util.h"
#include "common/modeldata.h"
#include "selfdrive/modeld/models/commonmodel.h"
#include "selfdrive/modeld/runners/run.h"
constexpr int NAV_INPUT_SIZE = 256*256;
constexpr int NAV_FEATURE_LEN = 64;
constexpr int NAV_DESIRE_LEN = 32;
constexpr int NAV_PLAN_MHP_N = 5;
struct NavModelOutputXY {
float x;
float y;
};
static_assert(sizeof(NavModelOutputXY) == sizeof(float)*2);
struct NavModelOutputPlan {
std::array<NavModelOutputXY, TRAJECTORY_SIZE> mean;
std::array<NavModelOutputXY, TRAJECTORY_SIZE> std;
float prob;
};
static_assert(sizeof(NavModelOutputPlan) == sizeof(NavModelOutputXY)*TRAJECTORY_SIZE*2 + sizeof(float));
struct NavModelOutputPlans {
std::array<NavModelOutputPlan, NAV_PLAN_MHP_N> predictions;
constexpr const NavModelOutputPlan &get_best_prediction() const {
int max_idx = 0;
for (int i = 1; i < predictions.size(); i++) {
if (predictions[i].prob > predictions[max_idx].prob) {
max_idx = i;
}
}
return predictions[max_idx];
}
};
static_assert(sizeof(NavModelOutputPlans) == sizeof(NavModelOutputPlan)*NAV_PLAN_MHP_N);
struct NavModelOutputDesirePrediction {
std::array<float, NAV_DESIRE_LEN> values;
};
static_assert(sizeof(NavModelOutputDesirePrediction) == sizeof(float)*NAV_DESIRE_LEN);
struct NavModelOutputFeatures {
std::array<float, NAV_FEATURE_LEN> values;
};
static_assert(sizeof(NavModelOutputFeatures) == sizeof(float)*NAV_FEATURE_LEN);
struct NavModelResult {
const NavModelOutputPlans plans;
const NavModelOutputDesirePrediction desire_pred;
const NavModelOutputFeatures features;
float dsp_execution_time;
};
static_assert(sizeof(NavModelResult) == sizeof(NavModelOutputPlans) + sizeof(NavModelOutputDesirePrediction) + sizeof(NavModelOutputFeatures) + sizeof(float));
constexpr int NAV_OUTPUT_SIZE = sizeof(NavModelResult) / sizeof(float);
constexpr int NAV_NET_OUTPUT_SIZE = NAV_OUTPUT_SIZE - 1;
struct NavModelState {
RunModel *m;
uint8_t net_input_buf[NAV_INPUT_SIZE];
float output[NAV_OUTPUT_SIZE];
};
void navmodel_init(NavModelState* s);
NavModelResult* navmodel_eval_frame(NavModelState* s, VisionBuf* buf);
void navmodel_publish(PubMaster &pm, uint32_t frame_id, const NavModelResult &model_res, float execution_time);
void navmodel_free(NavModelState* s);

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eab4b986e14d7d842d6d5487011c329d356fb56995b2ae7dc7188aefe6df9d97
size 12285002

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:83d53efc40053b02fe7d3da4ef6213a4a5a1ae4d1bd49c121b9beb6a54ea1148
size 3154868

@ -0,0 +1,12 @@
#!/bin/sh
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd $DIR
if [ -f /TICI ]; then
export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/data/pythonpath/third_party/snpe/larch64:$LD_LIBRARY_PATH"
export ADSP_LIBRARY_PATH="/data/pythonpath/third_party/snpe/dsp/"
else
export LD_LIBRARY_PATH="$DIR/../../third_party/snpe/x86_64-linux-clang:$DIR/../../openpilot/third_party/snpe/x86_64:$LD_LIBRARY_PATH"
fi
exec ./_navmodeld

@ -0,0 +1,59 @@
#include <sys/resource.h>
#include <limits.h>
#include <cstdio>
#include <cstdlib>
#include "cereal/visionipc/visionipc_client.h"
#include "common/swaglog.h"
#include "common/util.h"
#include "selfdrive/modeld/models/nav.h"
ExitHandler do_exit;
void run_model(NavModelState &model, VisionIpcClient &vipc_client) {
PubMaster pm({"navModel"});
double last_ts = 0;
uint32_t last_frame_id = 0;
VisionIpcBufExtra extra = {};
while (!do_exit) {
VisionBuf *buf = vipc_client.recv(&extra);
if (buf == nullptr) continue;
double t1 = millis_since_boot();
NavModelResult *model_res = navmodel_eval_frame(&model, buf);
double t2 = millis_since_boot();
// send navmodel packet
navmodel_publish(pm, extra.frame_id, *model_res, (t2 - t1) / 1000.0);
//printf("navmodel process: %.2fms, from last %.2fms\n", t2 - t1, t1 - last_ts);
last_ts = t1;
last_frame_id = extra.frame_id;
}
}
int main(int argc, char **argv) {
setpriority(PRIO_PROCESS, 0, -15);
// init the models
NavModelState model;
navmodel_init(&model);
LOGW("models loaded, navmodeld starting");
VisionIpcClient vipc_client = VisionIpcClient("navd", VISION_STREAM_MAP, true);
while (!do_exit && !vipc_client.connect(false)) {
util::sleep_for(100);
}
// run the models
if (vipc_client.connected) {
LOGW("connected with buffer size: %d", vipc_client.buffers[0].len);
run_model(model, vipc_client);
}
navmodel_free(&model);
return 0;
}

@ -156,6 +156,11 @@ class DriverStatus():
self._set_timers(active_monitoring=True) self._set_timers(active_monitoring=True)
def _reset_awareness(self):
self.awareness = 1.
self.awareness_active = 1.
self.awareness_passive = 1.
def _set_timers(self, active_monitoring): def _set_timers(self, active_monitoring):
if self.active_monitoring_mode and self.awareness <= self.threshold_prompt: if self.active_monitoring_mode and self.awareness <= self.threshold_prompt:
if active_monitoring: if active_monitoring:
@ -289,17 +294,17 @@ class DriverStatus():
self.hi_stds = 0 self.hi_stds = 0
def update_events(self, events, driver_engaged, ctrl_active, standstill): def update_events(self, events, driver_engaged, ctrl_active, standstill):
if (driver_engaged and self.awareness > 0) or not ctrl_active: if (driver_engaged and self.awareness > 0 and not self.active_monitoring_mode) or not ctrl_active: # reset only when on disengagement if red reached
# reset only when on disengagement if red reached self._reset_awareness()
self.awareness = 1.
self.awareness_active = 1.
self.awareness_passive = 1.
return return
driver_attentive = self.driver_distraction_filter.x < 0.37 driver_attentive = self.driver_distraction_filter.x < 0.37
awareness_prev = self.awareness awareness_prev = self.awareness
if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0): if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0):
if driver_engaged:
self._reset_awareness()
return
# only restore awareness when paying attention and alert is not red # only restore awareness when paying attention and alert is not red
self.awareness = min(self.awareness + ((self.settings._RECOVERY_FACTOR_MAX-self.settings._RECOVERY_FACTOR_MIN)*(1.-self.awareness)+self.settings._RECOVERY_FACTOR_MIN)*self.step_change, 1.) self.awareness = min(self.awareness + ((self.settings._RECOVERY_FACTOR_MAX-self.settings._RECOVERY_FACTOR_MIN)*(1.-self.awareness)+self.settings._RECOVERY_FACTOR_MIN)*self.step_change, 1.)
if self.awareness == 1.: if self.awareness == 1.:

@ -101,11 +101,12 @@ class TestMonitoring(unittest.TestCase):
((TEST_TIMESPAN-10-d_status.settings._AWARENESS_TIME)/2))/DT_DMON)].names[0], EventName.driverUnresponsive) ((TEST_TIMESPAN-10-d_status.settings._AWARENESS_TIME)/2))/DT_DMON)].names[0], EventName.driverUnresponsive)
# engaged, down to orange, driver pays attention, back to normal; then down to orange, driver touches wheel # engaged, down to orange, driver pays attention, back to normal; then down to orange, driver touches wheel
# - should have short orange recovery time and no green afterwards; should recover rightaway on wheel touch # - should have short orange recovery time and no green afterwards; wheel touch only recovers when paying attention
def test_normal_driver(self): def test_normal_driver(self):
ds_vector = [msg_DISTRACTED] * int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON) + \ ds_vector = [msg_DISTRACTED] * int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON) + \
[msg_ATTENTIVE] * int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON) + \ [msg_ATTENTIVE] * int(DISTRACTED_SECONDS_TO_ORANGE/DT_DMON) + \
[msg_DISTRACTED] * (int(TEST_TIMESPAN/DT_DMON)-int(DISTRACTED_SECONDS_TO_ORANGE*2/DT_DMON)) [msg_DISTRACTED] * int((DISTRACTED_SECONDS_TO_ORANGE+2)/DT_DMON) + \
[msg_ATTENTIVE] * (int(TEST_TIMESPAN/DT_DMON)-int((DISTRACTED_SECONDS_TO_ORANGE*3+2)/DT_DMON))
interaction_vector = [car_interaction_NOT_DETECTED] * int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON) + \ interaction_vector = [car_interaction_NOT_DETECTED] * int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON) + \
[car_interaction_DETECTED] * (int(TEST_TIMESPAN/DT_DMON)-int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON)) [car_interaction_DETECTED] * (int(TEST_TIMESPAN/DT_DMON)-int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON))
events, _ = self._run_seq(ds_vector, interaction_vector, always_true, always_false) events, _ = self._run_seq(ds_vector, interaction_vector, always_true, always_false)
@ -113,7 +114,8 @@ class TestMonitoring(unittest.TestCase):
self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted) self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted)
self.assertEqual(len(events[int(DISTRACTED_SECONDS_TO_ORANGE*1.5/DT_DMON)]), 0) self.assertEqual(len(events[int(DISTRACTED_SECONDS_TO_ORANGE*1.5/DT_DMON)]), 0)
self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted) self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted)
self.assertEqual(len(events[int((DISTRACTED_SECONDS_TO_ORANGE*3+0.1)/DT_DMON)]), 0) self.assertEqual(events[int((DISTRACTED_SECONDS_TO_ORANGE*3+0.1)/DT_DMON)].names[0], EventName.promptDriverDistracted)
self.assertEqual(len(events[int((DISTRACTED_SECONDS_TO_ORANGE*3+2.5)/DT_DMON)]), 0)
# engaged, down to orange, driver dodges camera, then comes back still distracted, down to red, \ # engaged, down to orange, driver dodges camera, then comes back still distracted, down to red, \
# driver dodges, and then touches wheel to no avail, disengages and reengages # driver dodges, and then touches wheel to no avail, disengages and reengages

@ -11,33 +11,18 @@
#include "selfdrive/ui/qt/maps/map_helpers.h" #include "selfdrive/ui/qt/maps/map_helpers.h"
const float DEFAULT_ZOOM = 13.5; // Don't go below 13 or features will start to disappear const float DEFAULT_ZOOM = 13.5; // Don't go below 13 or features will start to disappear
const int RENDER_HEIGHT = 512, RENDER_WIDTH = 512; const int HEIGHT = 512, WIDTH = 512;
const int HEIGHT = 256, WIDTH = 256;
const int NUM_VIPC_BUFFERS = 4; const int NUM_VIPC_BUFFERS = 4;
const int EARTH_CIRCUMFERENCE_METERS = 40075000; const int EARTH_CIRCUMFERENCE_METERS = 40075000;
const int PIXELS_PER_TILE = 256; const int PIXELS_PER_TILE = 256;
float get_meters_per_pixel(float lat, float zoom) {
float num_tiles = pow(2, zoom+1);
float meters_per_tile = cos(DEG2RAD(lat)) * EARTH_CIRCUMFERENCE_METERS / num_tiles;
return meters_per_tile / PIXELS_PER_TILE;
}
float get_zoom_level_for_scale(float lat, float meters_per_pixel) { float get_zoom_level_for_scale(float lat, float meters_per_pixel) {
float meters_per_tile = meters_per_pixel * PIXELS_PER_TILE; float meters_per_tile = meters_per_pixel * PIXELS_PER_TILE;
float num_tiles = cos(DEG2RAD(lat)) * EARTH_CIRCUMFERENCE_METERS / meters_per_tile; float num_tiles = cos(DEG2RAD(lat)) * EARTH_CIRCUMFERENCE_METERS / meters_per_tile;
return log2(num_tiles) - 1; return log2(num_tiles) - 1;
} }
void downsample(uint8_t *src, uint8_t *dst) {
for (int r = 0; r < HEIGHT; r++) {
for (int c = 0; c < WIDTH; c++) {
dst[r*WIDTH + c] = src[(r*2*RENDER_WIDTH + c*2) * 3];
}
}
}
MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool online) : m_settings(settings) { MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool online) : m_settings(settings) {
QSurfaceFormat fmt; QSurfaceFormat fmt;
@ -59,7 +44,7 @@ MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool online) : m_set
gl_functions->initializeOpenGLFunctions(); gl_functions->initializeOpenGLFunctions();
QOpenGLFramebufferObjectFormat fbo_format; QOpenGLFramebufferObjectFormat fbo_format;
fbo.reset(new QOpenGLFramebufferObject(RENDER_WIDTH, RENDER_HEIGHT, fbo_format)); fbo.reset(new QOpenGLFramebufferObject(WIDTH, HEIGHT, fbo_format));
std::string style = util::read_file(STYLE_PATH); std::string style = util::read_file(STYLE_PATH);
m_map.reset(new QMapboxGL(nullptr, m_settings, fbo->size(), 1)); m_map.reset(new QMapboxGL(nullptr, m_settings, fbo->size(), 1));
@ -69,7 +54,7 @@ MapRenderer::MapRenderer(const QMapboxGLSettings &settings, bool online) : m_set
m_map->resize(fbo->size()); m_map->resize(fbo->size());
m_map->setFramebufferObject(fbo->handle(), fbo->size()); m_map->setFramebufferObject(fbo->handle(), fbo->size());
gl_functions->glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT); gl_functions->glViewport(0, 0, WIDTH, HEIGHT);
if (online) { if (online) {
vipc_server.reset(new VisionIpcServer("navd")); vipc_server.reset(new VisionIpcServer("navd"));
@ -94,7 +79,7 @@ void MapRenderer::msgUpdate() {
auto orientation = location.getCalibratedOrientationNED(); auto orientation = location.getCalibratedOrientationNED();
bool localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && pos.getValid(); bool localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && pos.getValid();
if (localizer_valid) { if (localizer_valid && (sm->rcv_frame("liveLocationKalman") % 10) == 0) {
updatePosition(QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]), RAD2DEG(orientation.getValue()[2])); updatePosition(QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]), RAD2DEG(orientation.getValue()[2]));
} }
} }
@ -114,9 +99,9 @@ void MapRenderer::updatePosition(QMapbox::Coordinate position, float bearing) {
return; return;
} }
// Choose a zoom level that matches the scale of zoom level 13 at latitude 80deg // Choose a scale that ensures above 13 zoom level up to and above 75deg of lat
float scale_lat80 = get_meters_per_pixel(80, 13); float meters_per_pixel = 2;
float zoom = get_zoom_level_for_scale(position.first, scale_lat80); float zoom = get_zoom_level_for_scale(position.first, meters_per_pixel);
m_map->setCoordinate(position); m_map->setCoordinate(position);
m_map->setBearing(bearing); m_map->setBearing(bearing);
@ -150,13 +135,15 @@ void MapRenderer::sendVipc() {
.timestamp_eof = ts, .timestamp_eof = ts,
}; };
assert(cap.sizeInBytes() >= buf->len*4); assert(cap.sizeInBytes() >= buf->len);
uint8_t* dst = (uint8_t*)buf->addr; uint8_t* dst = (uint8_t*)buf->addr;
uint8_t* src = cap.bits(); uint8_t* src = cap.bits();
// 2x downsample + rgb to grayscale // RGB to greyscale
memset(dst, 128, buf->len); memset(dst, 128, buf->len);
downsample(src, dst); for (int i = 0; i < WIDTH * HEIGHT; i++) {
dst[i] = src[i * 3];
}
vipc_server->send(buf, &extra); vipc_server->send(buf, &extra);
@ -187,8 +174,10 @@ uint8_t* MapRenderer::getImage() {
uint8_t* src = cap.bits(); uint8_t* src = cap.bits();
uint8_t* dst = new uint8_t[WIDTH * HEIGHT]; uint8_t* dst = new uint8_t[WIDTH * HEIGHT];
// 2x downsample + rgb to grayscale // RGB to greyscale
downsample(src, dst); for (int i = 0; i < WIDTH * HEIGHT; i++) {
dst[i] = src[i * 3];
}
return dst; return dst;
} }

@ -9,7 +9,8 @@ from cffi import FFI
from common.ffi_wrapper import suffix from common.ffi_wrapper import suffix
from common.basedir import BASEDIR from common.basedir import BASEDIR
HEIGHT = WIDTH = 256 HEIGHT = WIDTH = SIZE = 512
METERS_PER_PIXEL = 2
def get_ffi(): def get_ffi():

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

Loading…
Cancel
Save