Merge branch 'master' into mqb-long

# Conflicts:
#	opendbc
#	panda
pull/23257/head
Jason Young 4 years ago
commit cd10722753
  1. 1
      .github/workflows/selfdrive_tests.yaml
  2. 93
      Jenkinsfile
  3. 1
      Pipfile
  4. 312
      Pipfile.lock
  5. 20
      README.md
  6. 7
      RELEASES.md
  7. 5
      SConstruct
  8. 2
      cereal
  9. 8
      common/api/__init__.py
  10. 23
      common/cython_hacks.py
  11. 9
      docs/CARS.md
  12. 4
      models/supercombo.dlc
  13. 4
      models/supercombo.onnx
  14. 2
      panda
  15. 5
      release/files_common
  16. 1
      release/files_tici
  17. 16
      release/verify.sh
  18. 11
      scripts/apply-pr.sh
  19. 2
      selfdrive/assets/offroad/fcc.html
  20. BIN
      selfdrive/assets/sounds/disengage.wav
  21. BIN
      selfdrive/assets/sounds/disengaged.wav
  22. BIN
      selfdrive/assets/sounds/engage.wav
  23. BIN
      selfdrive/assets/sounds/engaged.wav
  24. BIN
      selfdrive/assets/sounds/error.wav
  25. BIN
      selfdrive/assets/sounds/prompt.wav
  26. BIN
      selfdrive/assets/sounds/prompt_distracted.wav
  27. BIN
      selfdrive/assets/sounds/refuse.wav
  28. BIN
      selfdrive/assets/sounds/warning_1.wav
  29. BIN
      selfdrive/assets/sounds/warning_2.wav
  30. BIN
      selfdrive/assets/sounds/warning_immediate.wav
  31. BIN
      selfdrive/assets/sounds/warning_repeat.wav
  32. BIN
      selfdrive/assets/sounds/warning_soft.wav
  33. BIN
      selfdrive/assets/sounds_tici/disengaged.wav
  34. BIN
      selfdrive/assets/sounds_tici/engaged.wav
  35. BIN
      selfdrive/assets/sounds_tici/error.wav
  36. BIN
      selfdrive/assets/sounds_tici/warning_1.wav
  37. BIN
      selfdrive/assets/sounds_tici/warning_2.wav
  38. BIN
      selfdrive/assets/sounds_tici/warning_repeat.wav
  39. 10
      selfdrive/athena/athenad.py
  40. 4
      selfdrive/athena/manage_athenad.py
  41. 1
      selfdrive/boardd/.gitignore
  42. 5
      selfdrive/boardd/SConscript
  43. 94
      selfdrive/boardd/boardd.cc
  44. 92
      selfdrive/boardd/panda.cc
  45. 11
      selfdrive/boardd/panda.h
  46. 130
      selfdrive/boardd/tests/test_boardd_usbprotocol.cc
  47. 17
      selfdrive/camerad/SConscript
  48. 4
      selfdrive/camerad/cameras/camera_common.cc
  49. 5
      selfdrive/camerad/cameras/camera_common.h
  50. 92
      selfdrive/camerad/cameras/camera_frame_stream.cc
  51. 30
      selfdrive/camerad/cameras/camera_frame_stream.h
  52. 4
      selfdrive/camerad/cameras/camera_qcom.cc
  53. 6
      selfdrive/camerad/cameras/camera_qcom2.cc
  54. 6
      selfdrive/camerad/cameras/camera_replay.cc
  55. 4
      selfdrive/camerad/cameras/camera_webcam.cc
  56. 6
      selfdrive/camerad/test/get_thumbnails_for_segment.py
  57. 4
      selfdrive/car/car_helpers.py
  58. 11
      selfdrive/car/chrysler/carstate.py
  59. 12
      selfdrive/car/ford/carstate.py
  60. 11
      selfdrive/car/gm/carstate.py
  61. 23
      selfdrive/car/honda/carstate.py
  62. 6
      selfdrive/car/honda/interface.py
  63. 93
      selfdrive/car/honda/values.py
  64. 10
      selfdrive/car/hyundai/carstate.py
  65. 11
      selfdrive/car/interfaces.py
  66. 10
      selfdrive/car/mazda/carstate.py
  67. 2
      selfdrive/car/mazda/values.py
  68. 11
      selfdrive/car/nissan/carstate.py
  69. 10
      selfdrive/car/subaru/carstate.py
  70. 57
      selfdrive/car/toyota/carcontroller.py
  71. 16
      selfdrive/car/toyota/carstate.py
  72. 17
      selfdrive/car/toyota/interface.py
  73. 10
      selfdrive/car/toyota/tunes.py
  74. 38
      selfdrive/car/toyota/values.py
  75. 10
      selfdrive/car/volkswagen/carstate.py
  76. 3
      selfdrive/car/volkswagen/values.py
  77. 2
      selfdrive/common/params.cc
  78. 2
      selfdrive/common/version.h
  79. 5
      selfdrive/controls/controlsd.py
  80. 21
      selfdrive/controls/lib/alertmanager.py
  81. 65
      selfdrive/controls/lib/events.py
  82. 35
      selfdrive/controls/lib/lateral_planner.py
  83. 17
      selfdrive/controls/lib/longcontrol.py
  84. 31
      selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py
  85. 22
      selfdrive/controls/lib/longitudinal_planner.py
  86. 4
      selfdrive/controls/plannerd.py
  87. 2
      selfdrive/controls/tests/test_following_distance.py
  88. 4
      selfdrive/crash.py
  89. 2
      selfdrive/debug/cpu_usage_stat.py
  90. 93
      selfdrive/debug/cycle_alerts.py
  91. 2
      selfdrive/hardware/base.py
  92. 2
      selfdrive/hardware/eon/hardware.py
  93. 2
      selfdrive/hardware/pc/hardware.py
  94. 15
      selfdrive/hardware/tici/amplifier.py
  95. 4
      selfdrive/hardware/tici/hardware.h
  96. 2
      selfdrive/hardware/tici/hardware.py
  97. 100
      selfdrive/locationd/locationd.cc
  98. 7
      selfdrive/locationd/locationd.h
  99. 23
      selfdrive/locationd/models/live_kf.cc
  100. 7
      selfdrive/locationd/models/live_kf.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -247,6 +247,7 @@ jobs:
$UNIT_TEST selfdrive/athena && \ $UNIT_TEST selfdrive/athena && \
$UNIT_TEST selfdrive/thermald && \ $UNIT_TEST selfdrive/thermald && \
$UNIT_TEST tools/lib/tests && \ $UNIT_TEST tools/lib/tests && \
./selfdrive/boardd/tests/test_boardd_usbprotocol && \
./selfdrive/common/tests/test_util && \ ./selfdrive/common/tests/test_util && \
./selfdrive/loggerd/tests/test_logger &&\ ./selfdrive/loggerd/tests/test_logger &&\
./selfdrive/proclogd/tests/test_proclog && \ ./selfdrive/proclogd/tests/test_proclog && \

93
Jenkinsfile vendored

@ -1,7 +1,7 @@
def phone(String ip, String step_label, String cmd) { def phone(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """ def ssh_cmd = """
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} -p 8022 'comma@${ip}' /usr/bin/bash <<'EOF' ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'EOF'
set -e set -e
@ -57,38 +57,29 @@ pipeline {
} }
stages { stages {
stage('build releases') {
stage('Build release2') {
agent {
docker {
image 'python:3.7.3'
args '--user=root'
}
}
when { when {
branch 'devel-staging' branch 'devel-staging'
} }
steps {
phone_steps("eon-build", [
["build release2-staging & dashcam-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
])
}
}
stage('Build release3') { parallel {
agent { stage('release2') {
docker { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
image 'python:3.7.3' steps {
args '--user=root' phone_steps("eon-build", [
["build release2-staging & dashcam-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
])
}
}
stage('release3') {
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
steps {
phone_steps("tici", [
["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
])
}
} }
}
when {
branch 'devel-staging'
}
steps {
phone_steps("tici", [
["build release3-staging & dashcam3-staging", "PUSH=1 $SOURCE_DIR/release/build_release.sh"],
])
} }
} }
@ -105,43 +96,8 @@ pipeline {
} }
stages { stages {
/*
stage('PC tests') {
agent {
dockerfile {
filename 'Dockerfile.openpilotci'
args '--privileged --shm-size=1G --user=root'
}
}
stages {
stage('Build') {
steps {
sh 'scons -j$(nproc)'
}
}
}
post {
always {
// fix permissions since docker runs as another user
sh "chmod -R 777 ."
}
}
}
*/
stage('On-device Tests') { stage('On-device Tests') {
agent { agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
docker {
/*
filename 'Dockerfile.ondevice_ci'
args "--privileged -v /dev:/dev --shm-size=1G --user=root"
*/
image 'python:3.7.3'
args '--user=root'
}
}
stages { stages {
stage('parallel tests') { stage('parallel tests') {
parallel { parallel {
@ -247,6 +203,15 @@ pipeline {
} }
} }
stage('C3: replay') {
steps {
phone_steps("tici-party", [
["build", "cd selfdrive/manager && ./build.py"],
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
])
}
}
} }
} }

@ -57,6 +57,7 @@ pycapnp = "==1.1.0"
pycryptodome = "*" pycryptodome = "*"
PyJWT = "*" PyJWT = "*"
pylint = "*" pylint = "*"
pyopencl = "*"
pyserial = "*" pyserial = "*"
python-dateutil = "*" python-dateutil = "*"
PyYAML = "*" PyYAML = "*"

312
Pipfile.lock generated

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "c0f1a46ca2ccdc208a392c5ce78d9d928f82d40afd46a33da7b99b895798fce2" "sha256": "6b23dd7136502655f1c18c37d55db33d77f1b85e0572aed69869401df936003a"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -16,13 +16,20 @@
] ]
}, },
"default": { "default": {
"appdirs": {
"hashes": [
"sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
"sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
"version": "==1.4.4"
},
"astroid": { "astroid": {
"hashes": [ "hashes": [
"sha256:11f7356737b624c42e21e71fe85eea6875cb94c03c82ac76bd535a0ff10b0f25", "sha256:5939cf55de24b92bda00345d4d0659d01b3c7dafb5055165c330bc7c568ba273",
"sha256:abc423a1e85bc1553954a14f2053473d2b7f8baf32eae62a328be24f436b5107" "sha256:776ca0b748b4ad69c00bfe0fff38fa2d21c338e12c84aa9715ee0d473c422778"
], ],
"markers": "python_version ~= '3.6'", "markers": "python_version ~= '3.6'",
"version": "==2.8.5" "version": "==2.9.0"
}, },
"atomicwrites": { "atomicwrites": {
"hashes": [ "hashes": [
@ -141,11 +148,11 @@
}, },
"charset-normalizer": { "charset-normalizer": {
"hashes": [ "hashes": [
"sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", "sha256:735e240d9a8506778cd7a453d97e817e536bb1fc29f4f6961ce297b9c7a917b0",
"sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" "sha256:83fcdeb225499d6344c8f7f34684c2981270beacc32ede2e669e94f7fa544405"
], ],
"markers": "python_version >= '3'", "markers": "python_version >= '3'",
"version": "==2.0.7" "version": "==2.0.8"
}, },
"click": { "click": {
"hashes": [ "hashes": [
@ -167,29 +174,30 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6", "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681",
"sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6", "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed",
"sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c", "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4",
"sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999", "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568",
"sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e", "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e",
"sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992", "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f",
"sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d", "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f",
"sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588", "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712",
"sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa", "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e",
"sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d", "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58",
"sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd", "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44",
"sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d", "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6",
"sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953", "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d",
"sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2", "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636",
"sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8", "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba",
"sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6", "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120",
"sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9", "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3",
"sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6", "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d",
"sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad", "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b",
"sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76" "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81",
], "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"
"index": "pypi", ],
"version": "==35.0.0" "index": "pypi",
"version": "==36.0.0"
}, },
"cython": { "cython": {
"hashes": [ "hashes": [
@ -759,11 +767,23 @@
}, },
"pylint": { "pylint": {
"hashes": [ "hashes": [
"sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126", "sha256:4f4a52b132c05b49094b28e109febcec6bfb7bc6961c7485a5ad0a0f961df289",
"sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436" "sha256:b4b5a7b6d04e914a11c198c816042af1fb2d3cda29bb0c98a9c637010da2a5c5"
],
"index": "pypi",
"version": "==2.12.1"
},
"pyopencl": {
"hashes": [
"sha256:05ccbdc341f64f448bfdff173d1b1e79887129cb6c147605628bbd2e56bc3929",
"sha256:0e705b47733d1055c4d8f7478907222e5881519a0dbadd1bf288baebfc024999",
"sha256:51425e65ec49c738eefe21b1eeb1f39245b01cc0ddfd495fbe1f8df33dbc6c9e",
"sha256:59f9824426d823b544717cc25dee221e1a5c5143143efb8d94034cf4ef562913",
"sha256:662899ca2fb74f2d14c2d7ac0560d3cac07e6b0847a245694bb86a27a4d350ca",
"sha256:99e26fde9e5c418878a5a4a685b8ebca118f423bb09f33e7e24cfd8ef94aee30"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.11.1" "version": "==2021.2.9"
}, },
"pyserial": { "pyserial": {
"hashes": [ "hashes": [
@ -781,6 +801,13 @@
"index": "pypi", "index": "pypi",
"version": "==2.8.2" "version": "==2.8.2"
}, },
"pytools": {
"hashes": [
"sha256:db6cf83c9ba0a165d545029e2301621486d1e9ef295684072e5cd75316a13755"
],
"markers": "python_version ~= '3.6'",
"version": "==2021.2.9"
},
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
@ -883,19 +910,19 @@
}, },
"scons": { "scons": {
"hashes": [ "hashes": [
"sha256:663f819e744ddadcdf4f46b03289a7210313b86041efe1b9c8dde81dba437b72", "sha256:8c13911a2aa40552553488f7d625af4c0768fc8cdedab4a858d8ce42c8c3664d",
"sha256:691893b63f38ad14295f5104661d55cb738ec6514421c6261323351c25432b0a" "sha256:d47081587e3675cc168f1f54f0d74a69b328a2fc90ec4feb85f728677419b879"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.2.0" "version": "==4.3.0"
}, },
"sentry-sdk": { "sentry-sdk": {
"hashes": [ "hashes": [
"sha256:b9844751e40710e84a457c5bc29b21c383ccb2b63d76eeaad72f7f1c808c8828", "sha256:0db297ab32e095705c20f742c3a5dac62fe15c4318681884053d0898e5abb2f6",
"sha256:c091cc7115ff25fe3a0e410dbecd7a996f81a3f6137d2272daef32d6c3cfa6dc" "sha256:789a11a87ca02491896e121efdd64e8fd93327b69e8f2f7d42f03e2569648e88"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.4.3" "version": "==1.5.0"
}, },
"setproctitle": { "setproctitle": {
"hashes": [ "hashes": [
@ -1187,11 +1214,11 @@
}, },
"charset-normalizer": { "charset-normalizer": {
"hashes": [ "hashes": [
"sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0", "sha256:735e240d9a8506778cd7a453d97e817e536bb1fc29f4f6961ce297b9c7a917b0",
"sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b" "sha256:83fcdeb225499d6344c8f7f34684c2981270beacc32ede2e669e94f7fa544405"
], ],
"markers": "python_version >= '3'", "markers": "python_version >= '3'",
"version": "==2.0.7" "version": "==2.0.8"
}, },
"control": { "control": {
"hashes": [ "hashes": [
@ -1255,29 +1282,30 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:07bb7fbfb5de0980590ddfc7f13081520def06dc9ed214000ad4372fb4e3c7f6", "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681",
"sha256:18d90f4711bf63e2fb21e8c8e51ed8189438e6b35a6d996201ebd98a26abbbe6", "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed",
"sha256:1ed82abf16df40a60942a8c211251ae72858b25b7421ce2497c2eb7a1cee817c", "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4",
"sha256:22a38e96118a4ce3b97509443feace1d1011d0571fae81fc3ad35f25ba3ea999", "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568",
"sha256:2d69645f535f4b2c722cfb07a8eab916265545b3475fdb34e0be2f4ee8b0b15e", "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e",
"sha256:4a2d0e0acc20ede0f06ef7aa58546eee96d2592c00f450c9acb89c5879b61992", "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f",
"sha256:54b2605e5475944e2213258e0ab8696f4f357a31371e538ef21e8d61c843c28d", "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f",
"sha256:7075b304cd567694dc692ffc9747f3e9cb393cc4aa4fb7b9f3abd6f5c4e43588", "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712",
"sha256:7b7ceeff114c31f285528ba8b390d3e9cfa2da17b56f11d366769a807f17cbaa", "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e",
"sha256:7eba2cebca600a7806b893cb1d541a6e910afa87e97acf2021a22b32da1df52d", "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58",
"sha256:928185a6d1ccdb816e883f56ebe92e975a262d31cc536429041921f8cb5a62fd", "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44",
"sha256:9933f28f70d0517686bd7de36166dda42094eac49415459d9bdf5e7df3e0086d", "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6",
"sha256:a688ebcd08250eab5bb5bca318cc05a8c66de5e4171a65ca51db6bd753ff8953", "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d",
"sha256:abb5a361d2585bb95012a19ed9b2c8f412c5d723a9836418fab7aaa0243e67d2", "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636",
"sha256:c10c797ac89c746e488d2ee92bd4abd593615694ee17b2500578b63cad6b93a8", "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba",
"sha256:ced40344e811d6abba00295ced98c01aecf0c2de39481792d87af4fa58b7b4d6", "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120",
"sha256:d57e0cdc1b44b6cdf8af1d01807db06886f10177469312fbde8f44ccbb284bc9", "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3",
"sha256:d99915d6ab265c22873f1b4d6ea5ef462ef797b4140be4c9d8b179915e0985c6", "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d",
"sha256:eb80e8a1f91e4b7ef8b33041591e6d89b2b8e122d787e87eeb2b08da71bb16ad", "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b",
"sha256:ebeddd119f526bcf323a89f853afb12e225902a24d29b55fe18dd6fcb2838a76" "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81",
], "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"
"index": "pypi", ],
"version": "==35.0.0" "index": "pypi",
"version": "==36.0.0"
}, },
"cycler": { "cycler": {
"hashes": [ "hashes": [
@ -1343,11 +1371,11 @@
}, },
"fonttools": { "fonttools": {
"hashes": [ "hashes": [
"sha256:68071406009e7ef6a5fdcd85d95975cd6963867bb226f2b786bfffe15d1959ef", "sha256:dca694331af74c8ad47acc5171e57f6b78fac5692bf050f2ab572964577ac0dd",
"sha256:8c8f84131bf04f3b1dcf99b9763cec35c347164ab6ad006e18d2f99fcab05529" "sha256:eff1da7ea274c54cb8842853005a139f711646cbf6f1bcfb6c9b86a627f35ff0"
], ],
"markers": "python_version >= '3.7'", "markers": "python_version >= '3.7'",
"version": "==4.28.1" "version": "==4.28.2"
}, },
"hexdump": { "hexdump": {
"hashes": [ "hashes": [
@ -1358,19 +1386,19 @@
}, },
"hypothesis": { "hypothesis": {
"hashes": [ "hashes": [
"sha256:8f7318839b64bae1410704933fe01f3db5f80f20f2ba8d4ea3520443ae3a001d", "sha256:92005826894782cc57c47b98debe1bc889b716f0774e60f83536f14360a9199e",
"sha256:f1627022f75306c653115fbd169ba6a4322804f60533a7b46625fd0eecba43d5" "sha256:c4943d159f299a96b1c23277c4bcf0759a757888b67a6d73e9c70d853024aa15"
], ],
"index": "pypi", "index": "pypi",
"version": "==6.24.5" "version": "==6.27.2"
}, },
"identify": { "identify": {
"hashes": [ "hashes": [
"sha256:4f85f9bd8e6e5e2d61b2f8de5ff5313d8a1cfac4c88822d74406de45ad10bd82", "sha256:a33ae873287e81651c7800ca309dc1f84679b763c9c8b30680e16fbfa82f0107",
"sha256:8cb609a671d2f861ae7fe583711a43fd2faab0c892f39cbc4568b0c51b354238" "sha256:eba31ca80258de6bb51453084bff4a923187cd2193b9c13710f2516ab30732cc"
], ],
"markers": "python_full_version >= '3.6.1'", "markers": "python_full_version >= '3.6.1'",
"version": "==2.3.6" "version": "==2.4.0"
}, },
"idna": { "idna": {
"hashes": [ "hashes": [
@ -1682,47 +1710,47 @@
}, },
"opencv-python-headless": { "opencv-python-headless": {
"hashes": [ "hashes": [
"sha256:014e4a892f67043610521e673883da5ad98a6e61356a609c6502b9d22144f2f1", "sha256:01f76ca55fdb7e94c3e7eab5035376d06518155e3d88a08096e4670e57a0cee4",
"sha256:01a6411eb9eecf58db3a50fa761f0527bdd2b23b884b3bea44eb57e6d06e4767", "sha256:03349d9fb28703b2eaa8b1f333a6139b9849596ae4445cb1d76e2a7f5e4a2cf8",
"sha256:067e4736cdc70712f43c064bfe53ba80492c20479ad3290849337f948572c392", "sha256:29f5372dabdcd571074f0539bd294a2f5a245a00b871827af6d75a971b3f657e",
"sha256:09b21c48144a9dd82e435d66ccd834f3a391a2cd2b69f787aabbc7cd3576f2f1", "sha256:30261b87477a718993fa7cd8a44b7de986b81f8005e23110978c58fd53eb5e43",
"sha256:113b8ce88f337ab3a1ee670b8c5afc3fbf0d8aafec7b2ee897ad5efd9926ce90", "sha256:33e534fbc7a417a05ef6b14812fe8ff6b6b7152c22d502b61536c50ad63f80cb",
"sha256:1162de92301d0d13642afe8e163593982d3159ef21eba95a56ba3f5ca8024861", "sha256:3a8457918ecbca57669f141e7dba92e56af370876d022d75d58b94174d11e26b",
"sha256:1799ae397677bd3a68aff4eb4f5c122baf3ee73a26fe67391e0d618bd2b7d046", "sha256:4ef93f338b16e95418b69293924745a36f23e3d05da5ee10dde76af72b0889e3",
"sha256:1e0498bc4a39d887e84801b82df5cc943a5ae4b596511da7e69df64b263f293b", "sha256:5009a183be7a6817ff216dcb63ef95022c74e360011fa52aa33bc833256693b5",
"sha256:27b9118b342dc464590ed233c7f468c8cb68ee0e5d9734d9b67fb0bc1b10abd6", "sha256:5331ce17a094bea4f8132ee23b2eaade85904199c0d04501102c9bb889302c67",
"sha256:32e1f478457adeea4e1a3d6baf33310331740cceffd83a2f34e00a7e5bae6407", "sha256:659107ea6059b5cc953e1a32136a54998540cefea47b01dd62f1e806d10cbe39",
"sha256:37e20b1a0c623c7e01381668b83342387798d0f32ff8a7d7f3dd0ef127eb10fa", "sha256:6d949ec3e10cffa915ab1853e490674b8c420ba29eb0aeea72785f3e254dc7a1",
"sha256:3e32018e65eabc9aab10c7a109be5f735cf2c3934a47025b6903dcec034d460b", "sha256:6e7710aff3a0717f39c9ade77fdd9111203b09589539655044e73cc5d9960666",
"sha256:4b9fa45f1b364e7585fd4a2781565667efed49124748fb3ec2a66e40b7398338", "sha256:7b4bd3c6a0d2601b2619ae406eb85a41dff538a7c9cb2a54fb1e90631bf33887",
"sha256:526417214a1cad27e3d2d064e535868ae05f5a07a0e344d54fe81078bbacfa57", "sha256:7da49405e163b7a2cf891bf54a877ff3e198bc0bfe55009c1d19eb5a0153921d",
"sha256:540fa340b29dd86670e1f91bbcd2675df032b774f77d08909faf818696524617", "sha256:7f8dd594ea0b0049d1614d7bfba984ebd926b2f12670edf6ae3d9d5d6ff8f8f0",
"sha256:64bfc55fe60ed9ad778ec73161f0cea58e425a3f2f06293756f82880319bffa9", "sha256:8f8a06f75dc69631404e0846038d30ff43c9a9d60fcffe07c7a88f8b8c8c776c",
"sha256:685c41fce11c83a238ab40bd57d5b39c3fc4f87a898565cd27e4a521b5902a62", "sha256:99e678db353102119cbfe9d17aef520bacf585a3a287c4278dd1ce6fcd3be8f7",
"sha256:69c55e055e81a2eead40e6f2521ad27bdd11523dc0c1de5e5aea23041a0a49f0", "sha256:a1f9d41c6afe86fdbe85ac31ff9a6ce893af2d0fce68fbd1581dbbc0e4dfcb25",
"sha256:6e0757762096158b803b366c386f25f24715662465c448ed70ce65ecd93e41a2", "sha256:a1fd5bbf5db00432fb368c73e7d70ead13f69619b33e01dabf2906426a1a9277",
"sha256:79ca8b8feab911a80692fa3871c02526844f112e0f8bf4749f78383696cfaa4c", "sha256:a5461ad9789c784e75713d6c213c0e34b709073c71ec8ed94129419ea0ce7c01",
"sha256:807ac9d48c11b3f734d9de5eda69e1c4197204bac1e6db3d3d51859832df6fc1", "sha256:a6ba305364df31b8ac8471a719371d0c05e1e5f7cc5b8a2295e7e958f9bc39bb",
"sha256:84f037ba74482548c066ad5e341f77ebcd3d08d06a540a4a1182300045b8bde8", "sha256:bbf37d5de98b09e7513e61fca6ebf6466fd82c3c2f0475e51d2a3c80e0bc1a92",
"sha256:989d4817a2f22a601707ea36a3664a6c26cee155ed441363d5f29d73d9aec610", "sha256:bc9502064e8c3ff6f40b74c8a68fb31d0c9eae18c1d3f52d4e3f0ccda986f7cb",
"sha256:a148f8bdb54e03b1d899acb95aa6d870d6f10eed037d9b4700912068f3952c32", "sha256:cdea7ab1698b69274eb69b16efdd7b16944c5019c06f0ace9530f91862496cf4",
"sha256:bb29a911c4a67f9710b53b94766be1bb22926b98460790091d4620a534329736", "sha256:cdfec5dedd44617d94725170446cbe77c0b45044188bdc97cd251e698aeee822",
"sha256:c8b595731b5c519daf2bf3c074ec2080eb51e8efef198a4493f930b5e4439bb0", "sha256:db112fe9ffde7af96df09befcefdd33c4338f3a34fbfe894e04e66e14f584d9e",
"sha256:d1735296d88e16379d001c4946c1a196b87e3c3ff4da0c10762918dded02f670", "sha256:db461f2f0bfac155d56be7688ab6b43c140ce8b944aa5e6cfcb754bfeeeca750",
"sha256:d73972a62d3e48ee01783d4eee01152c93348c70305c91f85fb668ddcc8907ad", "sha256:dc303a5e09089001fd4fd51bd18a6d519e81ad5cbc36bb4b5fc3388d22a64be1",
"sha256:dabf703efe12ec4341efd71c1dd384542e28fc49b7d2ea02f185ef22bb75a1c8", "sha256:eb9e571427b7f44b8d8f9a3b6b7b25e45bc8e8895ed3cf3ecd917c0125cf3477",
"sha256:fdd80034749b8b2f211c2549293b34af691e4f90bf17b320a84b09b784275cc2" "sha256:f4fbd431b2b0014b7d99e870f428eebf50a0149e4be1a72b905569aaadf4b540"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.5.4.58" "version": "==4.5.4.60"
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:096d689d78ca690e4cd8a89568ba06d07ca097e3306a4381635073ca91479966", "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
"sha256:14317396d1e8cdb122989b916fa2c7e9ca8e2be9e8060a6eff75b6b7b4d8a7e0" "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==21.2" "version": "==21.3"
}, },
"parameterized": { "parameterized": {
"hashes": [ "hashes": [
@ -1923,11 +1951,11 @@
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"
], ],
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==2.4.7" "version": "==3.0.6"
}, },
"pyprof2calltree": { "pyprof2calltree": {
"hashes": [ "hashes": [
@ -2007,33 +2035,35 @@
}, },
"scipy": { "scipy": {
"hashes": [ "hashes": [
"sha256:1437073f1d4664990879aa8f9547524764372e0fef84a077be4b19e82bba7a8d", "sha256:033ce76ed4e9f62923e1f8124f7e2b0800db533828c853b402c7eec6e9465d80",
"sha256:17fd991a275e4283453f89d404209aa92059ac68d76d804b4bc1716a3742e1b5", "sha256:173308efba2270dcd61cd45a30dfded6ec0085b4b6eb33b5eb11ab443005e088",
"sha256:1ea6233f5a365cb7945b4304bd06323ece3ece85d6a3fa8598d2f53e513467c9", "sha256:21b66200cf44b1c3e86495e3a436fc7a26608f92b8d43d344457c54f1c024cbc",
"sha256:2d25272c03ee3c0fe5e0dff1bb7889280bb6c9e1766fa9c7bde81ad8a5f78694", "sha256:2c56b820d304dffcadbbb6cbfbc2e2c79ee46ea291db17e288e73cd3c64fefa9",
"sha256:30bdda199667e74b50208a793eb1ba47a04e5e3fa16f5ff06c6f7969ae78e4da", "sha256:304dfaa7146cffdb75fbf6bb7c190fd7688795389ad060b970269c8576d038e9",
"sha256:359b60a0cccd17723b9d5e329a5212a710e771a3ddde800e472fb93732756c46", "sha256:3f78181a153fa21c018d346f595edd648344751d7f03ab94b398be2ad083ed3e",
"sha256:39f838ea5ce8da868785193d88d05cf5a6d5c390804ec99de29a28e1dcdd53e6", "sha256:4d242d13206ca4302d83d8a6388c9dfce49fc48fdd3c20efad89ba12f785bf9e",
"sha256:4d175ba93e00d8eef8f7cd70d4d88a9106a86800c82ea03cf2268c36d6545483", "sha256:5d1cc2c19afe3b5a546ede7e6a44ce1ff52e443d12b231823268019f608b9b12",
"sha256:5273d832fb9cd5724ee0d335c16a903b923441107dd973d27fc4293075a9f4e3", "sha256:5f2cfc359379c56b3a41b17ebd024109b2049f878badc1e454f31418c3a18436",
"sha256:54951f51d731c832b1b8885e0a92e89f33d087de7e40d02078bf0d49c7cbdbb5", "sha256:65bd52bf55f9a1071398557394203d881384d27b9c2cad7df9a027170aeaef93",
"sha256:74f518ce542533054695f743e4271cb8986b63f95bb51d70fcee4f3929cbff7d", "sha256:7edd9a311299a61e9919ea4192dd477395b50c014cdc1a1ac572d7c27e2207fa",
"sha256:7b1d0f5f524518f1a86f288443528e4ff4a739c0966db663af4129b7ac7849f8", "sha256:8499d9dd1459dc0d0fe68db0832c3d5fc1361ae8e13d05e6849b358dc3f2c279",
"sha256:82c5befebf54d799d77e5f0205c03030f57f69ba2541baa44d2e6ad138c28cd3", "sha256:866ada14a95b083dd727a845a764cf95dd13ba3dc69a16b99038001b05439709",
"sha256:8482c8e45857ab0a5446eb7460d2307a27cbbe659d6d2257820c6d6eb950fd0f", "sha256:87069cf875f0262a6e3187ab0f419f5b4280d3dcf4811ef9613c605f6e4dca95",
"sha256:87cf3964db0f1cce17aeed5bfc1b89a6b4b07dbfc48e50d21fa3549e00456803", "sha256:93378f3d14fff07572392ce6a6a2ceb3a1f237733bd6dcb9eb6a2b29b0d19085",
"sha256:8b5726a0fedeaa6beb1095e4466998bdd1d1e960b28db9b5a16c89cbd7b2ebf1", "sha256:95c2d250074cfa76715d58830579c64dff7354484b284c2b8b87e5a38321672c",
"sha256:97eb573e361a73a553b915dc195c6f72a08249964b1a33f157f9659f3b6210d1", "sha256:ab5875facfdef77e0a47d5fd39ea178b58e60e454a4c85aa1e52fcb80db7babf",
"sha256:a80eb01c43fd98257ec7a49ff5cec0edba32031b5f86503f55399a48cb2c5379", "sha256:ca36e7d9430f7481fc7d11e015ae16fbd5575615a8e9060538104778be84addf",
"sha256:cac71d5476a6f56b50459da21f6221707e0051ebd428b2137db32ef4a43bb15e", "sha256:ceebc3c4f6a109777c0053dfa0282fddb8893eddfb0d598574acfb734a926168",
"sha256:d86abd1ddf421dea5e9cebfeb4de0d205b3dc04e78249afedba9c6c3b2227ff2", "sha256:e2c036492e673aad1b7b0d0ccdc0cb30a968353d2c4bf92ac8e73509e1bf212c",
"sha256:dc2d1bf41294e63c7302bf499973ac0c7f73c93c01763db43055f6525234bf11", "sha256:eb326658f9b73c07081300daba90a8746543b5ea177184daed26528273157294",
"sha256:e08b81fcd9bf98740b58dc6fdd7879e33a64dcb682201c1135f7d4a75216bb05", "sha256:eb7ae2c4dbdb3c9247e07acc532f91077ae6dbc40ad5bd5dca0bb5a176ee9bda",
"sha256:e3efe7ef75dfe627b354ab0af0dbc918eadee97cc80ff1aabea6d3e01114ebdd", "sha256:edad1cf5b2ce1912c4d8ddad20e11d333165552aba262c882e28c78bbc09dbf6",
"sha256:fa2dbabaaecdb502641b0b3c00dec05fb475ae48655c66da16c9ed24eda1e711" "sha256:eef93a446114ac0193a7b714ce67659db80caf940f3232bad63f4c7a81bc18df",
], "sha256:f7eaea089345a35130bc9a39b89ec1ff69c208efa97b3f8b25ea5d4c41d88094",
"index": "pypi", "sha256:f99d206db1f1ae735a8192ab93bd6028f3a42f6fa08467d37a14eb96c9dd34a3"
"version": "==1.7.2" ],
"index": "pypi",
"version": "==1.7.3"
}, },
"setuptools-scm": { "setuptools-scm": {
"hashes": [ "hashes": [

@ -1,4 +1,4 @@
![](https://user-images.githubusercontent.com/37757984/127420744-89ca219c-8f8e-46d3-bccf-c1cb53b81bb1.png) ![](https://i.imgur.com/b0ZyIx5.jpg)
Table of Contents Table of Contents
======================= =======================
@ -21,16 +21,16 @@ What is openpilot?
<table> <table>
<tr> <tr>
<td><a href="https://www.youtube.com/watch?v=mgAbfr42oI8" title="YouTube" rel="noopener"><img src="https://i.imgur.com/kAtT6Ei.png"></a></td> <td><a href="https://youtu.be/NmBfgOanCyk" title="Video By Greer Viau"><img src="https://i.imgur.com/1w8c6d2.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=394rJKeh76k" title="YouTube" rel="noopener"><img src="https://i.imgur.com/lTt8cS2.png"></a></td> <td><a href="https://youtu.be/VHKyqZ7t8Gw" title="Video By Logan LeGrand"><img src="https://i.imgur.com/LnBucik.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=1iNOc3cq8cs" title="YouTube" rel="noopener"><img src="https://i.imgur.com/ANnuSpe.png"></a></td> <td><a href="https://youtu.be/VxiR4iyBruo" title="Video By Charlie Kim"><img src="https://i.imgur.com/4Qoy48c.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=Vr6NgrB-zHw" title="YouTube" rel="noopener"><img src="https://i.imgur.com/Qypanuq.png"></a></td> <td><a href="https://youtu.be/-IkImTe1NYE" title="Video By Aragon"><img src="https://i.imgur.com/04VNzPf.jpg"></a></td>
</tr> </tr>
<tr> <tr>
<td><a href="https://www.youtube.com/watch?v=Ug41KIKF0oo" title="YouTube" rel="noopener"><img src="https://i.imgur.com/3caZ7xM.png"></a></td> <td><a href="https://youtu.be/iIUICQkdwFQ" title="Video By Logan LeGrand"><img src="https://i.imgur.com/b1LHQTy.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=NVR_CdG1FRg" title="YouTube" rel="noopener"><img src="https://i.imgur.com/bAZOwql.png"></a></td> <td><a href="https://youtu.be/XOsa0FsVIsg" title="Video By PinoyDrives"><img src="https://i.imgur.com/6FG0Bd8.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=tkEvIdzdfUE" title="YouTube" rel="noopener"><img src="https://i.imgur.com/EFINEzG.png"></a></td> <td><a href="https://youtu.be/bCwcJ98R_Xw" title="Video By JS"><img src="https://i.imgur.com/zO18CbW.jpg"></a></td>
<td><a href="https://www.youtube.com/watch?v=_P-N1ewNne4" title="YouTube" rel="noopener"><img src="https://i.imgur.com/gAyAq22.png"></a></td> <td><a href="https://youtu.be/BQ0tF3MTyyc" title="Video By Tsai-Fi"><img src="https://i.imgur.com/eZzelq3.jpg"></a></td>
</tr> </tr>
</table> </table>
@ -40,7 +40,7 @@ Running in a car
To use openpilot in a car, you need four things To use openpilot in a car, you need four things
* This software. It's free and available right here. * This software. It's free and available right here.
* One of [the 140+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot. * One of [the 150+ supported cars](docs/CARS.md). We support Honda, Toyota, Hyundai, Nissan, Kia, Chrysler, Lexus, Acura, Audi, VW, and more. If your car is not supported, but has adaptive cruise control and lane keeping assist, it's likely able to run openpilot.
* A supported device to run this software. This can be a [comma two](https://comma.ai/shop/products/two), [comma three](https://comma.ai/shop/products/three), or if you like to experiment, a [Ubuntu computer with webcams](https://github.com/commaai/openpilot/tree/master/tools/webcam). * A supported device to run this software. This can be a [comma two](https://comma.ai/shop/products/two), [comma three](https://comma.ai/shop/products/three), or if you like to experiment, a [Ubuntu computer with webcams](https://github.com/commaai/openpilot/tree/master/tools/webcam).
* A way to connect to your car. With a comma two or three, you need only a [car harness](https://comma.ai/shop/products/car-harness). With an EON Gold or PC, you also need a [black panda](https://comma.ai/shop/products/panda). * A way to connect to your car. With a comma two or three, you need only a [car harness](https://comma.ai/shop/products/car-harness). With an EON Gold or PC, you also need a [black panda](https://comma.ai/shop/products/panda).

@ -1,3 +1,10 @@
Version 0.8.12 (2021-12-XX)
========================
* New alert sounds
* Honda Accord 2021 support thanks to csouers!
* Honda Accord Hybrid 2021 support thanks to csouers!
* Lexus RC 2020 support thanks to ErichMoraga!
Version 0.8.11 (2021-11-29) Version 0.8.11 (2021-11-29)
======================== ========================
* Support for CAN FD on the red panda * Support for CAN FD on the red panda

@ -60,7 +60,6 @@ if arch == "aarch64" and TICI:
arch = "larch64" arch = "larch64"
USE_WEBCAM = os.getenv("USE_WEBCAM") is not None USE_WEBCAM = os.getenv("USE_WEBCAM") is not None
USE_FRAME_STREAM = os.getenv("USE_FRAME_STREAM") is not None
lenv = { lenv = {
"PATH": os.environ['PATH'], "PATH": os.environ['PATH'],
@ -352,7 +351,7 @@ if GetOption("clazy"):
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0] qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks) qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
Export('env', 'qt_env', 'arch', 'real_arch', 'SHARED', 'USE_WEBCAM', 'USE_FRAME_STREAM') Export('env', 'qt_env', 'arch', 'real_arch', 'SHARED', 'USE_WEBCAM')
SConscript(['selfdrive/common/SConscript']) SConscript(['selfdrive/common/SConscript'])
Import('_common', '_gpucommon', '_gpu_libs') Import('_common', '_gpucommon', '_gpu_libs')
@ -387,7 +386,7 @@ rednose_config = {
}, },
} }
if arch != "aarch64": if arch not in ["aarch64", "larch64"]:
rednose_config['to_build'].update({ rednose_config['to_build'].update({
'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, []), 'gnss': ('#selfdrive/locationd/models/gnss_kf.py', True, []),
'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, []), 'loc_4': ('#selfdrive/locationd/models/loc_kf.py', True, []),

@ -1 +1 @@
Subproject commit 032aca6ca38a342e26fb9cc986b7f72b91cd9b55 Subproject commit c4a1c9fa00c5915fbdf7f6c29f5968dfc1be0630

@ -3,7 +3,7 @@ import os
import requests import requests
from datetime import datetime, timedelta from datetime import datetime, timedelta
from common.basedir import PERSIST from common.basedir import PERSIST
from selfdrive.version import version from selfdrive.version import get_version
API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com') API_HOST = os.getenv('API_HOST', 'https://api.commadotai.com')
@ -34,13 +34,13 @@ class Api():
if isinstance(token, bytes): if isinstance(token, bytes):
token = token.decode('utf8') token = token.decode('utf8')
return token return token
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params): def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
headers = {} headers = {}
if access_token is not None: if access_token is not None:
headers['Authorization'] = "JWT "+access_token headers['Authorization'] = "JWT " + access_token
headers['User-Agent'] = "openpilot-" + version headers['User-Agent'] = "openpilot-" + get_version()
return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params) return requests.request(method, API_HOST + "/" + endpoint, timeout=timeout, headers=headers, params=params)

@ -1,23 +0,0 @@
import os
import sysconfig
from Cython.Distutils import build_ext
def get_ext_filename_without_platform_suffix(filename):
name, ext = os.path.splitext(filename)
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
if ext_suffix == ext:
return filename
ext_suffix = ext_suffix.replace(ext, '')
idx = name.find(ext_suffix)
if idx == -1:
return filename
else:
return name[:idx] + ext
class BuildExtWithoutPlatformSuffix(build_ext):
def get_ext_filename(self, ext_name):
filename = super().get_ext_filename(ext_name)
return get_ext_filename_without_platform_suffix(filename)

@ -7,8 +7,8 @@
| Acura | ILX 2016-19 | AcuraWatch Plus | openpilot | 25mph<sup>1</sup> | 25mph | | Acura | ILX 2016-19 | AcuraWatch Plus | openpilot | 25mph<sup>1</sup> | 25mph |
| Acura | RDX 2016-18 | AcuraWatch Plus | openpilot | 25mph<sup>1</sup> | 12mph | | Acura | RDX 2016-18 | AcuraWatch Plus | openpilot | 25mph<sup>1</sup> | 12mph |
| Acura | RDX 2019-21 | All | Stock | 0mph | 3mph | | Acura | RDX 2019-21 | All | Stock | 0mph | 3mph |
| Honda | Accord 2018-20 | All | Stock | 0mph | 3mph | | Honda | Accord 2018-21 | All | Stock | 0mph | 3mph |
| Honda | Accord Hybrid 2018-20 | All | Stock | 0mph | 3mph | | Honda | Accord Hybrid 2018-21 | All | Stock | 0mph | 3mph |
| Honda | Civic Hatchback 2017-21 | Honda Sensing | Stock | 0mph | 12mph | | Honda | Civic Hatchback 2017-21 | Honda Sensing | Stock | 0mph | 12mph |
| Honda | Civic Coupe 2016-18 | Honda Sensing | openpilot | 0mph | 12mph | | Honda | Civic Coupe 2016-18 | Honda Sensing | openpilot | 0mph | 12mph |
| Honda | Civic Coupe 2019-20 | All | Stock | 0mph | 2mph<sup>2</sup> | | Honda | Civic Coupe 2019-20 | All | Stock | 0mph | 2mph<sup>2</sup> |
@ -34,9 +34,10 @@
| Lexus | ES Hybrid 2017-18 | LSS | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | ES Hybrid 2017-18 | LSS | Stock<sup>3</sup>| 0mph | 0mph |
| Lexus | ES Hybrid 2019-21 | All | openpilot | 0mph | 0mph | | Lexus | ES Hybrid 2019-21 | All | openpilot | 0mph | 0mph |
| Lexus | IS 2017-2019 | All | Stock | 22mph | 0mph | | Lexus | IS 2017-2019 | All | Stock | 22mph | 0mph |
| Lexus | NX 2018 | All | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | NX 2018-2019 | All | Stock<sup>3</sup>| 0mph | 0mph |
| Lexus | NX 2020 | All | openpilot | 0mph | 0mph | | Lexus | NX 2020 | All | openpilot | 0mph | 0mph |
| Lexus | NX Hybrid 2018-19 | All | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | NX Hybrid 2018-19 | All | Stock<sup>3</sup>| 0mph | 0mph |
| Lexus | RC 2020 | All | Stock | 22mph | 0mph |
| Lexus | RX 2016-18 | All | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | RX 2016-18 | All | Stock<sup>3</sup>| 0mph | 0mph |
| Lexus | RX 2020-21 | All | openpilot | 0mph | 0mph | | Lexus | RX 2020-21 | All | openpilot | 0mph | 0mph |
| Lexus | RX Hybrid 2016-19 | All | Stock<sup>3</sup>| 0mph | 0mph | | Lexus | RX Hybrid 2016-19 | All | Stock<sup>3</sup>| 0mph | 0mph |
@ -49,7 +50,7 @@
| Toyota | Camry 2021-22 | All | openpilot | 0mph<sup>4</sup> | 0mph | | Toyota | Camry 2021-22 | All | openpilot | 0mph<sup>4</sup> | 0mph |
| Toyota | Camry Hybrid 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph | | Toyota | Camry Hybrid 2018-20 | All | Stock | 0mph<sup>4</sup> | 0mph |
| Toyota | Camry Hybrid 2021-22 | All | openpilot | 0mph | 0mph | | Toyota | Camry Hybrid 2021-22 | All | openpilot | 0mph | 0mph |
| Toyota | C-HR 2017-20 | All | Stock | 0mph | 0mph | | Toyota | C-HR 2017-21 | All | Stock | 0mph | 0mph |
| Toyota | C-HR Hybrid 2017-19 | All | Stock | 0mph | 0mph | | Toyota | C-HR Hybrid 2017-19 | All | Stock | 0mph | 0mph |
| Toyota | Corolla 2017-19 | All | Stock<sup>3</sup>| 20mph<sup>1</sup> | 0mph | | Toyota | Corolla 2017-19 | All | Stock<sup>3</sup>| 20mph<sup>1</sup> | 0mph |
| Toyota | Corolla 2020-22 | All | openpilot | 0mph | 0mph | | Toyota | Corolla 2020-22 | All | openpilot | 0mph | 0mph |

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9e2d3a3b513bf3d0ea4255fc41d57e84b141643fd3d47268c33862110387ea05 oid sha256:ebb75bbd4c19994752f038f181359dce358a2c95c4ad1760d2d1ee346a45503d
size 56685051 size 56684955

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:43d362484b134fce8d05ff0cb0971c53ceee64d96a4d97c1b6d19cc849f40588 oid sha256:6e004a9ff90304fd3dfd661f3f52886affa677d486ff78c131d8a4887757b5f1
size 56707084 size 57552499

@ -1 +1 @@
Subproject commit 76dc825ad8329bdc3fff97583ad88a7af81c7e2e Subproject commit b0bc1b5197c63231c7af9e22929c8d41212631b6

@ -36,7 +36,6 @@ common/filter_simple.py
common/stat_live.py common/stat_live.py
common/spinner.py common/spinner.py
common/text_window.py common/text_window.py
common/cython_hacks.py
common/SConscript common/SConscript
common/kalman/.gitignore common/kalman/.gitignore
@ -306,6 +305,8 @@ selfdrive/loggerd/omx_encoder.h
selfdrive/loggerd/logger.cc selfdrive/loggerd/logger.cc
selfdrive/loggerd/logger.h selfdrive/loggerd/logger.h
selfdrive/loggerd/loggerd.cc selfdrive/loggerd/loggerd.cc
selfdrive/loggerd/loggerd.h
selfdrive/loggerd/main.cc
selfdrive/loggerd/bootlog.cc selfdrive/loggerd/bootlog.cc
selfdrive/loggerd/raw_logger.cc selfdrive/loggerd/raw_logger.cc
selfdrive/loggerd/raw_logger.h selfdrive/loggerd/raw_logger.h
@ -366,8 +367,6 @@ selfdrive/camerad/snapshot/*
selfdrive/camerad/include/* selfdrive/camerad/include/*
selfdrive/camerad/cameras/camera_common.h selfdrive/camerad/cameras/camera_common.h
selfdrive/camerad/cameras/camera_common.cc selfdrive/camerad/cameras/camera_common.cc
selfdrive/camerad/cameras/camera_frame_stream.cc
selfdrive/camerad/cameras/camera_frame_stream.h
selfdrive/camerad/cameras/camera_qcom.cc selfdrive/camerad/cameras/camera_qcom.cc
selfdrive/camerad/cameras/camera_qcom.h selfdrive/camerad/cameras/camera_qcom.h
selfdrive/camerad/cameras/camera_replay.cc selfdrive/camerad/cameras/camera_replay.cc

@ -4,7 +4,6 @@ selfdrive/timezoned.py
selfdrive/assets/navigation/* selfdrive/assets/navigation/*
selfdrive/assets/training_wide/* selfdrive/assets/training_wide/*
selfdrive/assets/sounds_tici/*
selfdrive/camerad/cameras/camera_qcom2.cc selfdrive/camerad/cameras/camera_qcom2.cc
selfdrive/camerad/cameras/camera_qcom2.h selfdrive/camerad/cameras/camera_qcom2.h

@ -0,0 +1,16 @@
#!/bin/bash
set -e
RED="\033[0;31m"
GREEN="\033[0;32m"
CLEAR="\033[0m"
BRANCHES="devel dashcam dashcam3 release2 release3"
for b in $BRANCHES; do
if git diff --quiet origin/$b origin/$b-staging && [ "$(git rev-parse origin/$b)" = "$(git rev-parse origin/$b-staging)" ]; then
printf "%-10s $GREEN ok $CLEAR\n" "$b"
else
printf "%-10s $RED mismatch $CLEAR\n" "$b"
fi
done

@ -0,0 +1,11 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "usage: $0 <pull-request-number>"
exit 1
fi
BASE="https://github.com/commaai/openpilot/pull/"
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
curl -L $BASE/$PR_NUM.patch | git apply

@ -11,7 +11,7 @@
<p>FCC ID: 2AOHHTURBOXSOMD845</p> <p>FCC ID: 2AOHHTURBOXSOMD845</p>
<h5>Quectel/EG25-G</h5> <h5>Quectel/EG25-G</h5>
<p>FCC ID: XMR201903EG25GM</p> <p>FCC ID: XMR201903EG25G</p>
<p> <p>
This device complies with Part 15 of the FCC Rules. This device complies with Part 15 of the FCC Rules.
Operation is subject to the following two conditions: Operation is subject to the following two conditions:

Binary file not shown.

@ -30,7 +30,7 @@ from selfdrive.hardware import HARDWARE, PC
from selfdrive.loggerd.config import ROOT from selfdrive.loggerd.config import ROOT
from selfdrive.loggerd.xattr_cache import getxattr, setxattr from selfdrive.loggerd.xattr_cache import getxattr, setxattr
from selfdrive.swaglog import cloudlog, SWAGLOG_DIR from selfdrive.swaglog import cloudlog, SWAGLOG_DIR
from selfdrive.version import version, get_version, get_git_remote, get_git_branch, get_git_commit from selfdrive.version import get_version, get_origin, get_short_branch, get_commit
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai') ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4")) HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
@ -176,9 +176,9 @@ def getMessage(service=None, timeout=1000):
def getVersion(): def getVersion():
return { return {
"version": get_version(), "version": get_version(),
"remote": get_git_remote(), "remote": get_origin(),
"branch": get_git_branch(), "branch": get_short_branch(),
"commit": get_git_commit(), "commit": get_commit(),
} }
@ -551,7 +551,7 @@ def main():
except socket.timeout: except socket.timeout:
try: try:
r = requests.get("http://api.commadotai.com/v1/me", allow_redirects=False, r = requests.get("http://api.commadotai.com/v1/me", allow_redirects=False,
headers={"User-Agent": f"openpilot-{version}"}, timeout=15.0) headers={"User-Agent": f"openpilot-{get_version()}"}, timeout=15.0)
if r.status_code == 302 and r.headers['Location'].startswith("http://u.web2go.com"): if r.status_code == 302 and r.headers['Location'].startswith("http://u.web2go.com"):
params.put_bool("PrimeRedirected", True) params.put_bool("PrimeRedirected", True)
except Exception: except Exception:

@ -6,7 +6,7 @@ from multiprocessing import Process
from common.params import Params from common.params import Params
from selfdrive.manager.process import launcher from selfdrive.manager.process import launcher
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.version import version, dirty from selfdrive.version import get_version, get_dirty
ATHENA_MGR_PID_PARAM = "AthenadPid" ATHENA_MGR_PID_PARAM = "AthenadPid"
@ -14,7 +14,7 @@ ATHENA_MGR_PID_PARAM = "AthenadPid"
def main(): def main():
params = Params() params = Params()
dongle_id = params.get("DongleId").decode('utf-8') dongle_id = params.get("DongleId").decode('utf-8')
cloudlog.bind_global(dongle_id=dongle_id, version=version, dirty=dirty) cloudlog.bind_global(dongle_id=dongle_id, version=get_version(), dirty=get_dirty())
try: try:
while 1: while 1:

@ -1,2 +1,3 @@
boardd boardd
boardd_api_impl.cpp boardd_api_impl.cpp
tests/test_boardd_usbprotocol

@ -1,6 +1,9 @@
Import('env', 'envCython', 'common', 'cereal', 'messaging') Import('env', 'envCython', 'common', 'cereal', 'messaging')
env.Program('boardd', ['boardd.cc', 'panda.cc', 'pigeon.cc'], LIBS=['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']) libs = ['usb-1.0', common, cereal, messaging, 'pthread', 'zmq', 'capnp', 'kj']
env.Program('boardd', ['boardd.cc', 'panda.cc', 'pigeon.cc'], LIBS=libs)
env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc']) env.Library('libcan_list_to_can_capnp', ['can_list_to_can_capnp.cc'])
envCython.Program('boardd_api_impl.so', 'boardd_api_impl.pyx', LIBS=["can_list_to_can_capnp", 'capnp', 'kj'] + envCython["LIBS"]) envCython.Program('boardd_api_impl.so', 'boardd_api_impl.pyx', LIBS=["can_list_to_can_capnp", 'capnp', 'kj'] + envCython["LIBS"])
if GetOption('test'):
env.Program('tests/test_boardd_usbprotocol', ['tests/test_boardd_usbprotocol.cc', 'panda.cc'], LIBS=libs)

@ -62,7 +62,7 @@ std::atomic<bool> pigeon_active(false);
ExitHandler do_exit; ExitHandler do_exit;
std::string get_time_str(const struct tm &time) { static std::string get_time_str(const struct tm &time) {
char s[30] = {'\0'}; char s[30] = {'\0'};
std::strftime(s, std::size(s), "%Y-%m-%d %H:%M:%S", &time); std::strftime(s, std::size(s), "%Y-%m-%d %H:%M:%S", &time);
return s; return s;
@ -70,11 +70,43 @@ std::string get_time_str(const struct tm &time) {
bool check_all_connected(const std::vector<Panda *> &pandas) { bool check_all_connected(const std::vector<Panda *> &pandas) {
for (const auto& panda : pandas) { for (const auto& panda : pandas) {
if (!panda->connected) return false; if (!panda->connected) {
do_exit = true;
return false;
}
} }
return true; return true;
} }
enum class SyncTimeDir { TO_PANDA, FROM_PANDA };
void sync_time(Panda *panda, SyncTimeDir dir) {
if (!panda->has_rtc) return;
setenv("TZ", "UTC", 1);
struct tm sys_time = util::get_time();
struct tm rtc_time = panda->get_rtc();
if (dir == SyncTimeDir::TO_PANDA) {
if (util::time_valid(sys_time)) {
// Write time to RTC if it looks reasonable
double seconds = difftime(mktime(&rtc_time), mktime(&sys_time));
if (std::abs(seconds) > 1.1) {
panda->set_rtc(sys_time);
LOGW("Updating panda RTC. dt = %.2f System: %s RTC: %s",
seconds, get_time_str(sys_time).c_str(), get_time_str(rtc_time).c_str());
}
}
} else if (dir == SyncTimeDir::FROM_PANDA) {
if (!util::time_valid(sys_time) && util::time_valid(rtc_time)) {
const struct timeval tv = {mktime(&rtc_time), 0};
settimeofday(&tv, 0);
LOGE("System time wrong, setting from RTC. System: %s RTC: %s",
get_time_str(sys_time).c_str(), get_time_str(rtc_time).c_str());
}
}
}
bool safety_setter_thread(std::vector<Panda *> pandas) { bool safety_setter_thread(std::vector<Panda *> pandas) {
LOGD("Starting safety setter thread"); LOGD("Starting safety setter thread");
@ -168,19 +200,7 @@ Panda *usb_connect(std::string serial="", uint32_t index=0) {
std::call_once(connected_once, &Panda::set_usb_power_mode, panda, cereal::PeripheralState::UsbPowerMode::CDP); std::call_once(connected_once, &Panda::set_usb_power_mode, panda, cereal::PeripheralState::UsbPowerMode::CDP);
#endif #endif
if (panda->has_rtc) { sync_time(panda.get(), SyncTimeDir::FROM_PANDA);
setenv("TZ","UTC",1);
struct tm sys_time = util::get_time();
struct tm rtc_time = panda->get_rtc();
if (!util::time_valid(sys_time) && util::time_valid(rtc_time)) {
LOGE("System time wrong, setting from RTC. System: %s RTC: %s",
get_time_str(sys_time).c_str(), get_time_str(rtc_time).c_str());
const struct timeval tv = {mktime(&rtc_time), 0};
settimeofday(&tv, 0);
}
}
return panda.release(); return panda.release();
} }
@ -194,14 +214,8 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) {
subscriber->setTimeout(100); subscriber->setTimeout(100);
// run as fast as messages come in // run as fast as messages come in
while (!do_exit) { while (!do_exit && check_all_connected(pandas)) {
if (!check_all_connected(pandas)) {
do_exit = true;
break;
}
Message * msg = subscriber->receive(); Message * msg = subscriber->receive();
if (!msg) { if (!msg) {
if (errno == EINTR) { if (errno == EINTR) {
do_exit = true; do_exit = true;
@ -239,12 +253,7 @@ void can_recv_thread(std::vector<Panda *> pandas) {
uint64_t next_frame_time = nanos_since_boot() + dt; uint64_t next_frame_time = nanos_since_boot() + dt;
std::vector<can_frame> raw_can_data; std::vector<can_frame> raw_can_data;
while (!do_exit) { while (!do_exit && check_all_connected(pandas)) {
if (!check_all_connected(pandas)){
do_exit = true;
break;
}
bool comms_healthy = true; bool comms_healthy = true;
raw_can_data.clear(); raw_can_data.clear();
for (const auto& panda : pandas) { for (const auto& panda : pandas) {
@ -315,7 +324,7 @@ bool send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool s
for (uint32_t i=0; i<pandas.size(); i++) { for (uint32_t i=0; i<pandas.size(); i++) {
auto panda = pandas[i]; auto panda = pandas[i];
auto pandaState = pandaStates[i]; const auto &pandaState = pandaStates[i];
// Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node // Make sure CAN buses are live: safety_setter_thread does not work if Panda CAN are silent and there is only one other CAN node
if (pandaState.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) { if (pandaState.safety_model == (uint8_t)(cereal::CarParams::SafetyModel::SILENT)) {
@ -416,12 +425,8 @@ void panda_state_thread(PubMaster *pm, std::vector<Panda *> pandas, bool spoofin
LOGD("start panda state thread"); LOGD("start panda state thread");
// run at 2hz // run at 2hz
while (!do_exit) { while (!do_exit && check_all_connected(pandas)) {
if(!check_all_connected(pandas)) { // send out peripheralState
do_exit = true;
break;
}
send_peripheral_state(pm, peripheral_panda); send_peripheral_state(pm, peripheral_panda);
ignition = send_panda_states(pm, pandas, spoofing_started); ignition = send_panda_states(pm, pandas, spoofing_started);
@ -433,7 +438,7 @@ void panda_state_thread(PubMaster *pm, std::vector<Panda *> pandas, bool spoofin
break; break;
} }
// clear VIN, CarParams, and set new safety on car start // clear ignition-based params and set new safety on car start
if (ignition && !ignition_last) { if (ignition && !ignition_last) {
params.clearAll(CLEAR_ON_IGNITION_ON); params.clearAll(CLEAR_ON_IGNITION_ON);
if (!safety_future.valid() || safety_future.wait_for(0ms) == std::future_status::ready) { if (!safety_future.valid() || safety_future.wait_for(0ms) == std::future_status::ready) {
@ -528,21 +533,8 @@ void peripheral_control_thread(Panda *panda) {
} }
// Write to rtc once per minute when no ignition present // Write to rtc once per minute when no ignition present
if ((panda->has_rtc) && !ignition && (cnt % 120 == 1)) { if (!ignition && (cnt % 120 == 1)) {
// Write time to RTC if it looks reasonable sync_time(panda, SyncTimeDir::TO_PANDA);
setenv("TZ","UTC",1);
struct tm sys_time = util::get_time();
if (util::time_valid(sys_time)) {
struct tm rtc_time = panda->get_rtc();
double seconds = difftime(mktime(&rtc_time), mktime(&sys_time));
if (std::abs(seconds) > 1.1) {
panda->set_rtc(sys_time);
LOGW("Updating panda RTC. dt = %.2f System: %s RTC: %s",
seconds, get_time_str(sys_time).c_str(), get_time_str(rtc_time).c_str());
}
}
} }
} }
} }

@ -363,7 +363,8 @@ uint8_t Panda::len_to_dlc(uint8_t len) {
} }
} }
void Panda::can_send(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) {
if (send.size() < (can_data_list.size() * CANPACKET_MAX_SIZE)) { if (send.size() < (can_data_list.size() * CANPACKET_MAX_SIZE)) {
send.resize(can_data_list.size() * CANPACKET_MAX_SIZE); send.resize(can_data_list.size() * CANPACKET_MAX_SIZE);
} }
@ -383,7 +384,7 @@ void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
} }
auto can_data = cmsg.getDat(); auto can_data = cmsg.getDat();
uint8_t data_len_code = len_to_dlc(can_data.size()); uint8_t data_len_code = len_to_dlc(can_data.size());
assert(can_data.size() <= (hw_type == cereal::PandaState::PandaType::RED_PANDA) ? 64 : 8); assert(can_data.size() <= ((hw_type == cereal::PandaState::PandaType::RED_PANDA) ? 64 : 8));
assert(can_data.size() == dlc_to_len[data_len_code]); assert(can_data.size() == dlc_to_len[data_len_code]);
can_header header; can_header header;
@ -410,67 +411,58 @@ void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
ptr += copy_size + 1; ptr += copy_size + 1;
counter++; counter++;
} }
usb_bulk_write(3, to_write, ptr, 5); write_func(to_write, ptr);
} }
} }
} }
void Panda::can_send(capnp::List<cereal::CanData>::Reader can_data_list) {
pack_can_buffer(can_data_list, [=](uint8_t* data, size_t size) {
usb_bulk_write(3, data, size, 5);
});
}
bool Panda::can_receive(std::vector<can_frame>& out_vec) { bool Panda::can_receive(std::vector<can_frame>& out_vec) {
uint8_t data[RECV_SIZE]; uint8_t data[RECV_SIZE];
int recv = usb_bulk_read(0x81, (uint8_t*)data, RECV_SIZE); int recv = usb_bulk_read(0x81, (uint8_t*)data, RECV_SIZE);
// Not sure if this can happen
if (recv < 0) recv = 0;
if (recv == RECV_SIZE) {
LOGW("Receive buffer full");
}
if (!comms_healthy) { if (!comms_healthy) {
return false; return false;
} }
if (recv == RECV_SIZE) {
LOGW("Panda receive buffer full");
}
static uint8_t tail[CANPACKET_MAX_SIZE]; return (recv <= 0) ? true : unpack_can_buffer(data, recv, out_vec);
uint8_t tail_size = 0; }
uint8_t counter = 0;
for (int i = 0; i < recv; i += USBPACKET_MAX_SIZE) { bool Panda::unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec) {
// Check for counter every 64 bytes (length of USB packet) recv_buf.clear();
if (counter != data[i]) { for (int i = 0; i < size; i += USBPACKET_MAX_SIZE) {
if (data[i] != i / USBPACKET_MAX_SIZE) {
LOGE("CAN: MALFORMED USB RECV PACKET"); LOGE("CAN: MALFORMED USB RECV PACKET");
break; comms_healthy = false;
} return false;
counter++;
uint8_t chunk_len = ((recv - i) > USBPACKET_MAX_SIZE) ? 63 : (recv - i - 1); // as 1 is always reserved for counter
uint8_t chunk[USBPACKET_MAX_SIZE + CANPACKET_MAX_SIZE];
memcpy(chunk, tail, tail_size);
memcpy(&chunk[tail_size], &data[i+1], chunk_len);
chunk_len += tail_size;
tail_size = 0;
uint8_t pos = 0;
while (pos < chunk_len) {
uint8_t data_len = dlc_to_len[(chunk[pos] >> 4)];
uint8_t pckt_len = CANPACKET_HEAD_SIZE + data_len;
if (pckt_len <= (chunk_len - pos)) {
can_header header;
memcpy(&header, &chunk[pos], CANPACKET_HEAD_SIZE);
can_frame &canData = out_vec.emplace_back();
canData.busTime = 0;
canData.address = header.addr;
canData.src = header.bus + bus_offset;
if (header.rejected) { canData.src += CANPACKET_REJECTED; }
if (header.returned) { canData.src += CANPACKET_RETURNED; }
canData.dat.assign((char*)&chunk[pos+CANPACKET_HEAD_SIZE], data_len);
pos += pckt_len;
} else {
// Keep partial CAN packet until next USB packet
tail_size = (chunk_len - pos);
memcpy(tail, &chunk[pos], tail_size);
break;
}
} }
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;
while (pos < recv_buf.size()) {
can_header header;
memcpy(&header, &recv_buf[pos], CANPACKET_HEAD_SIZE);
can_frame &canData = out_vec.emplace_back();
canData.busTime = 0;
canData.address = header.addr;
canData.src = header.bus + bus_offset;
if (header.rejected) { canData.src += CANPACKET_REJECTED; }
if (header.returned) { canData.src += CANPACKET_RETURNED; }
const uint8_t data_len = dlc_to_len[header.data_len_code];
canData.dat.assign((char *)&recv_buf[pos + CANPACKET_HEAD_SIZE], data_len);
pos += CANPACKET_HEAD_SIZE + data_len;
} }
return true; return true;
} }

@ -3,6 +3,7 @@
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <ctime> #include <ctime>
#include <functional>
#include <list> #include <list>
#include <mutex> #include <mutex>
#include <optional> #include <optional>
@ -17,7 +18,7 @@
#define PANDA_BUS_CNT 4 #define PANDA_BUS_CNT 4
#define RECV_SIZE (0x4000U) #define RECV_SIZE (0x4000U)
#define USB_TX_SOFT_LIMIT (0x100U) #define USB_TX_SOFT_LIMIT (0x100U)
#define USBPACKET_MAX_SIZE (0x40U) #define USBPACKET_MAX_SIZE (0x40)
#define CANPACKET_HEAD_SIZE 5U #define CANPACKET_HEAD_SIZE 5U
#define CANPACKET_MAX_SIZE 72U #define CANPACKET_MAX_SIZE 72U
#define CANPACKET_REJECTED (0xC0U) #define CANPACKET_REJECTED (0xC0U)
@ -69,6 +70,7 @@ class Panda {
libusb_device_handle *dev_handle = NULL; libusb_device_handle *dev_handle = NULL;
std::mutex usb_lock; std::mutex usb_lock;
std::vector<uint8_t> send; std::vector<uint8_t> send;
std::vector<uint8_t> recv_buf;
void handle_usb_issue(int err, const char func[]); void handle_usb_issue(int err, const char func[]);
void cleanup(); void cleanup();
@ -113,4 +115,11 @@ class Panda {
uint8_t len_to_dlc(uint8_t len); uint8_t len_to_dlc(uint8_t len);
void can_send(capnp::List<cereal::CanData>::Reader can_data_list); void can_send(capnp::List<cereal::CanData>::Reader can_data_list);
bool can_receive(std::vector<can_frame>& out_vec); bool can_receive(std::vector<can_frame>& out_vec);
protected:
// for unit tests
Panda(uint32_t bus_offset) : bus_offset(bus_offset) {}
void pack_can_buffer(const capnp::List<cereal::CanData>::Reader &can_data_list,
std::function<void(uint8_t *, size_t)> write_func);
bool unpack_can_buffer(uint8_t *data, int size, std::vector<can_frame> &out_vec);
}; };

@ -0,0 +1,130 @@
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <random>
#include "catch2/catch.hpp"
#include "cereal/messaging/messaging.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) {
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
return dist(rng);
}
struct PandaTest : public Panda {
PandaTest(uint32_t bus_offset, int can_list_size, cereal::PandaState::PandaType hw_type);
void test_can_send();
void test_can_recv();
std::map<int, std::string> test_data;
int can_list_size = 0;
int total_pakets_size = 0;
MessageBuilder msg;
capnp::List<cereal::CanData>::Reader can_data_list;
};
PandaTest::PandaTest(uint32_t bus_offset_, int can_list_size, cereal::PandaState::PandaType hw_type) : can_list_size(can_list_size), Panda(bus_offset_) {
this->hw_type = hw_type;
int data_limit = ((hw_type == cereal::PandaState::PandaType::RED_PANDA) ? std::size(dlc_to_len) : 8);
// prepare test data
for (int i = 0; i < data_limit; ++i) {
std::random_device rd;
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char> rbe(rd());
int data_len = dlc_to_len[i];
std::string bytes(data_len, '\0');
std::generate(bytes.begin(), bytes.end(), std::ref(rbe));
test_data[data_len] = bytes;
}
// generate can messages for this panda
auto can_list = msg.initEvent().initSendcan(can_list_size);
for (uint8_t i = 0; i < can_list_size; ++i) {
auto can = can_list[i];
uint32_t id = random_int(0, std::size(dlc_to_len) - 1);
const std::string &dat = test_data[dlc_to_len[id]];
can.setAddress(i);
can.setSrc(random_int(0, 3) + bus_offset);
can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size()));
total_pakets_size += CANPACKET_HEAD_SIZE + dat.size();
}
can_data_list = can_list.asReader();
INFO("test " << can_list_size << " packets, total size " << total_pakets_size);
}
void PandaTest::test_can_send() {
std::vector<uint8_t> unpacked_data;
this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) {
int size_left = size;
for (int i = 0, counter = 0; i < size; i += USBPACKET_MAX_SIZE, counter++) {
REQUIRE(chunk[i] == counter);
const int len = std::min(USBPACKET_MAX_SIZE, size_left);
unpacked_data.insert(unpacked_data.end(), &chunk[i + 1], &chunk[i + len]);
size_left -= len;
}
});
REQUIRE(unpacked_data.size() == total_pakets_size);
int cnt = 0;
INFO("test can message integrity");
for (int pos = 0, pckt_len = 0; pos < unpacked_data.size(); pos += pckt_len) {
can_header header;
memcpy(&header, &unpacked_data[pos], CANPACKET_HEAD_SIZE);
const uint8_t data_len = dlc_to_len[header.data_len_code];
pckt_len = CANPACKET_HEAD_SIZE + data_len;
REQUIRE(header.addr == cnt);
REQUIRE(test_data.find(data_len) != test_data.end());
const std::string &dat = test_data[data_len];
REQUIRE(memcmp(dat.data(), &unpacked_data[pos + 5], dat.size()) == 0);
++cnt;
}
REQUIRE(cnt == can_list_size);
}
void PandaTest::test_can_recv() {
std::vector<can_frame> frames;
this->pack_can_buffer(can_data_list, [&](uint8_t *data, size_t size) {
this->unpack_can_buffer(data, size, frames);
});
REQUIRE(frames.size() == can_list_size);
for (int i = 0; i < frames.size(); ++i) {
REQUIRE(frames[i].address == i);
REQUIRE(test_data.find(frames[i].dat.size()) != test_data.end());
const std::string &dat = test_data[frames[i].dat.size()];
REQUIRE(memcmp(dat.data(), frames[i].dat.data(), dat.size()) == 0);
}
}
TEST_CASE("send/recv CAN 2.0 packets") {
auto bus_offset = GENERATE(0, 4);
auto can_list_size = GENERATE(1, 3, 5, 10, 30, 60, 100, 200);
PandaTest test(bus_offset, can_list_size, cereal::PandaState::PandaType::DOS);
SECTION("can_send") {
test.test_can_send();
}
SECTION("can_receive") {
test.test_can_recv();
}
}
TEST_CASE("send/recv CAN FD packets") {
auto bus_offset = GENERATE(0, 4);
auto can_list_size = GENERATE(1, 3, 5, 10, 30, 60, 100, 200);
PandaTest test(bus_offset, can_list_size, cereal::PandaState::PandaType::RED_PANDA);
SECTION("can_send") {
test.test_can_send();
}
SECTION("can_receive") {
test.test_can_recv();
}
}

@ -1,4 +1,4 @@
Import('env', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'USE_WEBCAM', 'USE_FRAME_STREAM') Import('env', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'USE_WEBCAM')
libs = ['m', 'pthread', common, 'jpeg', 'OpenCL', 'yuv', cereal, messaging, 'zmq', 'capnp', 'kj', visionipc, gpucommon] libs = ['m', 'pthread', common, 'jpeg', 'OpenCL', 'yuv', cereal, messaging, 'zmq', 'capnp', 'kj', visionipc, gpucommon]
@ -18,15 +18,12 @@ else:
env.Append(CFLAGS = '-DWEBCAM') env.Append(CFLAGS = '-DWEBCAM')
env.Append(CPPPATH = ['/usr/include/opencv4', '/usr/local/include/opencv4']) env.Append(CPPPATH = ['/usr/include/opencv4', '/usr/local/include/opencv4'])
else: else:
if USE_FRAME_STREAM: libs += ['avutil', 'avcodec', 'avformat', 'bz2', 'ssl', 'curl', 'crypto']
cameras = ['cameras/camera_frame_stream.cc'] # TODO: import replay_lib from root SConstruct
else: cameras = ['cameras/camera_replay.cc',
libs += ['avutil', 'avcodec', 'avformat', 'swscale', 'bz2', 'ssl', 'curl', 'crypto'] env.Object('camera-util', '#/selfdrive/ui/replay/util.cc'),
# TODO: import replay_lib from root SConstruct env.Object('camera-framereader', '#/selfdrive/ui/replay/framereader.cc'),
cameras = ['cameras/camera_replay.cc', env.Object('camera-filereader', '#/selfdrive/ui/replay/filereader.cc')]
env.Object('camera-util', '#/selfdrive/ui/replay/util.cc'),
env.Object('camera-framereader', '#/selfdrive/ui/replay/framereader.cc'),
env.Object('camera-filereader', '#/selfdrive/ui/replay/filereader.cc')]
if arch == "Darwin": if arch == "Darwin":
del libs[libs.index('OpenCL')] del libs[libs.index('OpenCL')]

@ -28,8 +28,6 @@
#include "selfdrive/camerad/cameras/camera_replay.h" #include "selfdrive/camerad/cameras/camera_replay.h"
#endif #endif
const int YUV_COUNT = 100;
class Debayer { class Debayer {
public: public:
Debayer(cl_device_id device_id, cl_context context, const CameraBuf *b, const CameraState *s) { Debayer(cl_device_id device_id, cl_context context, const CameraBuf *b, const CameraState *s) {
@ -109,7 +107,7 @@ void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s,
vipc_server->create_buffers(rgb_type, UI_BUF_COUNT, true, rgb_width, rgb_height); vipc_server->create_buffers(rgb_type, UI_BUF_COUNT, true, rgb_width, rgb_height);
rgb_stride = vipc_server->get_buffer(rgb_type)->stride; rgb_stride = vipc_server->get_buffer(rgb_type)->stride;
vipc_server->create_buffers(yuv_type, YUV_COUNT, false, rgb_width, rgb_height); vipc_server->create_buffers(yuv_type, YUV_BUFFER_COUNT, false, rgb_width, rgb_height);
if (ci->bayer) { if (ci->bayer) {
debayer = new Debayer(device_id, context, this, s); debayer = new Debayer(device_id, context, this, s);

@ -14,6 +14,7 @@
#include "selfdrive/common/queue.h" #include "selfdrive/common/queue.h"
#include "selfdrive/common/swaglog.h" #include "selfdrive/common/swaglog.h"
#include "selfdrive/common/visionimg.h" #include "selfdrive/common/visionimg.h"
#include "selfdrive/hardware/hw.h"
#define CAMERA_ID_IMX298 0 #define CAMERA_ID_IMX298 0
#define CAMERA_ID_IMX179 1 #define CAMERA_ID_IMX179 1
@ -26,7 +27,9 @@
#define CAMERA_ID_AR0231 8 #define CAMERA_ID_AR0231 8
#define CAMERA_ID_MAX 9 #define CAMERA_ID_MAX 9
#define UI_BUF_COUNT 4 const int UI_BUF_COUNT = 4;
const int YUV_BUFFER_COUNT = Hardware::EON() ? 100 : 40;
enum CameraType { enum CameraType {
RoadCam = 0, RoadCam = 0,

@ -1,92 +0,0 @@
#include "selfdrive/camerad/cameras/camera_frame_stream.h"
#include <unistd.h>
#include <cassert>
#include <capnp/dynamic.h>
#include "cereal/messaging/messaging.h"
#include "selfdrive/common/util.h"
#define FRAME_WIDTH 1164
#define FRAME_HEIGHT 874
extern ExitHandler do_exit;
namespace {
// TODO: make this more generic
CameraInfo cameras_supported[CAMERA_ID_MAX] = {
[CAMERA_ID_IMX298] = {
.frame_width = FRAME_WIDTH,
.frame_height = FRAME_HEIGHT,
.frame_stride = FRAME_WIDTH*3,
.bayer = false,
.bayer_flip = false,
},
[CAMERA_ID_OV8865] = {
.frame_width = 1632,
.frame_height = 1224,
.frame_stride = 2040, // seems right
.bayer = false,
.bayer_flip = 3,
.hdr = false
},
};
void camera_init(VisionIpcServer * v, CameraState *s, int camera_id, unsigned int fps, cl_device_id device_id, cl_context ctx, VisionStreamType rgb_type, VisionStreamType yuv_type) {
assert(camera_id < std::size(cameras_supported));
s->ci = cameras_supported[camera_id];
assert(s->ci.frame_width != 0);
s->camera_num = camera_id;
s->fps = fps;
s->buf.init(device_id, ctx, s, v, FRAME_BUF_COUNT, rgb_type, yuv_type);
}
void run_frame_stream(CameraState &camera, const char* frame_pkt) {
SubMaster sm({frame_pkt});
size_t buf_idx = 0;
while (!do_exit) {
sm.update(1000);
if(sm.updated(frame_pkt)) {
auto msg = static_cast<capnp::DynamicStruct::Reader>(sm[frame_pkt]);
auto frame = msg.get(frame_pkt).as<capnp::DynamicStruct>();
camera.buf.camera_bufs_metadata[buf_idx] = {
.frame_id = frame.get("frameId").as<uint32_t>(),
.timestamp_eof = frame.get("timestampEof").as<uint64_t>(),
.timestamp_sof = frame.get("timestampSof").as<uint64_t>(),
};
cl_command_queue q = camera.buf.camera_bufs[buf_idx].copy_q;
cl_mem yuv_cl = camera.buf.camera_bufs[buf_idx].buf_cl;
auto image = frame.get("image").as<capnp::Data>();
clEnqueueWriteBuffer(q, yuv_cl, CL_TRUE, 0, image.size(), image.begin(), 0, NULL, NULL);
camera.buf.queue(buf_idx);
buf_idx = (buf_idx + 1) % FRAME_BUF_COUNT;
}
}
}
} // namespace
void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) {
camera_init(v, &s->road_cam, CAMERA_ID_IMX298, 20, device_id, ctx,
VISION_STREAM_RGB_BACK, VISION_STREAM_YUV_BACK);
camera_init(v, &s->driver_cam, CAMERA_ID_OV8865, 10, device_id, ctx,
VISION_STREAM_RGB_FRONT, VISION_STREAM_YUV_FRONT);
}
void cameras_open(MultiCameraState *s) {}
void cameras_close(MultiCameraState *s) {}
void camera_autoexposure(CameraState *s, float grey_frac) {}
void process_road_camera(MultiCameraState *s, CameraState *c, int cnt) {}
void cameras_run(MultiCameraState *s) {
std::thread t = start_process_thread(s, &s->road_cam, process_road_camera);
set_thread_name("frame_streaming");
run_frame_stream(s->road_cam, "roadCameraState");
t.join();
}

@ -1,30 +0,0 @@
#pragma once
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
#ifdef __APPLE__
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif
#include "camera_common.h"
#define FRAME_BUF_COUNT 16
typedef struct CameraState {
int camera_num;
CameraInfo ci;
int fps;
float digital_gain;
CameraBuf buf;
} CameraState;
typedef struct MultiCameraState {
CameraState road_cam;
CameraState driver_cam;
SubMaster *sm;
PubMaster *pm;
} MultiCameraState;

@ -211,12 +211,12 @@ void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_i
/*fps*/ 20, /*fps*/ 20,
#endif #endif
device_id, ctx, device_id, ctx,
VISION_STREAM_RGB_BACK, VISION_STREAM_YUV_BACK); VISION_STREAM_RGB_BACK, VISION_STREAM_ROAD);
camera_init(v, &s->driver_cam, CAMERA_ID_OV8865, 1, camera_init(v, &s->driver_cam, CAMERA_ID_OV8865, 1,
/*pixel_clock=*/72000000, /*line_length_pclk=*/1602, /*pixel_clock=*/72000000, /*line_length_pclk=*/1602,
/*max_gain=*/510, 10, device_id, ctx, /*max_gain=*/510, 10, device_id, ctx,
VISION_STREAM_RGB_FRONT, VISION_STREAM_YUV_FRONT); VISION_STREAM_RGB_FRONT, VISION_STREAM_DRIVER);
s->sm = new SubMaster({"driverState"}); s->sm = new SubMaster({"driverState"});
s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"}); s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"});

@ -709,13 +709,13 @@ static void camera_open(CameraState *s) {
void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) { void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) {
camera_init(s, v, &s->road_cam, CAMERA_ID_AR0231, 1, 20, device_id, ctx, camera_init(s, v, &s->road_cam, CAMERA_ID_AR0231, 1, 20, device_id, ctx,
VISION_STREAM_RGB_BACK, VISION_STREAM_YUV_BACK); // swap left/right VISION_STREAM_RGB_BACK, VISION_STREAM_ROAD); // swap left/right
printf("road camera initted \n"); printf("road camera initted \n");
camera_init(s, v, &s->wide_road_cam, CAMERA_ID_AR0231, 0, 20, device_id, ctx, camera_init(s, v, &s->wide_road_cam, CAMERA_ID_AR0231, 0, 20, device_id, ctx,
VISION_STREAM_RGB_WIDE, VISION_STREAM_YUV_WIDE); VISION_STREAM_RGB_WIDE, VISION_STREAM_WIDE_ROAD);
printf("wide road camera initted \n"); printf("wide road camera initted \n");
camera_init(s, v, &s->driver_cam, CAMERA_ID_AR0231, 2, 20, device_id, ctx, camera_init(s, v, &s->driver_cam, CAMERA_ID_AR0231, 2, 20, device_id, ctx,
VISION_STREAM_RGB_FRONT, VISION_STREAM_YUV_FRONT); VISION_STREAM_RGB_FRONT, VISION_STREAM_DRIVER);
printf("driver camera initted \n"); printf("driver camera initted \n");
s->sm = new SubMaster({"driverState"}); s->sm = new SubMaster({"driverState"});

@ -23,7 +23,7 @@ std::string get_url(std::string route_name, const std::string &camera, int segme
} }
void camera_init(VisionIpcServer *v, CameraState *s, int camera_id, unsigned int fps, cl_device_id device_id, cl_context ctx, VisionStreamType rgb_type, VisionStreamType yuv_type, const std::string &url) { void camera_init(VisionIpcServer *v, CameraState *s, int camera_id, unsigned int fps, cl_device_id device_id, cl_context ctx, VisionStreamType rgb_type, VisionStreamType yuv_type, const std::string &url) {
s->frame = new FrameReader(true); s->frame = new FrameReader();
if (!s->frame->load(url)) { if (!s->frame->load(url)) {
printf("failed to load stream from %s", url.c_str()); printf("failed to load stream from %s", url.c_str());
assert(0); assert(0);
@ -98,9 +98,9 @@ void process_road_camera(MultiCameraState *s, CameraState *c, int cnt) {
void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) { void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) {
camera_init(v, &s->road_cam, CAMERA_ID_LGC920, 20, device_id, ctx, camera_init(v, &s->road_cam, CAMERA_ID_LGC920, 20, device_id, ctx,
VISION_STREAM_RGB_BACK, VISION_STREAM_YUV_BACK, get_url(road_camera_route, "fcamera", 0)); VISION_STREAM_RGB_BACK, VISION_STREAM_ROAD, get_url(road_camera_route, "fcamera", 0));
// camera_init(v, &s->driver_cam, CAMERA_ID_LGC615, 10, device_id, ctx, // camera_init(v, &s->driver_cam, CAMERA_ID_LGC615, 10, device_id, ctx,
// VISION_STREAM_RGB_FRONT, VISION_STREAM_YUV_FRONT, get_url(driver_camera_route, "dcamera", 0)); // VISION_STREAM_RGB_FRONT, VISION_STREAM_DRIVER, get_url(driver_camera_route, "dcamera", 0));
s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"}); s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"});
} }

@ -141,9 +141,9 @@ void driver_camera_thread(CameraState *s) {
void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) { void cameras_init(VisionIpcServer *v, MultiCameraState *s, cl_device_id device_id, cl_context ctx) {
camera_init(v, &s->road_cam, CAMERA_ID_LGC920, 20, device_id, ctx, camera_init(v, &s->road_cam, CAMERA_ID_LGC920, 20, device_id, ctx,
VISION_STREAM_RGB_BACK, VISION_STREAM_YUV_BACK); VISION_STREAM_RGB_BACK, VISION_STREAM_ROAD);
camera_init(v, &s->driver_cam, CAMERA_ID_LGC615, 10, device_id, ctx, camera_init(v, &s->driver_cam, CAMERA_ID_LGC615, 10, device_id, ctx,
VISION_STREAM_RGB_FRONT, VISION_STREAM_YUV_FRONT); VISION_STREAM_RGB_FRONT, VISION_STREAM_DRIVER);
s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"}); s->pm = new PubMaster({"roadCameraState", "driverCameraState", "thumbnail"});
} }

@ -20,9 +20,13 @@ if __name__ == "__main__":
mkdirs_exists_ok(out_path) mkdirs_exists_ok(out_path)
r = Route(args.route) r = Route(args.route)
lr = list(LogReader(r.qlog_paths()[args.segment])) path = r.log_paths()[args.segment] or r.qlog_paths()[args.segment]
lr = list(LogReader(path))
for msg in tqdm(lr): for msg in tqdm(lr):
if msg.which() == 'thumbnail': if msg.which() == 'thumbnail':
with open(os.path.join(out_path, f"{msg.thumbnail.frameId}.jpg"), 'wb') as f: with open(os.path.join(out_path, f"{msg.thumbnail.frameId}.jpg"), 'wb') as f:
f.write(msg.thumbnail.thumbnail) f.write(msg.thumbnail.thumbnail)
elif msg.which() == 'navThumbnail':
with open(os.path.join(out_path, f"nav_{msg.navThumbnail.frameId}.jpg"), 'wb') as f:
f.write(msg.navThumbnail.thumbnail)

@ -1,7 +1,7 @@
import os import os
from common.params import Params from common.params import Params
from common.basedir import BASEDIR from common.basedir import BASEDIR
from selfdrive.version import comma_remote, tested_branch from selfdrive.version import get_comma_remote, get_tested_branch
from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars from selfdrive.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from selfdrive.car.vin import get_vin, VIN_UNKNOWN from selfdrive.car.vin import get_vin, VIN_UNKNOWN
from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car from selfdrive.car.fw_versions import get_fw_versions, match_fw_to_car
@ -14,7 +14,7 @@ EventName = car.CarEvent.EventName
def get_startup_event(car_recognized, controller_available, fw_seen): def get_startup_event(car_recognized, controller_available, fw_seen):
if comma_remote and tested_branch: if get_comma_remote() and get_tested_branch():
event = EventName.startup event = EventName.startup
else: else:
event = EventName.startupMaster event = EventName.startupMaster

@ -31,10 +31,13 @@ class CarState(CarStateBase):
ret.espDisabled = (cp.vl["TRACTION_BUTTON"]["TRACTION_OFF"] == 1) ret.espDisabled = (cp.vl["TRACTION_BUTTON"]["TRACTION_OFF"] == 1)
ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"] ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"] cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"],
ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"] cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"],
ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"] cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"],
unit=1,
)
ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2. ret.vEgoRaw = (cp.vl["SPEED_1"]["SPEED_LEFT"] + cp.vl["SPEED_1"]["SPEED_RIGHT"]) / 2.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = not ret.vEgoRaw > 0.001 ret.standstill = not ret.vEgoRaw > 0.001

@ -10,10 +10,14 @@ WHEEL_RADIUS = 0.33
class CarState(CarStateBase): class CarState(CarStateBase):
def update(self, cp): def update(self, cp):
ret = car.CarState.new_message() ret = car.CarState.new_message()
ret.wheelSpeeds.rr = cp.vl["WheelSpeed_CG1"]["WhlRr_W_Meas"] * WHEEL_RADIUS
ret.wheelSpeeds.rl = cp.vl["WheelSpeed_CG1"]["WhlRl_W_Meas"] * WHEEL_RADIUS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["WheelSpeed_CG1"]["WhlFr_W_Meas"] * WHEEL_RADIUS cp.vl["WheelSpeed_CG1"]["WhlFl_W_Meas"],
ret.wheelSpeeds.fl = cp.vl["WheelSpeed_CG1"]["WhlFl_W_Meas"] * WHEEL_RADIUS cp.vl["WheelSpeed_CG1"]["WhlFr_W_Meas"],
cp.vl["WheelSpeed_CG1"]["WhlRl_W_Meas"],
cp.vl["WheelSpeed_CG1"]["WhlRr_W_Meas"],
unit=WHEEL_RADIUS,
)
ret.vEgoRaw = mean([ret.wheelSpeeds.rr, ret.wheelSpeeds.rl, ret.wheelSpeeds.fr, ret.wheelSpeeds.fl]) ret.vEgoRaw = mean([ret.wheelSpeeds.rr, ret.wheelSpeeds.rl, ret.wheelSpeeds.fr, ret.wheelSpeeds.fl])
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = not ret.vEgoRaw > 0.001 ret.standstill = not ret.vEgoRaw > 0.001

@ -1,6 +1,5 @@
from cereal import car from cereal import car
from common.numpy_fast import mean from common.numpy_fast import mean
from selfdrive.config import Conversions as CV
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
@ -21,10 +20,12 @@ class CarState(CarStateBase):
self.prev_cruise_buttons = self.cruise_buttons self.prev_cruise_buttons = self.cruise_buttons
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"] self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
ret.wheelSpeeds.fl = pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = pt_cp.vl["EBCMWheelSpdFront"]["FRWheelSpd"] * CV.KPH_TO_MS pt_cp.vl["EBCMWheelSpdFront"]["FLWheelSpd"],
ret.wheelSpeeds.rl = pt_cp.vl["EBCMWheelSpdRear"]["RLWheelSpd"] * CV.KPH_TO_MS pt_cp.vl["EBCMWheelSpdFront"]["FRWheelSpd"],
ret.wheelSpeeds.rr = pt_cp.vl["EBCMWheelSpdRear"]["RRWheelSpd"] * CV.KPH_TO_MS pt_cp.vl["EBCMWheelSpdRear"]["RLWheelSpd"],
pt_cp.vl["EBCMWheelSpdRear"]["RRWheelSpd"],
)
ret.vEgoRaw = mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr]) ret.vEgoRaw = mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr])
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.vEgoRaw < 0.01 ret.standstill = ret.vEgoRaw < 0.01

@ -5,7 +5,7 @@ from opendbc.can.can_define import CANDefine
from opendbc.can.parser import CANParser from opendbc.can.parser import CANParser
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.car.interfaces import CarStateBase from selfdrive.car.interfaces import CarStateBase
from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, SPEED_FACTOR, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL from selfdrive.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_ALT_BRAKE_SIGNAL
TransmissionType = car.CarParams.TransmissionType TransmissionType = car.CarParams.TransmissionType
@ -107,7 +107,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg):
else: else:
checks += [("CRUISE_PARAMS", 50)] checks += [("CRUISE_PARAMS", 50)]
if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_2021, CAR.ACCORDH, CAR.ACCORDH_2021, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E):
signals += [("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK", 1)] signals += [("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK", 1)]
elif CP.carFingerprint == CAR.ODYSSEY_CHN: elif CP.carFingerprint == CAR.ODYSSEY_CHN:
signals += [("DRIVERS_DOOR_OPEN", "SCM_BUTTONS", 1)] signals += [("DRIVERS_DOOR_OPEN", "SCM_BUTTONS", 1)]
@ -185,7 +185,7 @@ class CarState(CarStateBase):
# ******************* parse out can ******************* # ******************* parse out can *******************
# TODO: find wheels moving bit in dbc # TODO: find wheels moving bit in dbc
if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_2021, CAR.ACCORDH, CAR.ACCORDH_2021, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E):
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 0.1 ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 0.1
ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"])
elif self.CP.carFingerprint == CAR.ODYSSEY_CHN: elif self.CP.carFingerprint == CAR.ODYSSEY_CHN:
@ -213,16 +213,17 @@ class CarState(CarStateBase):
self.brake_error = cp.vl["STANDSTILL"]["BRAKE_ERROR_1"] or cp.vl["STANDSTILL"]["BRAKE_ERROR_2"] self.brake_error = cp.vl["STANDSTILL"]["BRAKE_ERROR_1"] or cp.vl["STANDSTILL"]["BRAKE_ERROR_2"]
ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0 ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0
speed_factor = SPEED_FACTOR.get(self.CP.carFingerprint, 1.) ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"] * CV.KPH_TO_MS * speed_factor cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"],
ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"] * CV.KPH_TO_MS * speed_factor cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"],
ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"] * CV.KPH_TO_MS * speed_factor cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"],
ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"] * CV.KPH_TO_MS * speed_factor cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"],
v_wheel = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr)/4. )
v_wheel = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.0
# blend in transmission speed at low speed, since it has more low speed accuracy # blend in transmission speed at low speed, since it has more low speed accuracy
v_weight = interp(v_wheel, v_weight_bp, v_weight_v) v_weight = interp(v_wheel, v_weight_bp, v_weight_v)
ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * speed_factor + v_weight * v_wheel ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"] ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
@ -235,7 +236,7 @@ class CarState(CarStateBase):
250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"]) 250, cp.vl["SCM_FEEDBACK"]["LEFT_BLINKER"], cp.vl["SCM_FEEDBACK"]["RIGHT_BLINKER"])
ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1 ret.brakeHoldActive = cp.vl["VSA_STATUS"]["BRAKE_HOLD_ACTIVE"] == 1
if self.CP.carFingerprint in (CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN, CAR.CRV_5G, CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, if self.CP.carFingerprint in (CAR.CIVIC, CAR.ODYSSEY, CAR.ODYSSEY_CHN, CAR.CRV_5G, CAR.ACCORD, CAR.ACCORD_2021, CAR.ACCORDH, CAR.ACCORDH_2021, CAR.CIVIC_BOSCH,
CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E): CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E):
self.park_brake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0 self.park_brake = cp.vl["EPB_STATUS"]["EPB_STATE"] != 0
else: else:

@ -110,7 +110,7 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 1. tire_stiffness_factor = 1.
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate in (CAR.ACCORD, CAR.ACCORDH): elif candidate in (CAR.ACCORD, CAR.ACCORD_2021, CAR.ACCORDH, CAR.ACCORDH_2021):
stop_and_go = True stop_and_go = True
ret.mass = 3279. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3279. * CV.LB_TO_KG + STD_CARGO_KG
ret.wheelbase = 2.83 ret.wheelbase = 2.83
@ -143,6 +143,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.444 tire_stiffness_factor = 0.444
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_5G: elif candidate == CAR.CRV_5G:
stop_and_go = True stop_and_go = True
@ -160,6 +161,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
tire_stiffness_factor = 0.677 tire_stiffness_factor = 0.677
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.CRV_HYBRID: elif candidate == CAR.CRV_HYBRID:
stop_and_go = True stop_and_go = True
@ -170,6 +172,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
tire_stiffness_factor = 0.677 tire_stiffness_factor = 0.677
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.FIT: elif candidate == CAR.FIT:
stop_and_go = False stop_and_go = False
@ -201,6 +204,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
tire_stiffness_factor = 0.5 tire_stiffness_factor = 0.5
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.025]] ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.16], [0.025]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.ACURA_RDX: elif candidate == CAR.ACURA_RDX:
stop_and_go = False stop_and_go = False

@ -59,6 +59,8 @@ VISUAL_HUD = {
class CAR: class CAR:
ACCORD = "HONDA ACCORD 2018" ACCORD = "HONDA ACCORD 2018"
ACCORDH = "HONDA ACCORD HYBRID 2018" ACCORDH = "HONDA ACCORD HYBRID 2018"
ACCORD_2021 = "HONDA ACCORD 2021"
ACCORDH_2021 = "HONDA ACCORD HYBRID 2021"
CIVIC = "HONDA CIVIC 2016" CIVIC = "HONDA CIVIC 2016"
CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019" CIVIC_BOSCH = "HONDA CIVIC (BOSCH) 2019"
CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019" CIVIC_BOSCH_DIESEL = "HONDA CIVIC SEDAN 1.6 DIESEL 2019"
@ -268,12 +270,10 @@ FW_VERSIONS = {
], ],
(Ecu.fwdCamera, 0x18dab5f1, None): [ (Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TWA-A070\x00\x00', b'36161-TWA-A070\x00\x00',
b'36161-TWA-A330\x00\x00',
], ],
(Ecu.fwdRadar, 0x18dab0f1, None): [ (Ecu.fwdRadar, 0x18dab0f1, None): [
b'36802-TWA-A080\x00\x00', b'36802-TWA-A080\x00\x00',
b'36802-TWA-A070\x00\x00', b'36802-TWA-A070\x00\x00',
b'36802-TWA-A330\x00\x00',
], ],
(Ecu.eps, 0x18da30f1, None): [ (Ecu.eps, 0x18da30f1, None): [
b'39990-TVA-A160\x00\x00', b'39990-TVA-A160\x00\x00',
@ -281,6 +281,78 @@ FW_VERSIONS = {
b'39990-TVA-A340\x00\x00', b'39990-TVA-A340\x00\x00',
], ],
}, },
CAR.ACCORD_2021: {
(Ecu.programmedFuelInjection, 0x18da10f1, None): [
b'37805-6B2-C520\x00\x00',
],
(Ecu.transmission, 0x18da1ef1, None): [
b'28102-6B8-A700\x00\x00',
],
(Ecu.electricBrakeBooster, 0x18da2bf1, None): [
b'46114-TVA-A320\x00\x00',
],
(Ecu.gateway, 0x18daeff1, None): [
b'38897-TVA-A020\x00\x00',
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-TVA-E520\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-TVA-L420\x00\x00',
],
(Ecu.combinationMeter, 0x18da60f1, None): [
b'78109-TVC-A230\x00\x00',
],
(Ecu.hud, 0x18da61f1, None): [
b'78209-TVA-A110\x00\x00',
],
(Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TVC-A910\x00\x00',
],
(Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TVC-A330\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36802-TVC-A330\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TVA-A340\x00\x00',
],
(Ecu.unknown, 0x18da3af1, None): [
b'39390-TVA-A120\x00\x00',
],
},
CAR.ACCORDH_2021: {
(Ecu.gateway, 0x18daeff1, None): [
b'38897-TWD-J020\x00\x00',
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-TWA-A530\x00\x00',
b'57114-TWA-B520\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-TWA-L420\x00\x00',
],
(Ecu.combinationMeter, 0x18da60f1, None): [
b'78109-TWA-A030\x00\x00',
b'78109-TWA-A230\x00\x00',
],
(Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TWA-A910\x00\x00',
],
(Ecu.fwdCamera, 0x18dab5f1, None): [
b'36161-TWA-A330\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36802-TWA-A330\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TVA-A340\x00\x00',
],
(Ecu.unknown, 0x18da3af1, None): [
b'39390-TVA-A120\x00\x00',
],
},
CAR.CIVIC: { CAR.CIVIC: {
(Ecu.programmedFuelInjection, 0x18da10f1, None): [ (Ecu.programmedFuelInjection, 0x18da10f1, None): [
b'37805-5AA-A640\x00\x00', b'37805-5AA-A640\x00\x00',
@ -1324,7 +1396,9 @@ FW_VERSIONS = {
DBC = { DBC = {
CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None), CAR.ACCORD: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACCORD_2021: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACCORDH: dbc_dict('honda_accord_2018_can_generated', None), CAR.ACCORDH: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACCORDH_2021: dbc_dict('honda_accord_2018_can_generated', None),
CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.ACURA_ILX: dbc_dict('acura_ilx_2016_can_generated', 'acura_ilx_2016_nidec'),
CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'), CAR.ACURA_RDX: dbc_dict('acura_rdx_2018_can_generated', 'acura_ilx_2016_nidec'),
CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None), CAR.ACURA_RDX_3G: dbc_dict('acura_rdx_2020_can_generated', None),
@ -1354,18 +1428,9 @@ STEER_THRESHOLD = {
CAR.CRV_EU: 400, CAR.CRV_EU: 400,
} }
# TODO: is this real?
SPEED_FACTOR = {
# default is 1, overrides go here
CAR.CRV: 1.025,
CAR.CRV_5G: 1.025,
CAR.CRV_EU: 1.025,
CAR.CRV_HYBRID: 1.025,
CAR.HRV: 1.025,
}
HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY]) HONDA_NIDEC_ALT_PCM_ACCEL = set([CAR.ODYSSEY])
HONDA_NIDEC_ALT_SCM_MESSAGES = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, HONDA_NIDEC_ALT_SCM_MESSAGES = set([CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN,
CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE]) CAR.PILOT, CAR.PILOT_2019, CAR.PASSPORT, CAR.RIDGELINE])
HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E]) HONDA_BOSCH = set([CAR.ACCORD, CAR.ACCORD_2021, CAR.ACCORDH, CAR.ACCORDH_2021, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G,
HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G]) CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E])
HONDA_BOSCH_ALT_BRAKE_SIGNAL = set([CAR.ACCORD, CAR.ACCORD_2021, CAR.CRV_5G, CAR.ACURA_RDX_3G])

@ -28,10 +28,12 @@ class CarState(CarStateBase):
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0 ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
ret.wheelSpeeds.fl = cp.vl["WHL_SPD11"]["WHL_SPD_FL"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["WHL_SPD11"]["WHL_SPD_FR"] * CV.KPH_TO_MS cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
ret.wheelSpeeds.rl = cp.vl["WHL_SPD11"]["WHL_SPD_RL"] * CV.KPH_TO_MS cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
ret.wheelSpeeds.rr = cp.vl["WHL_SPD11"]["WHL_SPD_RR"] * CV.KPH_TO_MS cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

@ -78,6 +78,7 @@ class CarInterfaceBase():
ret.steerMaxBP = [0.] ret.steerMaxBP = [0.]
ret.steerMaxV = [1.] ret.steerMaxV = [1.]
ret.minSteerSpeed = 0. ret.minSteerSpeed = 0.
ret.wheelSpeedFactor = 1.0
ret.pcmCruise = True # openpilot's state is tied to the PCM's cruise state on most cars ret.pcmCruise = True # openpilot's state is tied to the PCM's cruise state on most cars
ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this ret.minEnableSpeed = -1. # enable is done by stock ACC, so ignore this
@ -210,6 +211,16 @@ class CarStateBase:
v_ego_x = self.v_ego_kf.update(v_ego_raw) v_ego_x = self.v_ego_kf.update(v_ego_raw)
return float(v_ego_x[0]), float(v_ego_x[1]) return float(v_ego_x[0]), float(v_ego_x[1])
def get_wheel_speeds(self, fl, fr, rl, rr, unit=CV.KPH_TO_MS):
factor = unit * self.CP.wheelSpeedFactor
wheelSpeeds = car.CarState.WheelSpeeds.new_message()
wheelSpeeds.fl = fl * factor
wheelSpeeds.fr = fr * factor
wheelSpeeds.rl = rl * factor
wheelSpeeds.rr = rr * factor
return wheelSpeeds
def update_blinker_from_lamp(self, blinker_time: int, left_blinker_lamp: bool, right_blinker_lamp: bool): def update_blinker_from_lamp(self, blinker_time: int, left_blinker_lamp: bool, right_blinker_lamp: bool):
"""Update blinkers from lights. Enable output when light was seen within the last `blinker_time` """Update blinkers from lights. Enable output when light was seen within the last `blinker_time`
iterations""" iterations"""

@ -20,10 +20,12 @@ class CarState(CarStateBase):
def update(self, cp, cp_cam): def update(self, cp, cp_cam):
ret = car.CarState.new_message() ret = car.CarState.new_message()
ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]["FL"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS"]["FR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["FL"],
ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS"]["RL"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["FR"],
ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS"]["RR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["RL"],
cp.vl["WHEEL_SPEEDS"]["RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

@ -21,7 +21,7 @@ class CAR:
CX9 = "MAZDA CX-9" CX9 = "MAZDA CX-9"
MAZDA3 = "MAZDA 3" MAZDA3 = "MAZDA 3"
MAZDA6 = "MAZDA 6" MAZDA6 = "MAZDA 6"
CX9_2021 = "Mazda CX-9 2021" # No Steer Lockout CX9_2021 = "MAZDA CX-9 2021" # No Steer Lockout
class LKAS_LIMITS: class LKAS_LIMITS:
STEER_THRESHOLD = 15 STEER_THRESHOLD = 15

@ -35,11 +35,12 @@ class CarState(CarStateBase):
elif self.CP.carFingerprint in [CAR.LEAF, CAR.LEAF_IC]: elif self.CP.carFingerprint in [CAR.LEAF, CAR.LEAF_IC]:
ret.brakePressed = bool(cp.vl["CRUISE_THROTTLE"]["USER_BRAKE_PRESSED"]) ret.brakePressed = bool(cp.vl["CRUISE_THROTTLE"]["USER_BRAKE_PRESSED"])
ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FL"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FL"],
ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RL"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS_FRONT"]["WHEEL_SPEED_FR"],
ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RL"],
cp.vl["WHEEL_SPEEDS_REAR"]["WHEEL_SPEED_RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

@ -23,10 +23,12 @@ class CarState(CarStateBase):
else: else:
ret.brakePressed = cp.vl["Brake_Status"]["Brake"] == 1 ret.brakePressed = cp.vl["Brake_Status"]["Brake"] == 1
ret.wheelSpeeds.fl = cp.vl["Wheel_Speeds"]["FL"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["Wheel_Speeds"]["FR"] * CV.KPH_TO_MS cp.vl["Wheel_Speeds"]["FL"],
ret.wheelSpeeds.rl = cp.vl["Wheel_Speeds"]["RL"] * CV.KPH_TO_MS cp.vl["Wheel_Speeds"]["FR"],
ret.wheelSpeeds.rr = cp.vl["Wheel_Speeds"]["RR"] * CV.KPH_TO_MS cp.vl["Wheel_Speeds"]["RL"],
cp.vl["Wheel_Speeds"]["RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4. ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
# Kalman filter, even though Subaru raw wheel speed is heaviliy filtered by default # Kalman filter, even though Subaru raw wheel speed is heaviliy filtered by default
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

@ -5,35 +5,18 @@ from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_comma
create_accel_command, create_acc_cancel_command, \ create_accel_command, create_acc_cancel_command, \
create_fcw_command, create_lta_steer_command create_fcw_command, create_lta_steer_command
from selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \ from selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \
MIN_ACC_SPEED, PEDAL_HYST_GAP, PEDAL_SCALE, CarControllerParams MIN_ACC_SPEED, PEDAL_TRANSITION, CarControllerParams
from opendbc.can.packer import CANPacker from opendbc.can.packer import CANPacker
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
def accel_hysteresis(accel, accel_steady, enabled):
# for small accel oscillations within ACCEL_HYST_GAP, don't change the accel command
if not enabled:
# send 0 when disabled, otherwise acc faults
accel_steady = 0.
elif accel > accel_steady + CarControllerParams.ACCEL_HYST_GAP:
accel_steady = accel - CarControllerParams.ACCEL_HYST_GAP
elif accel < accel_steady - CarControllerParams.ACCEL_HYST_GAP:
accel_steady = accel + CarControllerParams.ACCEL_HYST_GAP
accel = accel_steady
return accel, accel_steady
class CarController(): class CarController():
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.last_steer = 0 self.last_steer = 0
self.accel_steady = 0.
self.alert_active = False self.alert_active = False
self.last_standstill = False self.last_standstill = False
self.standstill_req = False self.standstill_req = False
self.steer_rate_limited = False self.steer_rate_limited = False
self.use_interceptor = False
self.packer = CANPacker(dbc_name) self.packer = CANPacker(dbc_name)
@ -43,25 +26,22 @@ class CarController():
# *** compute control surfaces *** # *** compute control surfaces ***
# gas and brake # gas and brake
interceptor_gas_cmd = 0. if CS.CP.enableGasInterceptor and enabled:
pcm_accel_cmd = actuators.accel MAX_INTERCEPTOR_GAS = 0.5
# RAV4 has very sensitive has pedal
if CS.CP.enableGasInterceptor: if CS.CP.carFingerprint in [CAR.RAV4, CAR.RAV4H, CAR.HIGHLANDER, CAR.HIGHLANDERH]:
# handle hysteresis when around the minimum acc speed PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0])
if CS.out.vEgo < MIN_ACC_SPEED: elif CS.CP.carFingerprint in [CAR.COROLLA]:
self.use_interceptor = True PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0])
elif CS.out.vEgo > MIN_ACC_SPEED + PEDAL_HYST_GAP: else:
self.use_interceptor = False PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0])
# offset for creep and windbrake
if self.use_interceptor and active: pedal_offset = interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-.4, 0.0, 0.2])
# only send negative accel when using interceptor. gas handles acceleration pedal_command = PEDAL_SCALE * (actuators.accel + pedal_offset)
# +0.18 m/s^2 offset to reduce ABS pump usage when OP is engaged interceptor_gas_cmd = clip(pedal_command, 0., MAX_INTERCEPTOR_GAS)
MAX_INTERCEPTOR_GAS = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED], [0.2, 0.5]) else:
interceptor_gas_cmd = clip(actuators.accel / PEDAL_SCALE, 0., MAX_INTERCEPTOR_GAS) interceptor_gas_cmd = 0.
pcm_accel_cmd = 0.18 - max(0, -actuators.accel) pcm_accel_cmd = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
pcm_accel_cmd, self.accel_steady = accel_hysteresis(pcm_accel_cmd, self.accel_steady, enabled)
pcm_accel_cmd = clip(pcm_accel_cmd, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
# steer torque # steer torque
new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX)) new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
@ -88,7 +68,6 @@ class CarController():
self.standstill_req = False self.standstill_req = False
self.last_steer = apply_steer self.last_steer = apply_steer
self.last_accel = pcm_accel_cmd
self.last_standstill = CS.out.standstill self.last_standstill = CS.out.standstill
can_sends = [] can_sends = []
@ -113,7 +92,7 @@ class CarController():
lead = lead or CS.out.vEgo < 12. # at low speed we always assume the lead is present do ACC can be engaged lead = lead or CS.out.vEgo < 12. # at low speed we always assume the lead is present do ACC can be engaged
# Lexus IS uses a different cancellation message # Lexus IS uses a different cancellation message
if pcm_cancel_cmd and CS.CP.carFingerprint == CAR.LEXUS_IS: if pcm_cancel_cmd and CS.CP.carFingerprint in [CAR.LEXUS_IS, CAR.LEXUS_RC]:
can_sends.append(create_acc_cancel_command(self.packer)) can_sends.append(create_acc_cancel_command(self.packer))
elif CS.CP.openpilotLongitudinalControl: elif CS.CP.openpilotLongitudinalControl:
can_sends.append(create_accel_command(self.packer, pcm_accel_cmd, pcm_cancel_cmd, self.standstill_req, lead, CS.acc_type)) can_sends.append(create_accel_command(self.packer, pcm_accel_cmd, pcm_cancel_cmd, self.standstill_req, lead, CS.acc_type))

@ -41,10 +41,12 @@ class CarState(CarStateBase):
ret.gas = cp.vl["GAS_PEDAL"]["GAS_PEDAL"] ret.gas = cp.vl["GAS_PEDAL"]["GAS_PEDAL"]
ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0 ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0
ret.wheelSpeeds.fl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FL"],
ret.wheelSpeeds.rl = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_FR"],
ret.wheelSpeeds.rr = cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"] * CV.KPH_TO_MS cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RL"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_RR"],
)
ret.vEgoRaw = mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr]) ret.vEgoRaw = mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr])
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
@ -79,7 +81,7 @@ class CarState(CarStateBase):
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
ret.steerWarning = cp.vl["EPS_STATUS"]["LKA_STATE"] not in [1, 5] ret.steerWarning = cp.vl["EPS_STATUS"]["LKA_STATE"] not in [1, 5]
if self.CP.carFingerprint == CAR.LEXUS_IS: if self.CP.carFingerprint in [CAR.LEXUS_IS, CAR.LEXUS_RC]:
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
else: else:
@ -93,7 +95,7 @@ class CarState(CarStateBase):
# these cars are identified by an ACC_TYPE value of 2. # these cars are identified by an ACC_TYPE value of 2.
# TODO: it is possible to avoid the lockout and gain stop and go if you # TODO: it is possible to avoid the lockout and gain stop and go if you
# send your own ACC_CONTROL msg on startup with ACC_TYPE set to 1 # send your own ACC_CONTROL msg on startup with ACC_TYPE set to 1
if (self.CP.carFingerprint not in TSS2_CAR and self.CP.carFingerprint != CAR.LEXUS_IS) or \ if (self.CP.carFingerprint not in TSS2_CAR and self.CP.carFingerprint not in [CAR.LEXUS_IS, CAR.LEXUS_RC]) or \
(self.CP.carFingerprint in TSS2_CAR and self.acc_type == 1): (self.CP.carFingerprint in TSS2_CAR and self.acc_type == 1):
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
@ -168,7 +170,7 @@ class CarState(CarStateBase):
("STEER_TORQUE_SENSOR", 50), ("STEER_TORQUE_SENSOR", 50),
] ]
if CP.carFingerprint == CAR.LEXUS_IS: if CP.carFingerprint in [CAR.LEXUS_IS, CAR.LEXUS_RC]:
signals.append(("MAIN_ON", "DSU_CRUISE", 0)) signals.append(("MAIN_ON", "DSU_CRUISE", 0))
signals.append(("SET_SPEED", "DSU_CRUISE", 0)) signals.append(("SET_SPEED", "DSU_CRUISE", 0))
checks.append(("DSU_CRUISE", 5)) checks.append(("DSU_CRUISE", 5))

@ -65,7 +65,6 @@ class CarInterface(CarInterfaceBase):
ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_B) set_lat_tune(ret.lateralTuning, LatTunes.PID_B)
elif candidate == CAR.LEXUS_RXH: elif candidate == CAR.LEXUS_RXH:
stop_and_go = True stop_and_go = True
ret.wheelbase = 2.79 ret.wheelbase = 2.79
@ -81,6 +80,7 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.5533 # not optimized yet tire_stiffness_factor = 0.5533 # not optimized yet
ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 4387. * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_D) set_lat_tune(ret.lateralTuning, LatTunes.PID_D)
ret.wheelSpeedFactor = 1.035
elif candidate == CAR.LEXUS_RXH_TSS2: elif candidate == CAR.LEXUS_RXH_TSS2:
stop_and_go = True stop_and_go = True
@ -89,6 +89,7 @@ class CarInterface(CarInterfaceBase):
tire_stiffness_factor = 0.444 # not optimized yet tire_stiffness_factor = 0.444 # not optimized yet
ret.mass = 4481.0 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max ret.mass = 4481.0 * CV.LB_TO_KG + STD_CARGO_KG # mean between min and max
set_lat_tune(ret.lateralTuning, LatTunes.PID_E) set_lat_tune(ret.lateralTuning, LatTunes.PID_E)
ret.wheelSpeedFactor = 1.035
elif candidate in [CAR.CHR, CAR.CHRH]: elif candidate in [CAR.CHR, CAR.CHRH]:
stop_and_go = True stop_and_go = True
@ -186,6 +187,15 @@ class CarInterface(CarInterfaceBase):
ret.mass = 3736.8 * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3736.8 * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_L) set_lat_tune(ret.lateralTuning, LatTunes.PID_L)
elif candidate == CAR.LEXUS_RC:
ret.safetyConfigs[0].safetyParam = 77
stop_and_go = False
ret.wheelbase = 2.73050
ret.steerRatio = 13.3
tire_stiffness_factor = 0.444
ret.mass = 3736.8 * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_L)
elif candidate == CAR.LEXUS_CTH: elif candidate == CAR.LEXUS_CTH:
ret.safetyConfigs[0].safetyParam = 100 ret.safetyConfigs[0].safetyParam = 100
stop_and_go = True stop_and_go = True
@ -206,7 +216,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.PRIUS_TSS2: elif candidate == CAR.PRIUS_TSS2:
stop_and_go = True stop_and_go = True
ret.wheelbase = 2.70002 # from toyota online sepc. ret.wheelbase = 2.70002 # from toyota online sepc.
ret.steerRatio = 13.4 # True steerRation from older prius ret.steerRatio = 13.4 # True steerRatio from older prius
tire_stiffness_factor = 0.6371 # hand-tune tire_stiffness_factor = 0.6371 # hand-tune
ret.mass = 3115. * CV.LB_TO_KG + STD_CARGO_KG ret.mass = 3115. * CV.LB_TO_KG + STD_CARGO_KG
set_lat_tune(ret.lateralTuning, LatTunes.PID_N) set_lat_tune(ret.lateralTuning, LatTunes.PID_N)
@ -259,7 +269,8 @@ class CarInterface(CarInterfaceBase):
if ret.enableGasInterceptor: if ret.enableGasInterceptor:
set_long_tune(ret.longitudinalTuning, LongTunes.PEDAL) set_long_tune(ret.longitudinalTuning, LongTunes.PEDAL)
elif candidate in [CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.RAV4_TSS2, CAR.RAV4H_TSS2, CAR.LEXUS_NX_TSS2]: elif candidate in [CAR.COROLLA_TSS2, CAR.COROLLAH_TSS2, CAR.RAV4_TSS2, CAR.RAV4H_TSS2, CAR.LEXUS_NX_TSS2,
CAR.HIGHLANDER_TSS2, CAR.HIGHLANDERH_TSS2]:
set_long_tune(ret.longitudinalTuning, LongTunes.TSS2) set_long_tune(ret.longitudinalTuning, LongTunes.TSS2)
ret.stoppingDecelRate = 0.3 # reach stopping target smoothly ret.stoppingDecelRate = 0.3 # reach stopping target smoothly
ret.startingAccelRate = 6.0 # release brakes fast ret.startingAccelRate = 6.0 # release brakes fast

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from enum import Enum from enum import Enum
from selfdrive.car.toyota.values import MIN_ACC_SPEED, PEDAL_HYST_GAP
class LongTunes(Enum): class LongTunes(Enum):
@ -29,15 +28,8 @@ class LatTunes(Enum):
###### LONG ###### ###### LONG ######
def set_long_tune(tune, name): def set_long_tune(tune, name):
if name == LongTunes.PEDAL:
tune.deadzoneBP = [0.]
tune.deadzoneV = [0.]
tune.kpBP = [0., 5., MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_HYST_GAP, 35.]
tune.kpV = [1.2, 0.8, 0.765, 2.255, 1.5]
tune.kiBP = [0., MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_HYST_GAP, 35.]
tune.kiV = [0.18, 0.165, 0.489, 0.36]
# Improved longitudinal tune # Improved longitudinal tune
elif name == LongTunes.TSS2: if name == LongTunes.TSS2 or name == LongTunes.PEDAL:
tune.deadzoneBP = [0., 8.05] tune.deadzoneBP = [0., 8.05]
tune.deadzoneV = [.0, .14] tune.deadzoneV = [.0, .14]
tune.kpBP = [0., 5., 20.] tune.kpBP = [0., 5., 20.]

@ -6,9 +6,8 @@ from selfdrive.config import Conversions as CV
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu
MIN_ACC_SPEED = 19. * CV.MPH_TO_MS MIN_ACC_SPEED = 19. * CV.MPH_TO_MS
PEDAL_TRANSITION = 10. * CV.MPH_TO_MS
PEDAL_HYST_GAP = 3. * CV.MPH_TO_MS
PEDAL_SCALE = 3.0
class CarControllerParams: class CarControllerParams:
ACCEL_HYST_GAP = 0.06 # don't change accel command for small oscilalitons within this value ACCEL_HYST_GAP = 0.06 # don't change accel command for small oscilalitons within this value
@ -58,6 +57,7 @@ class CAR:
LEXUS_NX = "LEXUS NX 2018" LEXUS_NX = "LEXUS NX 2018"
LEXUS_NXH = "LEXUS NX HYBRID 2018" LEXUS_NXH = "LEXUS NX HYBRID 2018"
LEXUS_NX_TSS2 = "LEXUS NX 2020" LEXUS_NX_TSS2 = "LEXUS NX 2020"
LEXUS_RC = "LEXUS RC 2020"
LEXUS_RX = "LEXUS RX 2016" LEXUS_RX = "LEXUS RX 2016"
LEXUS_RXH = "LEXUS RX HYBRID 2017" LEXUS_RXH = "LEXUS RX HYBRID 2017"
LEXUS_RX_TSS2 = "LEXUS RX 2020" LEXUS_RX_TSS2 = "LEXUS RX 2020"
@ -442,6 +442,7 @@ FW_VERSIONS = {
b'\x0189663F438000\x00\x00\x00\x00', b'\x0189663F438000\x00\x00\x00\x00',
], ],
(Ecu.esp, 0x7b0, None): [ (Ecu.esp, 0x7b0, None): [
b'F152610012\x00\x00\x00\x00\x00\x00',
b'F152610013\x00\x00\x00\x00\x00\x00', b'F152610013\x00\x00\x00\x00\x00\x00',
b'F152610014\x00\x00\x00\x00\x00\x00', b'F152610014\x00\x00\x00\x00\x00\x00',
b'F152610040\x00\x00\x00\x00\x00\x00', b'F152610040\x00\x00\x00\x00\x00\x00',
@ -455,6 +456,7 @@ FW_VERSIONS = {
b'8821FF402400 ', b'8821FF402400 ',
b'8821FF404000 ', b'8821FF404000 ',
b'8821FF404100 ', b'8821FF404100 ',
b'8821FF405000 ',
b'8821FF406000 ', b'8821FF406000 ',
b'8821FF407100 ', b'8821FF407100 ',
], ],
@ -470,10 +472,12 @@ FW_VERSIONS = {
b'8821FF402400 ', b'8821FF402400 ',
b'8821FF404000 ', b'8821FF404000 ',
b'8821FF404100 ', b'8821FF404100 ',
b'8821FF405000 ',
b'8821FF406000 ', b'8821FF406000 ',
b'8821FF407100 ', b'8821FF407100 ',
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'8646FF401700 ',
b'8646FF402100 ', b'8646FF402100 ',
b'8646FF404000 ', b'8646FF404000 ',
b'8646FF406000 ', b'8646FF406000 ',
@ -804,10 +808,10 @@ FW_VERSIONS = {
b'\x018966353M7100\x00\x00\x00\x00', b'\x018966353M7100\x00\x00\x00\x00',
b'\x018966353Q2000\x00\x00\x00\x00', b'\x018966353Q2000\x00\x00\x00\x00',
b'\x018966353Q2300\x00\x00\x00\x00', b'\x018966353Q2300\x00\x00\x00\x00',
b'\x018966353Q4000\x00\x00\x00\x00',
b'\x018966353R1100\x00\x00\x00\x00', b'\x018966353R1100\x00\x00\x00\x00',
b'\x018966353R7100\x00\x00\x00\x00', b'\x018966353R7100\x00\x00\x00\x00',
b'\x018966353R8100\x00\x00\x00\x00', b'\x018966353R8100\x00\x00\x00\x00',
b'\x018966353Q4000\x00\x00\x00\x00',
], ],
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\x0232480000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', b'\x0232480000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
@ -1204,15 +1208,18 @@ FW_VERSIONS = {
b'\x018966333T5000\x00\x00\x00\x00', b'\x018966333T5000\x00\x00\x00\x00',
b'\x018966333T5100\x00\x00\x00\x00', b'\x018966333T5100\x00\x00\x00\x00',
b'\x018966333X6000\x00\x00\x00\x00', b'\x018966333X6000\x00\x00\x00\x00',
b'\x01896633T07000\x00\x00\x00\x00',
], ],
(Ecu.esp, 0x7b0, None): [ (Ecu.esp, 0x7b0, None): [
b'\x01F152606281\x00\x00\x00\x00\x00\x00', b'\x01F152606281\x00\x00\x00\x00\x00\x00',
b'\x01F152606340\x00\x00\x00\x00\x00\x00', b'\x01F152606340\x00\x00\x00\x00\x00\x00',
b'\x01F152606461\x00\x00\x00\x00\x00\x00',
b'\x01F15260E031\x00\x00\x00\x00\x00\x00', b'\x01F15260E031\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
b'8965B33252\x00\x00\x00\x00\x00\x00', b'8965B33252\x00\x00\x00\x00\x00\x00',
b'8965B33590\x00\x00\x00\x00\x00\x00', b'8965B33590\x00\x00\x00\x00\x00\x00',
b'8965B33690\x00\x00\x00\x00\x00\x00',
b'8965B48271\x00\x00\x00\x00\x00\x00', b'8965B48271\x00\x00\x00\x00\x00\x00',
], ],
(Ecu.fwdRadar, 0x750, 0xf): [ (Ecu.fwdRadar, 0x750, 0xf): [
@ -1224,6 +1231,7 @@ FW_VERSIONS = {
b'\x028646F33030D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F33030D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F3303200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F3303200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F3304100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F3304100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00',
b'\x028646F3304300\x00\x00\x00\x008646G2601500\x00\x00\x00\x00',
b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F4810200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00',
], ],
}, },
@ -1288,6 +1296,7 @@ FW_VERSIONS = {
b'\x01896637851000\x00\x00\x00\x00', b'\x01896637851000\x00\x00\x00\x00',
b'\x01896637852000\x00\x00\x00\x00', b'\x01896637852000\x00\x00\x00\x00',
b'\x01896637854000\x00\x00\x00\x00', b'\x01896637854000\x00\x00\x00\x00',
b'\x01896637878000\x00\x00\x00\x00',
], ],
(Ecu.esp, 0x7b0, None): [ (Ecu.esp, 0x7b0, None): [
b'F152678130\x00\x00\x00\x00\x00\x00', b'F152678130\x00\x00\x00\x00\x00\x00',
@ -1295,6 +1304,7 @@ FW_VERSIONS = {
], ],
(Ecu.dsu, 0x791, None): [ (Ecu.dsu, 0x791, None): [
b'881517803100\x00\x00\x00\x00', b'881517803100\x00\x00\x00\x00',
b'881517803300\x00\x00\x00\x00',
], ],
(Ecu.eps, 0x7a1, None): [ (Ecu.eps, 0x7a1, None): [
b'8965B78060\x00\x00\x00\x00\x00\x00', b'8965B78060\x00\x00\x00\x00\x00\x00',
@ -1306,6 +1316,7 @@ FW_VERSIONS = {
], ],
(Ecu.fwdCamera, 0x750, 0x6d): [ (Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F7801100\x00\x00\x00\x00', b'8646F7801100\x00\x00\x00\x00',
b'8646F7801300\x00\x00\x00\x00',
], ],
}, },
CAR.LEXUS_NX_TSS2: { CAR.LEXUS_NX_TSS2: {
@ -1358,6 +1369,26 @@ FW_VERSIONS = {
b'8646F7801100\x00\x00\x00\x00', b'8646F7801100\x00\x00\x00\x00',
], ],
}, },
CAR.LEXUS_RC: {
(Ecu.engine, 0x7e0, None): [
b'\x0232484000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.esp, 0x7b0, None): [
b'F152624221\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'881512409100\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B24081\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'8821F4702300\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F2402200\x00\x00\x00\x00',
],
},
CAR.LEXUS_RX: { CAR.LEXUS_RX: {
(Ecu.engine, 0x700, None): [ (Ecu.engine, 0x700, None): [
b'\x01896630E36200\x00\x00\x00\x00', b'\x01896630E36200\x00\x00\x00\x00',
@ -1558,6 +1589,7 @@ DBC = {
CAR.RAV4: dbc_dict('toyota_rav4_2017_pt_generated', 'toyota_adas'), CAR.RAV4: dbc_dict('toyota_rav4_2017_pt_generated', 'toyota_adas'),
CAR.PRIUS: dbc_dict('toyota_prius_2017_pt_generated', 'toyota_adas'), CAR.PRIUS: dbc_dict('toyota_prius_2017_pt_generated', 'toyota_adas'),
CAR.COROLLA: dbc_dict('toyota_corolla_2017_pt_generated', 'toyota_adas'), CAR.COROLLA: dbc_dict('toyota_corolla_2017_pt_generated', 'toyota_adas'),
CAR.LEXUS_RC: dbc_dict('lexus_is_2018_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX: dbc_dict('lexus_rx_350_2016_pt_generated', 'toyota_adas'), CAR.LEXUS_RX: dbc_dict('lexus_rx_350_2016_pt_generated', 'toyota_adas'),
CAR.LEXUS_RXH: dbc_dict('lexus_rx_hybrid_2017_pt_generated', 'toyota_adas'), CAR.LEXUS_RXH: dbc_dict('lexus_rx_hybrid_2017_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),

@ -20,10 +20,12 @@ class CarState(CarStateBase):
def update(self, pt_cp, cam_cp, ext_cp, trans_type): def update(self, pt_cp, cam_cp, ext_cp, trans_type):
ret = car.CarState.new_message() ret = car.CarState.new_message()
# Update vehicle speed and acceleration from ABS wheel speeds. # Update vehicle speed and acceleration from ABS wheel speeds.
ret.wheelSpeeds.fl = pt_cp.vl["ESP_19"]["ESP_VL_Radgeschw_02"] * CV.KPH_TO_MS ret.wheelSpeeds = self.get_wheel_speeds(
ret.wheelSpeeds.fr = pt_cp.vl["ESP_19"]["ESP_VR_Radgeschw_02"] * CV.KPH_TO_MS pt_cp.vl["ESP_19"]["ESP_VL_Radgeschw_02"],
ret.wheelSpeeds.rl = pt_cp.vl["ESP_19"]["ESP_HL_Radgeschw_02"] * CV.KPH_TO_MS pt_cp.vl["ESP_19"]["ESP_VR_Radgeschw_02"],
ret.wheelSpeeds.rr = pt_cp.vl["ESP_19"]["ESP_HR_Radgeschw_02"] * CV.KPH_TO_MS pt_cp.vl["ESP_19"]["ESP_HL_Radgeschw_02"],
pt_cp.vl["ESP_19"]["ESP_HR_Radgeschw_02"],
)
ret.vEgoRaw = float(np.mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr])) ret.vEgoRaw = float(np.mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr]))
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw) ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)

@ -152,6 +152,7 @@ FW_VERSIONS = {
(Ecu.engine, 0x7e0, None): [ (Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906016A \xf1\x897697', b'\xf1\x8704E906016A \xf1\x897697',
b'\xf1\x8704E906016AD\xf1\x895758', b'\xf1\x8704E906016AD\xf1\x895758',
b'\xf1\x8704E906016CE\xf1\x899096',
b'\xf1\x8704E906023AG\xf1\x891726', b'\xf1\x8704E906023AG\xf1\x891726',
b'\xf1\x8704E906023BN\xf1\x894518', b'\xf1\x8704E906023BN\xf1\x894518',
b'\xf1\x8704E906024K \xf1\x896811', b'\xf1\x8704E906024K \xf1\x896811',
@ -192,6 +193,7 @@ FW_VERSIONS = {
b'\xf1\x870CW300042F \xf1\x891604', b'\xf1\x870CW300042F \xf1\x891604',
b'\xf1\x870CW300043B \xf1\x891601', b'\xf1\x870CW300043B \xf1\x891601',
b'\xf1\x870CW300044S \xf1\x894530', b'\xf1\x870CW300044S \xf1\x894530',
b'\xf1\x870CW300044T \xf1\x895245',
b'\xf1\x870CW300045 \xf1\x894531', b'\xf1\x870CW300045 \xf1\x894531',
b'\xf1\x870CW300047D \xf1\x895261', b'\xf1\x870CW300047D \xf1\x895261',
b'\xf1\x870CW300048J \xf1\x890611', b'\xf1\x870CW300048J \xf1\x890611',
@ -274,6 +276,7 @@ FW_VERSIONS = {
], ],
(Ecu.fwdRadar, 0x757, None): [ (Ecu.fwdRadar, 0x757, None): [
b'\xf1\x875Q0907567G \xf1\x890390\xf1\x82\00101', b'\xf1\x875Q0907567G \xf1\x890390\xf1\x82\00101',
b'\xf1\x875Q0907567J \xf1\x890396\xf1\x82\x0101',
b'\xf1\x875Q0907572A \xf1\x890141\xf1\x82\00101', b'\xf1\x875Q0907572A \xf1\x890141\xf1\x82\00101',
b'\xf1\x875Q0907572B \xf1\x890200\xf1\x82\00101', b'\xf1\x875Q0907572B \xf1\x890200\xf1\x82\00101',
b'\xf1\x875Q0907572C \xf1\x890210\xf1\x82\00101', b'\xf1\x875Q0907572C \xf1\x890210\xf1\x82\00101',

@ -130,11 +130,13 @@ std::unordered_map<std::string, uint32_t> keys = {
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START}, {"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
{"LastGPSPosition", PERSISTENT}, {"LastGPSPosition", PERSISTENT},
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
{"LastUpdateException", PERSISTENT}, {"LastUpdateException", PERSISTENT},
{"LastUpdateTime", PERSISTENT}, {"LastUpdateTime", PERSISTENT},
{"LiveParameters", PERSISTENT}, {"LiveParameters", PERSISTENT},
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"NavSettingTime24h", PERSISTENT}, {"NavSettingTime24h", PERSISTENT},
{"NavdRender", PERSISTENT},
{"OpenpilotEnabledToggle", PERSISTENT}, {"OpenpilotEnabledToggle", PERSISTENT},
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"Passive", PERSISTENT}, {"Passive", PERSISTENT},

@ -1 +1 @@
#define COMMA_VERSION "0.8.11" #define COMMA_VERSION "0.8.12"

@ -540,8 +540,9 @@ class Controls:
if len(lat_plan.dPathPoints): if len(lat_plan.dPathPoints):
# Check if we deviated from the path # Check if we deviated from the path
left_deviation = actuators.steer > 0 and lat_plan.dPathPoints[0] < -0.1 # TODO use desired vs actual curvature
right_deviation = actuators.steer < 0 and lat_plan.dPathPoints[0] > 0.1 left_deviation = actuators.steer > 0 and lat_plan.dPathPoints[0] < -0.20
right_deviation = actuators.steer < 0 and lat_plan.dPathPoints[0] > 0.20
if left_deviation or right_deviation: if left_deviation or right_deviation:
self.events.add(EventName.steerSaturated) self.events.add(EventName.steerSaturated)

@ -41,6 +41,7 @@ class AlertManager:
self.activealerts: Dict[str, AlertEntry] = defaultdict(AlertEntry) self.activealerts: Dict[str, AlertEntry] = defaultdict(AlertEntry)
def reset(self) -> None: def reset(self) -> None:
self.alert: Optional[Alert] = None
self.alert_type: str = "" self.alert_type: str = ""
self.alert_text_1: str = "" self.alert_text_1: str = ""
self.alert_text_2: str = "" self.alert_text_2: str = ""
@ -74,13 +75,13 @@ class AlertManager:
# clear current alert # clear current alert
self.reset() self.reset()
a = current_alert.alert self.alert = current_alert.alert
if a is not None: if self.alert is not None:
self.alert_type = a.alert_type self.alert_type = self.alert.alert_type
self.audible_alert = a.audible_alert self.audible_alert = self.alert.audible_alert
self.visual_alert = a.visual_alert self.visual_alert = self.alert.visual_alert
self.alert_text_1 = a.alert_text_1 self.alert_text_1 = self.alert.alert_text_1
self.alert_text_2 = a.alert_text_2 self.alert_text_2 = self.alert.alert_text_2
self.alert_status = a.alert_status self.alert_status = self.alert.alert_status
self.alert_size = a.alert_size self.alert_size = self.alert.alert_size
self.alert_rate = a.alert_rate self.alert_rate = self.alert.alert_rate

@ -139,11 +139,10 @@ class Alert:
class NoEntryAlert(Alert): class NoEntryAlert(Alert):
def __init__(self, alert_text_2, audible_alert=AudibleAlert.chimeError, def __init__(self, alert_text_2, visual_alert=VisualAlert.none):
visual_alert=VisualAlert.none):
super().__init__("openpilot Unavailable", alert_text_2, AlertStatus.normal, super().__init__("openpilot Unavailable", alert_text_2, AlertStatus.normal,
AlertSize.mid, Priority.LOW, visual_alert, AlertSize.mid, Priority.LOW, visual_alert,
audible_alert, 3.) AudibleAlert.refuse, 3.)
class SoftDisableAlert(Alert): class SoftDisableAlert(Alert):
@ -151,7 +150,7 @@ class SoftDisableAlert(Alert):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2, super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.userPrompt, AlertSize.full, AlertStatus.userPrompt, AlertSize.full,
Priority.MID, VisualAlert.steerRequired, Priority.MID, VisualAlert.steerRequired,
AudibleAlert.chimeWarningRepeatInfinite, 2.), AudibleAlert.warningSoft, 2.),
class ImmediateDisableAlert(Alert): class ImmediateDisableAlert(Alert):
@ -159,7 +158,7 @@ class ImmediateDisableAlert(Alert):
super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2, super().__init__("TAKE CONTROL IMMEDIATELY", alert_text_2,
AlertStatus.critical, AlertSize.full, AlertStatus.critical, AlertSize.full,
Priority.HIGHEST, VisualAlert.steerRequired, Priority.HIGHEST, VisualAlert.steerRequired,
AudibleAlert.chimeWarningRepeatInfinite, 4.), AudibleAlert.warningImmediate, 4.),
class EngagementAlert(Alert): class EngagementAlert(Alert):
@ -201,7 +200,7 @@ def below_steer_speed_alert(CP: car.CarParams, sm: messaging.SubMaster, metric:
f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}", f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
"", "",
AlertStatus.userPrompt, AlertSize.small, AlertStatus.userPrompt, AlertSize.small,
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 0.4) Priority.MID, VisualAlert.steerRequired, AudibleAlert.prompt, 0.4)
def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert: def calibration_incomplete_alert(CP: car.CarParams, sm: messaging.SubMaster, metric: bool) -> Alert:
@ -321,7 +320,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"BRAKE!", "BRAKE!",
"Risk of Collision", "Risk of Collision",
AlertStatus.critical, AlertSize.full, AlertStatus.critical, AlertSize.full,
Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.chimeWarningRepeatInfinite, 2.), Priority.HIGHEST, VisualAlert.fcw, AudibleAlert.warningSoft, 2.),
}, },
EventName.ldw: { EventName.ldw: {
@ -329,7 +328,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"TAKE CONTROL", "TAKE CONTROL",
"Lane Departure Detected", "Lane Departure Detected",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.ldw, AudibleAlert.chimePrompt, 3.), Priority.LOW, VisualAlert.ldw, AudibleAlert.prompt, 3.),
}, },
# ********** events only containing alerts that display while engaged ********** # ********** events only containing alerts that display while engaged **********
@ -360,7 +359,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"Steering Temporarily Unavailable", "Steering Temporarily Unavailable",
"", "",
AlertStatus.userPrompt, AlertSize.small, AlertStatus.userPrompt, AlertSize.small,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimePrompt, 1.), Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.),
}, },
EventName.preDriverDistracted: { EventName.preDriverDistracted: {
@ -376,7 +375,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"KEEP EYES ON ROAD", "KEEP EYES ON ROAD",
"Driver Distracted", "Driver Distracted",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2RepeatInfinite, .1), Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
}, },
EventName.driverDistracted: { EventName.driverDistracted: {
@ -384,7 +383,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"DISENGAGE IMMEDIATELY", "DISENGAGE IMMEDIATELY",
"Driver Distracted", "Driver Distracted",
AlertStatus.critical, AlertSize.full, AlertStatus.critical, AlertSize.full,
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeatInfinite, .1), Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1),
}, },
EventName.preDriverUnresponsive: { EventName.preDriverUnresponsive: {
@ -400,7 +399,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"TOUCH STEERING WHEEL", "TOUCH STEERING WHEEL",
"Driver Unresponsive", "Driver Unresponsive",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.MID, VisualAlert.steerRequired, AudibleAlert.chimeWarning2RepeatInfinite, .1), Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
}, },
EventName.driverUnresponsive: { EventName.driverUnresponsive: {
@ -408,7 +407,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"DISENGAGE IMMEDIATELY", "DISENGAGE IMMEDIATELY",
"Driver Unresponsive", "Driver Unresponsive",
AlertStatus.critical, AlertSize.full, AlertStatus.critical, AlertSize.full,
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarningRepeatInfinite, .1), Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1),
}, },
EventName.manualRestart: { EventName.manualRestart: {
@ -452,7 +451,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"Car Detected in Blindspot", "Car Detected in Blindspot",
"", "",
AlertStatus.userPrompt, AlertSize.small, AlertStatus.userPrompt, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.chimePrompt, .1), Priority.LOW, VisualAlert.none, AudibleAlert.prompt, .1),
}, },
EventName.laneChange: { EventName.laneChange: {
@ -468,7 +467,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"TAKE CONTROL", "TAKE CONTROL",
"Turn Exceeds Steering Limit", "Turn Exceeds Steering Limit",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.chimeWarning2RepeatInfinite, 1.), Priority.LOW, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 1.),
}, },
# Thrown when the fan is driven at >50% but is not rotating # Thrown when the fan is driven at >50% but is not rotating
@ -496,44 +495,44 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# ********** events that affect controls state transitions ********** # ********** events that affect controls state transitions **********
EventName.pcmEnable: { EventName.pcmEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage), ET.ENABLE: EngagementAlert(AudibleAlert.engage),
}, },
EventName.buttonEnable: { EventName.buttonEnable: {
ET.ENABLE: EngagementAlert(AudibleAlert.chimeEngage), ET.ENABLE: EngagementAlert(AudibleAlert.engage),
}, },
EventName.pcmDisable: { EventName.pcmDisable: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
}, },
EventName.buttonCancel: { EventName.buttonCancel: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
}, },
EventName.brakeHold: { EventName.brakeHold: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"), ET.NO_ENTRY: NoEntryAlert("Brake Hold Active"),
}, },
EventName.parkBrake: { EventName.parkBrake: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: NoEntryAlert("Park Brake Engaged"), ET.NO_ENTRY: NoEntryAlert("Park Brake Engaged"),
}, },
EventName.pedalPressed: { EventName.pedalPressed: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: NoEntryAlert("Pedal Pressed During Attempt", ET.NO_ENTRY: NoEntryAlert("Pedal Pressed During Attempt",
visual_alert=VisualAlert.brakePressed), visual_alert=VisualAlert.brakePressed),
}, },
EventName.wrongCarMode: { EventName.wrongCarMode: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: wrong_car_mode_alert, ET.NO_ENTRY: wrong_car_mode_alert,
}, },
EventName.wrongCruiseMode: { EventName.wrongCruiseMode: {
ET.USER_DISABLE: EngagementAlert(AudibleAlert.chimeDisengage), ET.USER_DISABLE: EngagementAlert(AudibleAlert.disengage),
ET.NO_ENTRY: NoEntryAlert("Enable Adaptive Cruise"), ET.NO_ENTRY: NoEntryAlert("Enable Adaptive Cruise"),
}, },
@ -627,14 +626,12 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
# ten times the regular interval, or the average interval is more than 10% too high. # ten times the regular interval, or the average interval is more than 10% too high.
EventName.commIssue: { EventName.commIssue: {
ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"), ET.SOFT_DISABLE: SoftDisableAlert("Communication Issue between Processes"),
ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes", ET.NO_ENTRY: NoEntryAlert("Communication Issue between Processes"),
audible_alert=AudibleAlert.chimeDisengage),
}, },
# Thrown when manager detects a service exited unexpectedly while driving # Thrown when manager detects a service exited unexpectedly while driving
EventName.processNotRunning: { EventName.processNotRunning: {
ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device", ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device"),
audible_alert=AudibleAlert.chimeDisengage),
}, },
EventName.radarFault: { EventName.radarFault: {
@ -670,15 +667,13 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
EventName.lowMemory: { EventName.lowMemory: {
ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"), ET.SOFT_DISABLE: SoftDisableAlert("Low Memory: Reboot Your Device"),
ET.PERMANENT: NormalPermanentAlert("Low Memory", "Reboot your Device"), ET.PERMANENT: NormalPermanentAlert("Low Memory", "Reboot your Device"),
ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device", ET.NO_ENTRY: NoEntryAlert("Low Memory: Reboot Your Device"),
audible_alert=AudibleAlert.chimeDisengage),
}, },
EventName.highCpuUsage: { EventName.highCpuUsage: {
#ET.SOFT_DISABLE: SoftDisableAlert("System Malfunction: Reboot Your Device"), #ET.SOFT_DISABLE: SoftDisableAlert("System Malfunction: Reboot Your Device"),
#ET.PERMANENT: NormalPermanentAlert("System Malfunction", "Reboot your Device"), #ET.PERMANENT: NormalPermanentAlert("System Malfunction", "Reboot your Device"),
ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device", ET.NO_ENTRY: NoEntryAlert("System Malfunction: Reboot Your Device"),
audible_alert=AudibleAlert.chimeDisengage),
}, },
EventName.accFaulted: { EventName.accFaulted: {
@ -782,7 +777,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"openpilot Canceled", "openpilot Canceled",
"No close lead car", "No close lead car",
AlertStatus.normal, AlertSize.mid, AlertStatus.normal, AlertSize.mid,
Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, 3.), Priority.HIGH, VisualAlert.none, AudibleAlert.disengage, 3.),
ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"), ET.NO_ENTRY: NoEntryAlert("No Close Lead Car"),
}, },
@ -791,7 +786,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"openpilot Canceled", "openpilot Canceled",
"Speed too low", "Speed too low",
AlertStatus.normal, AlertSize.mid, AlertStatus.normal, AlertSize.mid,
Priority.HIGH, VisualAlert.none, AudibleAlert.chimeDisengage, 3.), Priority.HIGH, VisualAlert.none, AudibleAlert.disengage, 3.),
}, },
# When the car is driving faster than most cars in the training data the model outputs can be unpredictable # When the car is driving faster than most cars in the training data the model outputs can be unpredictable
@ -800,7 +795,7 @@ EVENTS: Dict[int, Dict[str, Union[Alert, Callable[[Any, messaging.SubMaster, boo
"Speed Too High", "Speed Too High",
"Model uncertain at this speed", "Model uncertain at this speed",
AlertStatus.userPrompt, AlertSize.mid, AlertStatus.userPrompt, AlertSize.mid,
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.chimeWarning2RepeatInfinite, 4.), Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.promptRepeat, 4.),
ET.NO_ENTRY: NoEntryAlert("Slow down to engage"), ET.NO_ENTRY: NoEntryAlert("Slow down to engage"),
}, },

@ -38,7 +38,7 @@ DESIRES = {
} }
class LateralPlanner(): class LateralPlanner:
def __init__(self, CP, use_lanelines=True, wide_camera=False): def __init__(self, CP, use_lanelines=True, wide_camera=False):
self.use_lanelines = use_lanelines self.use_lanelines = use_lanelines
self.LP = LanePlanner(wide_camera) self.LP = LanePlanner(wide_camera)
@ -55,8 +55,8 @@ class LateralPlanner():
self.prev_one_blinker = False self.prev_one_blinker = False
self.desire = log.LateralPlan.Desire.none self.desire = log.LateralPlan.Desire.none
self.path_xyz = np.zeros((TRAJECTORY_SIZE,3)) self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3))
self.path_xyz_stds = np.ones((TRAJECTORY_SIZE,3)) self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3))
self.plan_yaw = np.zeros((TRAJECTORY_SIZE,)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE,))
self.t_idxs = np.arange(TRAJECTORY_SIZE) self.t_idxs = np.arange(TRAJECTORY_SIZE)
self.y_pts = np.zeros(TRAJECTORY_SIZE) self.y_pts = np.zeros(TRAJECTORY_SIZE)
@ -67,12 +67,8 @@ class LateralPlanner():
def reset_mpc(self, x0=np.zeros(6)): def reset_mpc(self, x0=np.zeros(6)):
self.x0 = x0 self.x0 = x0
self.lat_mpc.reset(x0=self.x0) self.lat_mpc.reset(x0=self.x0)
self.desired_curvature = 0.0
self.safe_desired_curvature = 0.0
self.desired_curvature_rate = 0.0
self.safe_desired_curvature_rate = 0.0
def update(self, sm, CP): def update(self, sm):
v_ego = sm['carState'].vEgo v_ego = sm['carState'].vEgo
active = sm['controlsState'].active active = sm['controlsState'].active
measured_curvature = sm['controlsState'].curvature measured_curvature = sm['controlsState'].curvature
@ -110,7 +106,7 @@ class LateralPlanner():
self.lane_change_direction = LaneChangeDirection.none self.lane_change_direction = LaneChangeDirection.none
torque_applied = sm['carState'].steeringPressed and \ torque_applied = sm['carState'].steeringPressed and \
((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or ((sm['carState'].steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
(sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right)) (sm['carState'].steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))
blindspot_detected = ((sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or blindspot_detected = ((sm['carState'].leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or
@ -124,7 +120,7 @@ class LateralPlanner():
# LaneChangeState.laneChangeStarting # LaneChangeState.laneChangeStarting
elif self.lane_change_state == LaneChangeState.laneChangeStarting: elif self.lane_change_state == LaneChangeState.laneChangeStarting:
# fade out over .5s # fade out over .5s
self.lane_change_ll_prob = max(self.lane_change_ll_prob - 2*DT_MDL, 0.0) self.lane_change_ll_prob = max(self.lane_change_ll_prob - 2 * DT_MDL, 0.0)
# 98% certainty # 98% certainty
lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob lane_change_prob = self.LP.l_lane_change_prob + self.LP.r_lane_change_prob
@ -167,14 +163,14 @@ class LateralPlanner():
self.LP.rll_prob *= self.lane_change_ll_prob self.LP.rll_prob *= self.lane_change_ll_prob
if self.use_lanelines: if self.use_lanelines:
d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz)
self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, CP.steerRateCost) self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, self.steer_rate_cost)
else: else:
d_path_xyz = self.path_xyz d_path_xyz = self.path_xyz
path_cost = np.clip(abs(self.path_xyz[0,1]/self.path_xyz_stds[0,1]), 0.5, 1.5) * MPC_COST_LAT.PATH path_cost = np.clip(abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 1.5) * MPC_COST_LAT.PATH
# Heading cost is useful at low speed, otherwise end of plan can be off-heading # Heading cost is useful at low speed, otherwise end of plan can be off-heading
heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0])
self.lat_mpc.set_weights(path_cost, heading_cost, CP.steerRateCost) self.lat_mpc.set_weights(path_cost, heading_cost, self.steer_rate_cost)
y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:,1]) y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1])
heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw)
self.y_pts = y_pts self.y_pts = y_pts
@ -187,11 +183,10 @@ class LateralPlanner():
y_pts, y_pts,
heading_pts) heading_pts)
# init state for next # init state for next
self.x0[3] = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.lat_mpc.x_sol[:,3]) self.x0[3] = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.lat_mpc.x_sol[:, 3])
# Check for infeasible MPC solution
# Check for infeasable MPC solution mpc_nans = any(math.isnan(x) for x in self.lat_mpc.x_sol[:, 3])
mpc_nans = any(math.isnan(x) for x in self.lat_mpc.x_sol[:,3])
t = sec_since_boot() t = sec_since_boot()
if mpc_nans or self.lat_mpc.solution_status != 0: if mpc_nans or self.lat_mpc.solution_status != 0:
self.reset_mpc() self.reset_mpc()
@ -212,8 +207,8 @@ class LateralPlanner():
plan_send.lateralPlan.laneWidth = float(self.LP.lane_width) plan_send.lateralPlan.laneWidth = float(self.LP.lane_width)
plan_send.lateralPlan.dPathPoints = [float(x) for x in self.y_pts] plan_send.lateralPlan.dPathPoints = [float(x) for x in self.y_pts]
plan_send.lateralPlan.psis = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 2]] plan_send.lateralPlan.psis = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 2]]
plan_send.lateralPlan.curvatures = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N,3]] plan_send.lateralPlan.curvatures = [float(x) for x in self.lat_mpc.x_sol[0:CONTROL_N, 3]]
plan_send.lateralPlan.curvatureRates = [float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N-1]] +[0.0] plan_send.lateralPlan.curvatureRates = [float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N - 1]] + [0.0]
plan_send.lateralPlan.lProb = float(self.LP.lll_prob) plan_send.lateralPlan.lProb = float(self.LP.lll_prob)
plan_send.lateralPlan.rProb = float(self.LP.rll_prob) plan_send.lateralPlan.rProb = float(self.LP.rll_prob)
plan_send.lateralPlan.dProb = float(self.LP.d_prob) plan_send.lateralPlan.dProb = float(self.LP.d_prob)

@ -10,20 +10,20 @@ LongCtrlState = car.CarControl.Actuators.LongControlState
STOPPING_TARGET_SPEED_OFFSET = 0.01 STOPPING_TARGET_SPEED_OFFSET = 0.01
# As per ISO 15622:2018 for all speeds # As per ISO 15622:2018 for all speeds
ACCEL_MIN_ISO = -3.5 # m/s^2 ACCEL_MIN_ISO = -3.5 # m/s^2
ACCEL_MAX_ISO = 2.0 # m/s^2 ACCEL_MAX_ISO = 2.0 # m/s^2
def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, v_pid, def long_control_state_trans(CP, active, long_control_state, v_ego, v_target_future, v_pid,
output_accel, brake_pressed, cruise_standstill, min_speed_can): output_accel, brake_pressed, cruise_standstill, min_speed_can):
"""Update longitudinal control state machine""" """Update longitudinal control state machine"""
stopping_target_speed = min_speed_can + STOPPING_TARGET_SPEED_OFFSET stopping_target_speed = min_speed_can + STOPPING_TARGET_SPEED_OFFSET
stopping_condition = (v_ego < 2.0 and cruise_standstill) or \ stopping_condition = (v_ego < 2.0 and cruise_standstill) or \
(v_ego < CP.vEgoStopping and (v_ego < CP.vEgoStopping and
((v_pid < stopping_target_speed and v_target < stopping_target_speed) or ((v_pid < stopping_target_speed and v_target_future < stopping_target_speed) or
brake_pressed)) brake_pressed))
starting_condition = v_target > CP.vEgoStarting and not cruise_standstill starting_condition = v_target_future > CP.vEgoStarting and not cruise_standstill
if not active: if not active:
long_control_state = LongCtrlState.off long_control_state = LongCtrlState.off
@ -75,13 +75,10 @@ class LongControl():
v_target_upper = interp(CP.longitudinalActuatorDelayUpperBound, T_IDXS[:CONTROL_N], long_plan.speeds) v_target_upper = interp(CP.longitudinalActuatorDelayUpperBound, T_IDXS[:CONTROL_N], long_plan.speeds)
a_target_upper = 2 * (v_target_upper - long_plan.speeds[0])/CP.longitudinalActuatorDelayUpperBound - long_plan.accels[0] a_target_upper = 2 * (v_target_upper - long_plan.speeds[0])/CP.longitudinalActuatorDelayUpperBound - long_plan.accels[0]
v_target = min(v_target_lower, v_target_upper)
a_target = min(a_target_lower, a_target_upper) a_target = min(a_target_lower, a_target_upper)
v_target_future = long_plan.speeds[-1] v_target_future = long_plan.speeds[-1]
else: else:
v_target = 0.0
v_target_future = 0.0 v_target_future = 0.0
a_target = 0.0 a_target = 0.0
@ -103,11 +100,11 @@ class LongControl():
# tracking objects and driving # tracking objects and driving
elif self.long_control_state == LongCtrlState.pid: elif self.long_control_state == LongCtrlState.pid:
self.v_pid = v_target self.v_pid = long_plan.speeds[0]
# Toyota starts braking more when it thinks you want to stop # Toyota starts braking more when it thinks you want to stop
# Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration
prevent_overshoot = not CP.stoppingControl and CS.vEgo < 1.5 and v_target_future < 0.7 prevent_overshoot = not CP.stoppingControl and CS.vEgo < 1.5 and v_target_future < 0.7 and v_target_future < self.v_pid
deadzone = interp(CS.vEgo, CP.longitudinalTuning.deadzoneBP, CP.longitudinalTuning.deadzoneV) deadzone = interp(CS.vEgo, CP.longitudinalTuning.deadzoneBP, CP.longitudinalTuning.deadzoneV)
freeze_integrator = prevent_overshoot freeze_integrator = prevent_overshoot

@ -49,15 +49,16 @@ T_IDXS_LST = [index_function(idx, max_val=MAX_T, max_idx=N+1) for idx in range(N
T_IDXS = np.array(T_IDXS_LST) T_IDXS = np.array(T_IDXS_LST)
T_DIFFS = np.diff(T_IDXS, prepend=[0.]) T_DIFFS = np.diff(T_IDXS, prepend=[0.])
MIN_ACCEL = -3.5 MIN_ACCEL = -3.5
T_REACT = 1.8 T_REACT = 0.5
MAX_BRAKE = 9.81 T_FOLLOW = 1.45
COMFORT_BRAKE = 2.0
STOP_DISTANCE = 6.0
def get_stopped_equivalence_factor(v_lead): def get_stopped_equivalence_factor(v_lead):
return T_REACT * v_lead + (v_lead*v_lead) / (2 * MAX_BRAKE) return v_lead**2 / (2 * COMFORT_BRAKE) - (T_FOLLOW - T_REACT) * v_lead
def get_safe_obstacle_distance(v_ego): def get_safe_obstacle_distance(v_ego):
return 2 * T_REACT * v_ego + (v_ego*v_ego) / (2 * MAX_BRAKE) + 4.0 return (v_ego*v_ego) / (2 * COMFORT_BRAKE) + T_REACT * v_ego + STOP_DISTANCE
def desired_follow_distance(v_ego, v_lead): def desired_follow_distance(v_ego, v_lead):
return get_safe_obstacle_distance(v_ego) - get_stopped_equivalence_factor(v_lead) return get_safe_obstacle_distance(v_ego) - get_stopped_equivalence_factor(v_lead)
@ -242,7 +243,7 @@ class LongitudinalMpc():
self.solver.cost_set(i, 'Zl', Zl) self.solver.cost_set(i, 'Zl', Zl)
def set_weights_for_xva_policy(self): def set_weights_for_xva_policy(self):
W = np.asfortranarray(np.diag([0., 10., 1., 10., 0.0, 1.])) W = np.asfortranarray(np.diag([0., 10., 1., 10., 1.]))
for i in range(N): for i in range(N):
self.solver.cost_set(i, 'W', W) self.solver.cost_set(i, 'W', W)
# Setting the slice without the copy make the array not contiguous, # Setting the slice without the copy make the array not contiguous,
@ -298,8 +299,9 @@ class LongitudinalMpc():
self.cruise_min_a = min_a self.cruise_min_a = min_a
self.cruise_max_a = max_a self.cruise_max_a = max_a
def update(self, carstate, radarstate, v_cruise): def update(self, carstate, radarstate, v_cruise, prev_accel_constraint=False):
v_ego = self.x0[1] v_ego = self.x0[1]
a_ego = self.x0[2]
self.status = radarstate.leadOne.status or radarstate.leadTwo.status self.status = radarstate.leadOne.status or radarstate.leadTwo.status
lead_xv_0 = self.process_lead(radarstate.leadOne) lead_xv_0 = self.process_lead(radarstate.leadOne)
@ -317,17 +319,20 @@ class LongitudinalMpc():
# Fake an obstacle for cruise, this ensures smooth acceleration to set speed # Fake an obstacle for cruise, this ensures smooth acceleration to set speed
# when the leads are no factor. # when the leads are no factor.
cruise_lower_bound = v_ego + (3/4) * self.cruise_min_a * T_IDXS v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05)
cruise_upper_bound = v_ego + (3/4) * self.cruise_max_a * T_IDXS v_upper = v_ego + (T_IDXS * self.cruise_max_a * 1.05)
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_cruise_clipped = np.clip(v_cruise * np.ones(N+1),
cruise_lower_bound, v_lower,
cruise_upper_bound) v_upper)
cruise_obstacle = T_IDXS*v_cruise_clipped + get_safe_obstacle_distance(v_cruise_clipped) cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped)
x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle]) x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle])
self.source = SOURCES[np.argmin(x_obstacles[0])] self.source = SOURCES[np.argmin(x_obstacles[0])]
self.params[:,2] = np.min(x_obstacles, axis=1) self.params[:,2] = np.min(x_obstacles, axis=1)
self.params[:,3] = np.copy(self.prev_a) if prev_accel_constraint:
self.params[:,3] = np.copy(self.prev_a)
else:
self.params[:,3] = a_ego
self.run() self.run()
if (np.any(lead_xv_0[:,0] - self.x_sol[:,0] < CRASH_DISTANCE) and if (np.any(lead_xv_0[:,0] - self.x_sol[:,0] < CRASH_DISTANCE) and

@ -14,7 +14,7 @@ from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
LON_MPC_STEP = 0.2 # first step is 0.2s LON_MPC_STEP = 0.2 # first step is 0.2s
AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted AWARENESS_DECEL = -0.2 # car smoothly decel at .2m/s^2 when user is distracted
A_CRUISE_MIN = -1.2 A_CRUISE_MIN = -1.2
A_CRUISE_MAX_VALS = [1.2, 1.2, 0.8, 0.6] A_CRUISE_MAX_VALS = [1.2, 1.2, 0.8, 0.6]
A_CRUISE_MAX_BP = [0., 15., 25., 40.] A_CRUISE_MAX_BP = [0., 15., 25., 40.]
@ -35,13 +35,13 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
""" """
a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V) a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V)
a_y = v_ego**2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase) a_y = v_ego ** 2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase)
a_x_allowed = math.sqrt(max(a_total_max**2 - a_y**2, 0.)) a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.))
return [a_target[0], min(a_target[1], a_x_allowed)] return [a_target[0], min(a_target[1], a_x_allowed)]
class Planner(): class Planner:
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.mpc = LongitudinalMpc() self.mpc = LongitudinalMpc()
@ -50,14 +50,13 @@ class Planner():
self.v_desired = init_v self.v_desired = init_v
self.a_desired = init_a self.a_desired = init_a
self.alpha = np.exp(-DT_MDL/2.0) self.alpha = np.exp(-DT_MDL / 2.0)
self.v_desired_trajectory = np.zeros(CONTROL_N) self.v_desired_trajectory = np.zeros(CONTROL_N)
self.a_desired_trajectory = np.zeros(CONTROL_N) self.a_desired_trajectory = np.zeros(CONTROL_N)
self.j_desired_trajectory = np.zeros(CONTROL_N) self.j_desired_trajectory = np.zeros(CONTROL_N)
def update(self, sm):
def update(self, sm, CP):
v_ego = sm['carState'].vEgo v_ego = sm['carState'].vEgo
a_ego = sm['carState'].aEgo a_ego = sm['carState'].aEgo
@ -68,10 +67,13 @@ class Planner():
long_control_state = sm['controlsState'].longControlState long_control_state = sm['controlsState'].longControlState
force_slow_decel = sm['controlsState'].forceDecel force_slow_decel = sm['controlsState'].forceDecel
prev_accel_constraint = True
enabled = (long_control_state == LongCtrlState.pid) or (long_control_state == LongCtrlState.stopping) enabled = (long_control_state == LongCtrlState.pid) or (long_control_state == LongCtrlState.stopping)
if not enabled or sm['carState'].gasPressed: if not enabled or sm['carState'].gasPressed:
self.v_desired = v_ego self.v_desired = v_ego
self.a_desired = a_ego self.a_desired = a_ego
# Smoothly changing between accel trajectory is only relevant when OP is driving
prev_accel_constraint = False
# Prevent divergence, smooth in current v_ego # Prevent divergence, smooth in current v_ego
self.v_desired = self.alpha * self.v_desired + (1 - self.alpha) * v_ego self.v_desired = self.alpha * self.v_desired + (1 - self.alpha) * v_ego
@ -88,12 +90,12 @@ class Planner():
accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05) accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05)
self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1])
self.mpc.set_cur_state(self.v_desired, self.a_desired) self.mpc.set_cur_state(self.v_desired, self.a_desired)
self.mpc.update(sm['carState'], sm['radarState'], v_cruise) self.mpc.update(sm['carState'], sm['radarState'], v_cruise, prev_accel_constraint=prev_accel_constraint)
self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution) self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution) self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution)
self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution) self.j_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC[:-1], self.mpc.j_solution)
#TODO counter is only needed because radar is glitchy, remove once radar is gone # TODO counter is only needed because radar is glitchy, remove once radar is gone
self.fcw = self.mpc.crash_cnt > 5 self.fcw = self.mpc.crash_cnt > 5
if self.fcw: if self.fcw:
cloudlog.info("FCW triggered") cloudlog.info("FCW triggered")
@ -101,7 +103,7 @@ class Planner():
# Interpolate 0.05 seconds and save as starting point for next iteration # Interpolate 0.05 seconds and save as starting point for next iteration
a_prev = self.a_desired a_prev = self.a_desired
self.a_desired = float(interp(DT_MDL, T_IDXS[:CONTROL_N], self.a_desired_trajectory)) self.a_desired = float(interp(DT_MDL, T_IDXS[:CONTROL_N], self.a_desired_trajectory))
self.v_desired = self.v_desired + DT_MDL * (self.a_desired + a_prev)/2.0 self.v_desired = self.v_desired + DT_MDL * (self.a_desired + a_prev) / 2.0
def publish(self, sm, pm): def publish(self, sm, pm):
plan_send = messaging.new_message('longitudinalPlan') plan_send = messaging.new_message('longitudinalPlan')

@ -36,9 +36,9 @@ def plannerd_thread(sm=None, pm=None):
sm.update() sm.update()
if sm.updated['modelV2']: if sm.updated['modelV2']:
lateral_planner.update(sm, CP) lateral_planner.update(sm)
lateral_planner.publish(sm, pm) lateral_planner.publish(sm, pm)
longitudinal_planner.update(sm, CP) longitudinal_planner.update(sm)
longitudinal_planner.publish(sm, pm) longitudinal_planner.publish(sm, pm)

@ -29,7 +29,7 @@ class TestFollowingDistance(unittest.TestCase):
simulation_steady_state = run_following_distance_simulation(v_lead) simulation_steady_state = run_following_distance_simulation(v_lead)
correct_steady_state = desired_follow_distance(v_lead, v_lead) correct_steady_state = desired_follow_distance(v_lead, v_lead)
self.assertAlmostEqual(simulation_steady_state, correct_steady_state, delta=(correct_steady_state*.1 + .3)) self.assertAlmostEqual(simulation_steady_state, correct_steady_state, delta=(correct_steady_state*.1 + 1.0))
if __name__ == "__main__": if __name__ == "__main__":

@ -1,6 +1,6 @@
"""Install exception handler for process crash.""" """Install exception handler for process crash."""
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.version import version from selfdrive.version import get_version
import sentry_sdk import sentry_sdk
from sentry_sdk.integrations.threading import ThreadingIntegration from sentry_sdk.integrations.threading import ThreadingIntegration
@ -24,4 +24,4 @@ def bind_extra(**kwargs) -> None:
def init() -> None: def init() -> None:
sentry_sdk.init("https://a8dc76b5bfb34908a601d67e2aa8bcf9@o33823.ingest.sentry.io/77924", sentry_sdk.init("https://a8dc76b5bfb34908a601d67e2aa8bcf9@o33823.ingest.sentry.io/77924",
default_integrations=False, integrations=[ThreadingIntegration(propagate_hub=True)], default_integrations=False, integrations=[ThreadingIntegration(propagate_hub=True)],
release=version) release=get_version())

@ -66,7 +66,7 @@ if __name__ == "__main__":
for p in psutil.process_iter(): for p in psutil.process_iter():
if p == psutil.Process(): if p == psutil.Process():
continue continue
matched = any([l for l in p.cmdline() if any([pn for pn in monitored_proc_names if re.match(r'.*{}.*'.format(pn), l, re.M | re.I)])]) matched = any(l for l in p.cmdline() if any(pn for pn in monitored_proc_names if re.match(r'.*{}.*'.format(pn), l, re.M | re.I)))
if matched: if matched:
k = ' '.join(p.cmdline()) k = ' '.join(p.cmdline())
print('Add monitored proc:', k) print('Add monitored proc:', k)

@ -7,18 +7,31 @@ import time
from cereal import car, log from cereal import car, log
import cereal.messaging as messaging import cereal.messaging as messaging
from common.realtime import DT_CTRL
from selfdrive.car.honda.interface import CarInterface from selfdrive.car.honda.interface import CarInterface
from selfdrive.controls.lib.events import ET, EVENTS, Events from selfdrive.controls.lib.events import ET, EVENTS, Events
from selfdrive.controls.lib.alertmanager import AlertManager from selfdrive.controls.lib.alertmanager import AlertManager
EventName = car.CarEvent.EventName EventName = car.CarEvent.EventName
def cycle_alerts(duration=2000, is_metric=False): def cycle_alerts(duration=200, is_metric=False):
alerts = list(EVENTS.keys()) # all alerts
print(alerts) #alerts = list(EVENTS.keys())
alerts = [EventName.preDriverDistracted, EventName.promptDriverDistracted, EventName.driverDistracted] # this plays each type of audible alert
#alerts = [EventName.preLaneChangeLeft, EventName.preLaneChangeRight] alerts = [
(EventName.buttonEnable, ET.ENABLE),
(EventName.buttonCancel, ET.USER_DISABLE),
(EventName.wrongGear, ET.NO_ENTRY),
(EventName.vehicleModelInvalid, ET.SOFT_DISABLE),
(EventName.accFaulted, ET.IMMEDIATE_DISABLE),
# DM sequence
(EventName.preDriverDistracted, ET.WARNING),
(EventName.promptDriverDistracted, ET.WARNING),
(EventName.driverDistracted, ET.WARNING),
]
CP = CarInterface.get_params("HONDA CIVIC 2016") CP = CarInterface.get_params("HONDA CIVIC 2016")
sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration', sm = messaging.SubMaster(['deviceState', 'pandaStates', 'roadCameraState', 'modelV2', 'liveCalibration',
@ -30,43 +43,45 @@ def cycle_alerts(duration=2000, is_metric=False):
AM = AlertManager() AM = AlertManager()
frame = 0 frame = 0
idx, last_alert_millis = 0, 0 while True:
while 1:
if frame % duration == 0:
idx = (idx + 1) % len(alerts)
events.clear()
events.add(alerts[idx])
current_alert_types = [ET.PERMANENT, ET.USER_DISABLE, ET.IMMEDIATE_DISABLE, current_alert_types = [ET.PERMANENT, ET.USER_DISABLE, ET.IMMEDIATE_DISABLE,
ET.SOFT_DISABLE, ET.PRE_ENABLE, ET.NO_ENTRY, ET.SOFT_DISABLE, ET.PRE_ENABLE, ET.NO_ENTRY,
ET.ENABLE, ET.WARNING] ET.ENABLE, ET.WARNING]
a = events.create_alerts(current_alert_types, [CP, sm, is_metric])
AM.add_many(frame, a) for alert, et in alerts:
AM.process_alerts(frame) events.clear()
events.add(alert)
dat = messaging.new_message()
dat.init('controlsState') a = events.create_alerts([et, ], [CP, sm, is_metric])
dat.controlsState.alertText1 = AM.alert_text_1 AM.add_many(frame, a)
dat.controlsState.alertText2 = AM.alert_text_2 AM.process_alerts(frame)
dat.controlsState.alertSize = AM.alert_size print(AM.alert)
dat.controlsState.alertStatus = AM.alert_status for _ in range(duration):
dat.controlsState.alertBlinkingRate = AM.alert_rate dat = messaging.new_message()
dat.controlsState.alertType = AM.alert_type dat.init('controlsState')
dat.controlsState.alertSound = AM.audible_alert dat.controlsState.enabled = True
pm.send('controlsState', dat)
dat.controlsState.alertText1 = AM.alert_text_1
dat = messaging.new_message() dat.controlsState.alertText2 = AM.alert_text_2
dat.init('deviceState') dat.controlsState.alertSize = AM.alert_size
dat.deviceState.started = True dat.controlsState.alertStatus = AM.alert_status
pm.send('deviceState', dat) dat.controlsState.alertBlinkingRate = AM.alert_rate
dat.controlsState.alertType = AM.alert_type
dat = messaging.new_message('pandaStates', 1) dat.controlsState.alertSound = AM.audible_alert
dat.pandaStates[0].ignitionLine = True pm.send('controlsState', dat)
dat.pandaStates[0].pandaType = log.PandaState.PandaType.uno
pm.send('pandaStates', dat) dat = messaging.new_message()
dat.init('deviceState')
time.sleep(0.01) dat.deviceState.started = True
pm.send('deviceState', dat)
dat = messaging.new_message('pandaStates', 1)
dat.pandaStates[0].ignitionLine = True
dat.pandaStates[0].pandaType = log.PandaState.PandaType.uno
pm.send('pandaStates', dat)
frame += 1
time.sleep(DT_CTRL)
if __name__ == '__main__': if __name__ == '__main__':
cycle_alerts() cycle_alerts()

@ -1,7 +1,7 @@
from abc import abstractmethod from abc import abstractmethod
from collections import namedtuple from collections import namedtuple
ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'ambient']) ThermalConfig = namedtuple('ThermalConfig', ['cpu', 'gpu', 'mem', 'bat', 'ambient', 'pmic'])
class HardwareBase: class HardwareBase:
@staticmethod @staticmethod

@ -369,7 +369,7 @@ class Android(HardwareBase):
os.system('LD_LIBRARY_PATH="" svc power shutdown') os.system('LD_LIBRARY_PATH="" svc power shutdown')
def get_thermal_config(self): def get_thermal_config(self):
return ThermalConfig(cpu=((5, 7, 10, 12), 10), gpu=((16,), 10), mem=(2, 10), bat=(29, 1000), ambient=(25, 1)) return ThermalConfig(cpu=((5, 7, 10, 12), 10), gpu=((16,), 10), mem=(2, 10), bat=(29, 1000), ambient=(25, 1), pmic=((22,), 1000))
def set_screen_brightness(self, percentage): def set_screen_brightness(self, percentage):
with open("/sys/class/leds/lcd-backlight/brightness", "w") as f: with open("/sys/class/leds/lcd-backlight/brightness", "w") as f:

@ -78,7 +78,7 @@ class Pc(HardwareBase):
print("SHUTDOWN!") print("SHUTDOWN!")
def get_thermal_config(self): def get_thermal_config(self):
return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), bat=(None, 1), ambient=(None, 1)) return ThermalConfig(cpu=((None,), 1), gpu=((None,), 1), mem=(None, 1), bat=(None, 1), ambient=(None, 1), pmic=((None,), 1))
def set_screen_brightness(self, percentage): def set_screen_brightness(self, percentage):
pass pass

@ -31,17 +31,18 @@ BASE_CONFIG = [
AmpConfig("Enable PLL2", 0b1, 0x1A, 7, 0b10000000), AmpConfig("Enable PLL2", 0b1, 0x1A, 7, 0b10000000),
AmpConfig("DAI1: I2S mode", 0b00100, 0x14, 2, 0b01111100), AmpConfig("DAI1: I2S mode", 0b00100, 0x14, 2, 0b01111100),
AmpConfig("DAI2: I2S mode", 0b00100, 0x1C, 2, 0b01111100), AmpConfig("DAI2: I2S mode", 0b00100, 0x1C, 2, 0b01111100),
AmpConfig("Right speaker output volume", 0x1a, 0x3E, 0, 0b00011111), AmpConfig("Right speaker output volume", 0x1c, 0x3E, 0, 0b00011111),
AmpConfig("DAI1 Passband filtering: music mode", 0b1, 0x18, 7, 0b10000000), AmpConfig("DAI1 Passband filtering: music mode", 0b1, 0x18, 7, 0b10000000),
AmpConfig("DAI1 voice mode gain (DV1G)", 0b00, 0x2F, 4, 0b00110000), AmpConfig("DAI1 voice mode gain (DV1G)", 0b00, 0x2F, 4, 0b00110000),
AmpConfig("DAI1 attenuation (DV1)", 0x0, 0x2F, 0, 0b00001111), AmpConfig("DAI1 attenuation (DV1)", 0x0, 0x2F, 0, 0b00001111),
AmpConfig("DAI2 attenuation (DV2)", 0x0, 0x31, 0, 0b00001111), AmpConfig("DAI2 attenuation (DV2)", 0x0, 0x31, 0, 0b00001111),
AmpConfig("DAI2: DC blocking", 0b1, 0x20, 0, 0b00000001), AmpConfig("DAI2: DC blocking", 0b1, 0x20, 0, 0b00000001),
AmpConfig("DAI2: High sample rate", 0b0, 0x20, 3, 0b00001000), AmpConfig("DAI2: High sample rate", 0b0, 0x20, 3, 0b00001000),
AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000), AmpConfig("ALC enable", 0b1, 0x43, 7, 0b10000000),
AmpConfig("ALC/excursion limiter release time", 0b101, 0x43, 4, 0b01110000), AmpConfig("ALC/excursion limiter release time", 0b101, 0x43, 4, 0b01110000),
AmpConfig("ALC multiband enable", 0b1, 0x43, 3, 0b00001000),
AmpConfig("DAI1 EQ enable", 0b0, 0x49, 0, 0b00000001), AmpConfig("DAI1 EQ enable", 0b0, 0x49, 0, 0b00000001),
AmpConfig("DAI2 EQ enable", 0b0, 0x49, 1, 0b00000010), AmpConfig("DAI2 EQ enable", 0b1, 0x49, 1, 0b00000010),
AmpConfig("DAI2 EQ clip detection disabled", 0b1, 0x32, 4, 0b00010000), AmpConfig("DAI2 EQ clip detection disabled", 0b1, 0x32, 4, 0b00010000),
AmpConfig("DAI2 EQ attenuation", 0x5, 0x32, 0, 0b00001111), AmpConfig("DAI2 EQ attenuation", 0x5, 0x32, 0, 0b00001111),
AmpConfig("Excursion limiter upper corner freq", 0b100, 0x41, 4, 0b01110000), AmpConfig("Excursion limiter upper corner freq", 0b100, 0x41, 4, 0b01110000),
@ -62,11 +63,11 @@ BASE_CONFIG = [
AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000), AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000),
] ]
BASE_CONFIG += configs_from_eq_params(0x84, EQParams(0x65C4, 0xC07C, 0x3D66, 0x07D9, 0x120F)) BASE_CONFIG += configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656))
BASE_CONFIG += configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)) BASE_CONFIG += configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF))
BASE_CONFIG += configs_from_eq_params(0x98, EQParams(0x2822, 0xC1C7, 0x3B50, 0x0EF8, 0x180A)) BASE_CONFIG += configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42))
BASE_CONFIG += configs_from_eq_params(0xA2, EQParams(0x1009, 0xC5C2, 0x271F, 0x1A87, 0x32A6)) BASE_CONFIG += configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807))
BASE_CONFIG += configs_from_eq_params(0xAC, EQParams(0x2000, 0xCA1E, 0x4000, 0x2287, 0x0000)) BASE_CONFIG += configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B))
class Amplifier: class Amplifier:
AMP_I2C_BUS = 0 AMP_I2C_BUS = 0

@ -9,8 +9,8 @@
class HardwareTici : public HardwareNone { class HardwareTici : public HardwareNone {
public: public:
static constexpr float MAX_VOLUME = 1.0; static constexpr float MAX_VOLUME = 0.9;
static constexpr float MIN_VOLUME = 0.4; static constexpr float MIN_VOLUME = 0.15;
static bool TICI() { return true; } static bool TICI() { return true; }
static std::string get_os_version() { static std::string get_os_version() {
return "AGNOS " + util::read_file("/VERSION"); return "AGNOS " + util::read_file("/VERSION");

@ -275,7 +275,7 @@ class Tici(HardwareBase):
os.system("sudo poweroff") os.system("sudo poweroff")
def get_thermal_config(self): def get_thermal_config(self):
return ThermalConfig(cpu=((1, 2, 3, 4, 5, 6, 7, 8), 1000), gpu=((48,49), 1000), mem=(15, 1000), bat=(None, 1), ambient=(65, 1000)) return ThermalConfig(cpu=((1, 2, 3, 4, 5, 6, 7, 8), 1000), gpu=((48,49), 1000), mem=(15, 1000), bat=(None, 1), ambient=(65, 1000), pmic=((35, 36), 1000))
def set_screen_brightness(self, percentage): def set_screen_brightness(self, percentage):
try: try:

@ -18,6 +18,7 @@ const double MIN_STD_SANITY_CHECK = 1e-5; // m or rad
const double VALID_TIME_SINCE_RESET = 1.0; // s 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
static VectorXd floatlist2vector(const capnp::List<float, capnp::Kind::PRIMITIVE>::Reader& floatlist) { static VectorXd floatlist2vector(const capnp::List<float, capnp::Kind::PRIMITIVE>::Reader& floatlist) {
VectorXd res(floatlist.size()); VectorXd res(floatlist.size());
@ -130,16 +131,16 @@ void Localizer::build_live_location(cereal::LiveLocationKalman::Builder& fix) {
Vector3d nans = Vector3d(NAN, NAN, NAN); Vector3d nans = Vector3d(NAN, NAN, NAN);
// write measurements to msg // write measurements to msg
init_measurement(fix.initPositionGeodetic(), fix_pos_geo_vec, nans, this->last_gps_fix > 0); init_measurement(fix.initPositionGeodetic(), fix_pos_geo_vec, nans, this->gps_mode);
init_measurement(fix.initPositionECEF(), fix_ecef, fix_ecef_std, this->last_gps_fix > 0); init_measurement(fix.initPositionECEF(), fix_ecef, fix_ecef_std, this->gps_mode);
init_measurement(fix.initVelocityECEF(), vel_ecef, vel_ecef_std, this->last_gps_fix > 0); init_measurement(fix.initVelocityECEF(), vel_ecef, vel_ecef_std, this->gps_mode);
init_measurement(fix.initVelocityNED(), ned_vel, nans, this->last_gps_fix > 0); init_measurement(fix.initVelocityNED(), ned_vel, nans, this->gps_mode);
init_measurement(fix.initVelocityDevice(), vel_device, vel_device_std, true); init_measurement(fix.initVelocityDevice(), vel_device, vel_device_std, true);
init_measurement(fix.initAccelerationDevice(), accDevice, accDeviceErr, true); init_measurement(fix.initAccelerationDevice(), accDevice, accDeviceErr, true);
init_measurement(fix.initOrientationECEF(), orientation_ecef, orientation_ecef_std, this->last_gps_fix > 0); init_measurement(fix.initOrientationECEF(), orientation_ecef, orientation_ecef_std, this->gps_mode);
init_measurement(fix.initCalibratedOrientationECEF(), calibrated_orientation_ecef, nans, this->calibrated && this->last_gps_fix > 0); init_measurement(fix.initCalibratedOrientationECEF(), calibrated_orientation_ecef, nans, this->calibrated && this->gps_mode);
init_measurement(fix.initOrientationNED(), orientation_ned, nans, this->last_gps_fix > 0); init_measurement(fix.initOrientationNED(), orientation_ned, nans, this->gps_mode);
init_measurement(fix.initCalibratedOrientationNED(), calibrated_orientation_ned, nans, this->calibrated && this->last_gps_fix > 0); init_measurement(fix.initCalibratedOrientationNED(), calibrated_orientation_ned, nans, this->calibrated && this->gps_mode);
init_measurement(fix.initAngularVelocityDevice(), angVelocityDevice, angVelocityDeviceErr, true); init_measurement(fix.initAngularVelocityDevice(), angVelocityDevice, angVelocityDeviceErr, true);
init_measurement(fix.initVelocityCalibrated(), vel_calib, vel_calib_std, this->calibrated); init_measurement(fix.initVelocityCalibrated(), vel_calib, vel_calib_std, this->calibrated);
init_measurement(fix.initAngularVelocityCalibrated(), ang_vel_calib, ang_vel_calib_std, this->calibrated); init_measurement(fix.initAngularVelocityCalibrated(), ang_vel_calib, ang_vel_calib_std, this->calibrated);
@ -243,27 +244,39 @@ void Localizer::handle_sensors(double current_time, const capnp::List<cereal::Se
} }
} }
void Localizer::input_fake_gps_observations(double current_time) {
// This is done to make sure that the error estimate of the position does not blow up
// when the filter is in no-gps mode
// Steps : first predict -> observe current obs with reasonable STD
this->kf->predict(current_time);
VectorXd current_x = this->kf->get_x();
VectorXd ecef_pos = current_x.segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START);
VectorXd ecef_vel = current_x.segment<STATE_ECEF_VELOCITY_LEN>(STATE_ECEF_VELOCITY_START);
MatrixXdr ecef_pos_R = this->kf->get_fake_gps_pos_cov();
MatrixXdr ecef_vel_R = this->kf->get_fake_gps_vel_cov();
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R });
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R });
}
void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log) { void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log) {
// ignore the message if the fix is invalid // ignore the message if the fix is invalid
if (log.getFlags() % 2 == 0) {
return;
}
// Sanity checks
if ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0)) {
return;
}
if ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK)) { bool gps_invalid_flag = (log.getFlags() % 2 == 0);
return; bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY);
} bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0));
bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK));
bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK);
if (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK) { if (gps_invalid_flag || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane){
this->determine_gps_mode(current_time);
return; return;
} }
// Process message // Process message
this->last_gps_fix = current_time; this->last_gps_fix = current_time;
this->gps_mode = true;
Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() }; Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() };
this->converter = std::make_unique<LocalCoord>(geodetic); this->converter = std::make_unique<LocalCoord>(geodetic);
@ -273,7 +286,7 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(log.getSpeedAccuracy() * 10.0, 2)).asDiagonal(); MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(log.getSpeedAccuracy() * 10.0, 2)).asDiagonal();
this->unix_timestamp_millis = log.getTimestamp(); this->unix_timestamp_millis = log.getTimestamp();
double gps_est_error = (this->kf->get_x().head(3) - ecef_pos).norm(); double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm();
VectorXd orientation_ecef = quat2euler(vector2quat(this->kf->get_x().segment<STATE_ECEF_ORIENTATION_LEN>(STATE_ECEF_ORIENTATION_START))); VectorXd orientation_ecef = quat2euler(vector2quat(this->kf->get_x().segment<STATE_ECEF_ORIENTATION_LEN>(STATE_ECEF_ORIENTATION_START)));
VectorXd orientation_ned = ned_euler_from_ecef({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ecef); VectorXd orientation_ned = ned_euler_from_ecef({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ecef);
@ -290,11 +303,11 @@ void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::R
if (ecef_vel.norm() > 5.0 && orientation_error.norm() > 1.0) { if (ecef_vel.norm() > 5.0 && orientation_error.norm() > 1.0) {
LOGE("Locationd vs ubloxLocation orientation difference too large, kalman reset"); LOGE("Locationd vs ubloxLocation orientation difference too large, kalman reset");
this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R);
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat }); this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat });
} else if (gps_est_error > 100.0) { } else if (gps_est_error > 100.0) {
LOGE("Locationd vs ubloxLocation position difference too large, kalman reset"); LOGE("Locationd vs ubloxLocation position difference too large, kalman reset");
this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R);
} }
this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R });
@ -358,7 +371,8 @@ void Localizer::handle_live_calib(double current_time, const cereal::LiveCalibra
void Localizer::reset_kalman(double current_time) { void Localizer::reset_kalman(double current_time) {
VectorXd init_x = this->kf->get_initial_x(); VectorXd init_x = this->kf->get_initial_x();
this->reset_kalman(current_time, init_x.segment<4>(3), init_x.head(3)); MatrixXdr init_P = this->kf->get_initial_P();
this->reset_kalman(current_time, init_x, init_P);
} }
void Localizer::finite_check(double current_time) { void Localizer::finite_check(double current_time) {
@ -390,13 +404,27 @@ void Localizer::update_reset_tracker() {
} }
} }
void Localizer::reset_kalman(double current_time, VectorXd init_orient, VectorXd init_pos) { void Localizer::reset_kalman(double current_time, VectorXd init_orient, VectorXd init_pos, VectorXd init_vel, MatrixXdr init_pos_R, MatrixXdr init_vel_R) {
// too nonlinear to init on completely wrong // too nonlinear to init on completely wrong
VectorXd init_x = this->kf->get_initial_x(); VectorXd current_x = this->kf->get_x();
MatrixXdr current_P = this->kf->get_P();
MatrixXdr init_P = this->kf->get_initial_P(); MatrixXdr init_P = this->kf->get_initial_P();
init_x.segment<4>(3) = init_orient; MatrixXdr reset_orientation_P = this->kf->get_reset_orientation_P();
init_x.head(3) = init_pos; int non_ecef_state_err_len = init_P.rows() - (STATE_ECEF_POS_ERR_LEN + STATE_ECEF_ORIENTATION_ERR_LEN + STATE_ECEF_VELOCITY_ERR_LEN);
current_x.segment<STATE_ECEF_ORIENTATION_LEN>(STATE_ECEF_ORIENTATION_START) = init_orient;
current_x.segment<STATE_ECEF_VELOCITY_LEN>(STATE_ECEF_VELOCITY_START) = init_vel;
current_x.segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) = init_pos;
init_P.block<STATE_ECEF_POS_ERR_LEN, STATE_ECEF_POS_ERR_LEN>(STATE_ECEF_POS_ERR_START, STATE_ECEF_POS_ERR_START).diagonal() = init_pos_R.diagonal();
init_P.block<STATE_ECEF_ORIENTATION_ERR_LEN, STATE_ECEF_ORIENTATION_ERR_LEN>(STATE_ECEF_ORIENTATION_ERR_START, STATE_ECEF_ORIENTATION_ERR_START).diagonal() = reset_orientation_P.diagonal();
init_P.block<STATE_ECEF_VELOCITY_ERR_LEN, STATE_ECEF_VELOCITY_ERR_LEN>(STATE_ECEF_VELOCITY_ERR_START, STATE_ECEF_VELOCITY_ERR_START).diagonal() = init_vel_R.diagonal();
init_P.block(STATE_ANGULAR_VELOCITY_ERR_START, STATE_ANGULAR_VELOCITY_ERR_START, non_ecef_state_err_len, non_ecef_state_err_len).diagonal() = current_P.block(STATE_ANGULAR_VELOCITY_ERR_START, STATE_ANGULAR_VELOCITY_ERR_START, non_ecef_state_err_len, non_ecef_state_err_len).diagonal();
this->reset_kalman(current_time, current_x, init_P);
}
void Localizer::reset_kalman(double current_time, VectorXd init_x, MatrixXdr init_P) {
this->kf->init_state(init_x, init_P, current_time); this->kf->init_state(init_x, init_P, current_time);
this->last_reset_time = current_time; this->last_reset_time = current_time;
this->reset_tracker += 1.0; this->reset_tracker += 1.0;
@ -447,6 +475,22 @@ bool Localizer::isGpsOK() {
return this->kf->get_filter_time() - this->last_gps_fix < 1.0; return this->kf->get_filter_time() - this->last_gps_fix < 1.0;
} }
void Localizer::determine_gps_mode(double current_time) {
// 1. If the pos_std is greater than what's not acceptible and localizer is in gps-mode, reset to no-gps-mode
// 2. If the pos_std is greater than what's not acceptible and localizer is in no-gps-mode, fake obs
// 3. If the pos_std is smaller than what's not acceptible, let gps-mode be whatever it is
VectorXd current_pos_std = this->kf->get_P().block<STATE_ECEF_POS_ERR_LEN, STATE_ECEF_POS_ERR_LEN>(STATE_ECEF_POS_ERR_START, STATE_ECEF_POS_ERR_START).diagonal().array().sqrt();
if (current_pos_std.norm() > SANE_GPS_UNCERTAINTY){
if (this->gps_mode){
this->gps_mode = false;
this->reset_kalman(current_time);
}
else{
this->input_fake_gps_observations(current_time);
}
}
}
int Localizer::locationd_thread() { int Localizer::locationd_thread() {
const std::initializer_list<const char *> service_list = const std::initializer_list<const char *> service_list =
{ "gpsLocationExternal", "sensorEvents", "cameraOdometry", "liveCalibration", "carState" }; { "gpsLocationExternal", "sensorEvents", "cameraOdometry", "liveCalibration", "carState" };

@ -27,11 +27,13 @@ public:
int locationd_thread(); int locationd_thread();
void reset_kalman(double current_time = NAN); void reset_kalman(double current_time = NAN);
void reset_kalman(double current_time, Eigen::VectorXd init_orient, Eigen::VectorXd init_pos); void reset_kalman(double current_time, Eigen::VectorXd init_orient, Eigen::VectorXd init_pos, Eigen::VectorXd init_vel, MatrixXdr init_pos_R, MatrixXdr init_vel_R);
void reset_kalman(double current_time, Eigen::VectorXd init_x, MatrixXdr init_P);
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 isGpsOK();
void determine_gps_mode(double current_time);
kj::ArrayPtr<capnp::byte> get_message_bytes(MessageBuilder& msg_builder, uint64_t logMonoTime, kj::ArrayPtr<capnp::byte> get_message_bytes(MessageBuilder& msg_builder, uint64_t logMonoTime,
bool inputsOK, bool sensorsOK, bool gpsOK); bool inputsOK, bool sensorsOK, bool gpsOK);
@ -49,6 +51,8 @@ public:
void handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log); void handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log);
void handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log); void handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log);
void input_fake_gps_observations(double current_time);
private: private:
std::unique_ptr<LiveKalman> kf; std::unique_ptr<LiveKalman> kf;
@ -67,4 +71,5 @@ private:
double last_gps_fix = 0; double last_gps_fix = 0;
double reset_tracker = 0.0; double reset_tracker = 0.0;
bool device_fell = false; bool device_fell = false;
bool gps_mode = false;
}; };

@ -28,11 +28,14 @@ std::vector<Eigen::Map<MatrixXdr>> get_vec_mapmat(std::vector<MatrixXdr>& mat_ve
} }
LiveKalman::LiveKalman() { LiveKalman::LiveKalman() {
this->dim_state = 26; this->dim_state = live_initial_x.rows();
this->dim_state_err = 25; this->dim_state_err = live_initial_P_diag.rows();
this->initial_x = live_initial_x; this->initial_x = live_initial_x;
this->initial_P = live_initial_P_diag.asDiagonal(); this->initial_P = live_initial_P_diag.asDiagonal();
this->fake_gps_pos_cov = live_fake_gps_pos_cov_diag.asDiagonal();
this->fake_gps_vel_cov = live_fake_gps_vel_cov_diag.asDiagonal();
this->reset_orientation_P = live_reset_orientation_diag.asDiagonal();
this->Q = live_Q_diag.asDiagonal(); this->Q = live_Q_diag.asDiagonal();
for (auto& pair : live_obs_noise_diag) { for (auto& pair : live_obs_noise_diag) {
this->obs_noise[pair.first] = pair.second.asDiagonal(); this->obs_noise[pair.first] = pair.second.asDiagonal();
@ -87,6 +90,10 @@ std::optional<Estimate> LiveKalman::predict_and_observe(double t, int kind, std:
return r; return r;
} }
void LiveKalman::predict(double t) {
this->filter->predict(t);
}
Eigen::VectorXd LiveKalman::get_initial_x() { Eigen::VectorXd LiveKalman::get_initial_x() {
return this->initial_x; return this->initial_x;
} }
@ -95,6 +102,18 @@ MatrixXdr LiveKalman::get_initial_P() {
return this->initial_P; return this->initial_P;
} }
MatrixXdr LiveKalman::get_fake_gps_pos_cov() {
return this->fake_gps_pos_cov;
}
MatrixXdr LiveKalman::get_fake_gps_vel_cov() {
return this->fake_gps_vel_cov;
}
MatrixXdr LiveKalman::get_reset_orientation_P() {
return this->reset_orientation_P;
}
MatrixXdr LiveKalman::H(VectorXd in) { MatrixXdr LiveKalman::H(VectorXd in) {
assert(in.size() == 6); assert(in.size() == 6);
Matrix<double, 3, 6, Eigen::RowMajor> res; Matrix<double, 3, 6, Eigen::RowMajor> res;

@ -36,9 +36,13 @@ public:
std::optional<Estimate> predict_and_update_odo_speed(std::vector<Eigen::VectorXd> speed, double t, int kind); std::optional<Estimate> predict_and_update_odo_speed(std::vector<Eigen::VectorXd> speed, double t, int kind);
std::optional<Estimate> predict_and_update_odo_trans(std::vector<Eigen::VectorXd> trans, double t, int kind); std::optional<Estimate> predict_and_update_odo_trans(std::vector<Eigen::VectorXd> trans, double t, int kind);
std::optional<Estimate> predict_and_update_odo_rot(std::vector<Eigen::VectorXd> rot, double t, int kind); std::optional<Estimate> predict_and_update_odo_rot(std::vector<Eigen::VectorXd> rot, double t, int kind);
void predict(double t);
Eigen::VectorXd get_initial_x(); Eigen::VectorXd get_initial_x();
MatrixXdr get_initial_P(); MatrixXdr get_initial_P();
MatrixXdr get_fake_gps_pos_cov();
MatrixXdr get_fake_gps_vel_cov();
MatrixXdr get_reset_orientation_P();
MatrixXdr H(Eigen::VectorXd in); MatrixXdr H(Eigen::VectorXd in);
@ -52,6 +56,9 @@ private:
Eigen::VectorXd initial_x; Eigen::VectorXd initial_x;
MatrixXdr initial_P; MatrixXdr initial_P;
MatrixXdr fake_gps_pos_cov;
MatrixXdr fake_gps_vel_cov;
MatrixXdr reset_orientation_P;
MatrixXdr Q; // process noise MatrixXdr Q; // process noise
std::unordered_map<int, MatrixXdr> obs_noise; std::unordered_map<int, MatrixXdr> obs_noise;
}; };

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

Loading…
Cancel
Save