mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-27 05:12:06 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 13c07838e4 | |||
| 23ac14d802 | |||
| bb0f0c9c69 |
@@ -23,43 +23,56 @@ env:
|
|||||||
CI: 1
|
CI: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
generate_cereal_artifact:
|
||||||
|
name: Generate cereal validation artifacts
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Build openpilot
|
||||||
|
run: scons -j$(nproc) cereal
|
||||||
|
- name: Generate the log file
|
||||||
|
run: |
|
||||||
|
export PYTHONPATH=${{ github.workspace }}
|
||||||
|
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin
|
||||||
|
- name: 'Prepare artifact'
|
||||||
|
run: |
|
||||||
|
mkdir -p "cereal/messaging/tests/cereal_validations"
|
||||||
|
cp cereal/messaging/tests/validate_sp_cereal_upstream.py "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py"
|
||||||
|
cp schema_instances.bin "cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
||||||
|
- name: 'Upload Artifact'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: cereal_validations
|
||||||
|
path: cereal/messaging/tests/cereal_validations
|
||||||
|
|
||||||
validate_cereal_with_upstream:
|
validate_cereal_with_upstream:
|
||||||
name: Validate cereal with Upstream
|
name: Validate cereal with Upstream
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
needs: generate_cereal_artifact
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sunnypilot cereal
|
- name: Checkout sunnypilot
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
- name: Checkout upstream openpilot
|
||||||
sparse-checkout: cereal
|
|
||||||
|
|
||||||
- name: Init sunnypilot opendbc submodule
|
|
||||||
run: git submodule update --init --depth 1 opendbc_repo
|
|
||||||
|
|
||||||
- name: Checkout upstream openpilot cereal
|
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
repository: 'commaai/openpilot'
|
repository: 'commaai/openpilot'
|
||||||
path: upstream_openpilot
|
path: openpilot
|
||||||
sparse-checkout: cereal
|
submodules: true
|
||||||
ref: "refs/heads/master"
|
ref: "refs/heads/master"
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
- name: Init upstream opendbc submodule
|
- name: Build openpilot
|
||||||
working-directory: upstream_openpilot
|
working-directory: openpilot
|
||||||
run: git submodule update --init --depth 1 opendbc_repo
|
run: scons -j$(nproc) cereal
|
||||||
|
- name: Download build artifacts
|
||||||
- name: Install uv
|
uses: actions/download-artifact@v4
|
||||||
run: pip install uv
|
with:
|
||||||
|
name: cereal_validations
|
||||||
- name: Generate sunnypilot schema
|
path: openpilot/cereal/messaging/tests/cereal_validations
|
||||||
|
- name: 'Run the validation'
|
||||||
run: |
|
run: |
|
||||||
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
|
export PYTHONPATH=${{ github.workspace }}/openpilot
|
||||||
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
|
chmod +x openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
||||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
|
python3 openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f openpilot/cereal/messaging/tests/cereal_validations/schema_instances.bin
|
||||||
-g -f /tmp/sp_schema.json --cereal-dir cereal
|
|
||||||
|
|
||||||
- name: Validate against upstream
|
|
||||||
run: |
|
|
||||||
PYCAPNP_VER=$(python3 -c "import re; m=re.search(r'name = \"pycapnp\"\nversion = \"([^\"]+)\"', open('uv.lock').read()); print(m.group(1))")
|
|
||||||
uv run --isolated --with "pycapnp==${PYCAPNP_VER}" \
|
|
||||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py \
|
|
||||||
-r -f /tmp/sp_schema.json --cereal-dir upstream_openpilot/cereal
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
name: diff report
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment:
|
|
||||||
name: comment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
actions: read
|
|
||||||
steps:
|
|
||||||
- name: Wait for process replay
|
|
||||||
id: wait
|
|
||||||
continue-on-error: true
|
|
||||||
uses: lewagon/wait-on-check-action@v1.3.4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
check-name: process replay
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
allowed-conclusions: success,failure
|
|
||||||
wait-interval: 20
|
|
||||||
- name: Download diff
|
|
||||||
if: steps.wait.outcome == 'success'
|
|
||||||
uses: dawidd6/action-download-artifact@v6
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
workflow: tests.yaml
|
|
||||||
workflow_conclusion: ''
|
|
||||||
pr: ${{ github.event.number }}
|
|
||||||
name: diff_report_${{ github.event.number }}
|
|
||||||
path: .
|
|
||||||
allow_forks: true
|
|
||||||
- name: Comment on PR
|
|
||||||
if: steps.wait.outcome == 'success'
|
|
||||||
uses: thollander/actions-comment-pull-request@v2
|
|
||||||
with:
|
|
||||||
filePath: diff_report.txt
|
|
||||||
comment_tag: diff_report
|
|
||||||
pr_number: ${{ github.event.number }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -29,9 +29,9 @@ jobs:
|
|||||||
# Build
|
# Build
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
run: |
|
run: |
|
||||||
git lfs pull
|
# TODO: can we install just the "docs" dependency group without the normal deps?
|
||||||
pip install zensical
|
pip install mkdocs
|
||||||
python scripts/docs.py build
|
mkdocs build
|
||||||
|
|
||||||
# Push to docs.comma.ai
|
# Push to docs.comma.ai
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|||||||
@@ -9,6 +9,28 @@ env:
|
|||||||
PYTHONPATH: ${{ github.workspace }}
|
PYTHONPATH: ${{ github.workspace }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
update_translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- run: ./tools/op.sh setup
|
||||||
|
- name: Update translations
|
||||||
|
run: python3 selfdrive/ui/update_translations.py --vanish
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||||
|
with:
|
||||||
|
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||||
|
commit-message: "Update translations"
|
||||||
|
title: "[bot] Update translations"
|
||||||
|
body: "Automatic PR from repo-maintenance -> update_translations"
|
||||||
|
branch: "update-translations"
|
||||||
|
base: "master"
|
||||||
|
delete-branch: true
|
||||||
|
labels: bot
|
||||||
|
|
||||||
package_updates:
|
package_updates:
|
||||||
name: package_updates
|
name: package_updates
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ jobs:
|
|||||||
echo "Building sunnypilot's locationd..."
|
echo "Building sunnypilot's locationd..."
|
||||||
scons -j2 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/selfdrive/locationd
|
scons -j2 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/selfdrive/locationd
|
||||||
echo "Building openpilot's locationd..."
|
echo "Building openpilot's locationd..."
|
||||||
scons -j1 cache_dir=${{env.SCONS_CACHE_DIR}} --minimal selfdrive/locationd
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal selfdrive/locationd
|
||||||
echo "Building rest of sunnypilot"
|
echo "Building rest of sunnypilot"
|
||||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal
|
||||||
touch ${BUILD_DIR}/prebuilt
|
touch ${BUILD_DIR}/prebuilt
|
||||||
|
|||||||
@@ -156,22 +156,12 @@ jobs:
|
|||||||
id: print-diff
|
id: print-diff
|
||||||
if: always()
|
if: always()
|
||||||
run: cat selfdrive/test/process_replay/diff.txt
|
run: cat selfdrive/test/process_replay/diff.txt
|
||||||
- name: Print diff report
|
|
||||||
if: always()
|
|
||||||
run: cat selfdrive/test/process_replay/diff_report.txt
|
|
||||||
- uses: actions/upload-artifact@v6
|
- uses: actions/upload-artifact@v6
|
||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: process_replay_diff.txt
|
name: process_replay_diff.txt
|
||||||
path: selfdrive/test/process_replay/diff.txt
|
path: selfdrive/test/process_replay/diff.txt
|
||||||
- name: Upload diff report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
if: always() && github.event_name == 'pull_request'
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
name: diff_report_${{ github.event.number }}
|
|
||||||
path: selfdrive/test/process_replay/diff_report.txt
|
|
||||||
- name: Checkout ci-artifacts
|
- name: Checkout ci-artifacts
|
||||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -44,10 +44,8 @@ bin/
|
|||||||
config.json
|
config.json
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
compare_runtime*.html
|
compare_runtime*.html
|
||||||
selfdrive/modeld/models/tg_compiled_flags.json
|
|
||||||
|
|
||||||
# build artifacts
|
# build artifacts
|
||||||
docs_site/
|
|
||||||
selfdrive/pandad/pandad
|
selfdrive/pandad/pandad
|
||||||
cereal/services.h
|
cereal/services.h
|
||||||
cereal/gen
|
cereal/gen
|
||||||
|
|||||||
Vendored
-3
@@ -52,9 +52,6 @@
|
|||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"pid": "${command:pickMyProcess}",
|
"pid": "${command:pickMyProcess}",
|
||||||
"sourceMap": {
|
|
||||||
".": "${workspaceFolder}/opendbc/safety"
|
|
||||||
},
|
|
||||||
"initCommands": [
|
"initCommands": [
|
||||||
"script import time; time.sleep(3)"
|
"script import time; time.sleep(3)"
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-89
@@ -1,7 +1,4 @@
|
|||||||
sunnypilot Version 2026.002.000 (2026-xx-xx)
|
sunnypilot Version 2026.001.000 (2026-03-xx)
|
||||||
========================
|
|
||||||
|
|
||||||
sunnypilot Version 2026.001.000 (2026-05-06)
|
|
||||||
========================
|
========================
|
||||||
* What's Changed (sunnypilot/sunnypilot)
|
* What's Changed (sunnypilot/sunnypilot)
|
||||||
* Complete rewrite of the user interface from Qt C++ to Raylib Python
|
* Complete rewrite of the user interface from Qt C++ to Raylib Python
|
||||||
@@ -69,64 +66,6 @@ sunnypilot Version 2026.001.000 (2026-05-06)
|
|||||||
* Pause Lateral Control with Blinker: Post-Blinker Delay by @CHaucke89
|
* Pause Lateral Control with Blinker: Post-Blinker Delay by @CHaucke89
|
||||||
* SCC-V: Use p97 for predicted lateral accel by @yasu-oh
|
* SCC-V: Use p97 for predicted lateral accel by @yasu-oh
|
||||||
* Controls: Support for Torque Lateral Control v0 Tune by @sunnyhaibin
|
* Controls: Support for Torque Lateral Control v0 Tune by @sunnyhaibin
|
||||||
* [TIZI/TICI] ui: ensure null checks for `CarParams` and `CarParamsSP` by @sunnyhaibin
|
|
||||||
* [TIZI/TICI] ui: use `vCruiseCluster` and `vEgoCluster` for SLA `preActive` by @sunnyhaibin
|
|
||||||
* Fix display of values when using use_float_scaling by @CHaucke89
|
|
||||||
* models: fix default & index "0" by @nayan8teen
|
|
||||||
* [TIZI/TICI] visuals: Improved speed limit by @angaz
|
|
||||||
* ICBM: ensure button timers update on disable to clear stale presses by @jamesmikesell
|
|
||||||
* [TIZI/TICI] ui: simplify Smart Cruise Control text rendering by @sunnyhaibin
|
|
||||||
* controlsd: fix steer_limited_by_safety not updating under MADS by @zephleggett
|
|
||||||
* soundd: trigger timeout warning during MADS lateral-only by @zephleggett
|
|
||||||
* pandad: flasher for Rivian long upgrade module by @lukasloetkolben
|
|
||||||
* modeld_v2: tinygrad transformation warp by @Discountchubbs
|
|
||||||
* tools: block `manage_sunnylinkd` in sim startup script by @sunnyhaibin
|
|
||||||
* [MICI] ui: need superclass `_render` in `HudRendererSP` by @sunnyhaibin
|
|
||||||
* [TIZI/TICI] ui: Speed Limit Assist active status by @sunnyhaibin
|
|
||||||
* ui: reimplement "Screen Off" option to Onroad Brightness by @sunnyhaibin
|
|
||||||
* ui: don't hide steering wheel when blindspot disabled by @royjr
|
|
||||||
* ui: Speed Limit Assist `preActive` improvements by @sunnyhaibin
|
|
||||||
* ui: consolidate Speed Limit Assist `preActive` status rendering by @sunnyhaibin
|
|
||||||
* [MICI] ui: Speed Limit Assist `preActive` status by @sunnyhaibin
|
|
||||||
* sunnypilot modeld: remove thneed modeld by @Discountchubbs
|
|
||||||
* modeld_v2: decouple planplus scaling from accel by @Discountchubbs
|
|
||||||
* sunnylink: Handle exceptions in `getParamsAllKeysV1` to log crashes by @devtekve
|
|
||||||
* [TIZI/TICI] ui: Developer UI cleanup by @sunnyhaibin
|
|
||||||
* [TIZI/TICI] ui: dynamic alert size by @nayan8teen
|
|
||||||
* i18n(fr): Add French translations by @didlawowo
|
|
||||||
* Toyota: Stop and Go Hack (Alpha) by @sunnyhaibin
|
|
||||||
* ui: `AlertFadeAnimator` for longitudinal-related statuses by @sunnyhaibin
|
|
||||||
* pandad: gate unsupported pandas before flashing by @sunnyhaibin
|
|
||||||
* Rivian: Flash xnor's Longitudinal Upgrade Kit prior supported panda check by @lukasloetkolben
|
|
||||||
* [TIZI/TICI] ui: add back gate steering arc behind toggle by @sunnyhaibin
|
|
||||||
* ui: gate Onroad Brightness Delay on readiness by @sunnyhaibin
|
|
||||||
* ui: add new timer options for Onroad Brightness Delay by @sunnyhaibin
|
|
||||||
* [TIZI/TICI] ui: branch switcher is always available by @sunnyhaibin
|
|
||||||
* pandad: always prioritize internal panda by @sunnyhaibin
|
|
||||||
* sunnylinkd: fetch compressed params schema by @sunnyhaibin
|
|
||||||
* sunnypilot locationd: remove unused car_ekf filter by @sunnyhaibin
|
|
||||||
* modeld_v2: update deprecated temporalPose ref by @sunnyhaibin
|
|
||||||
* NNLC: restore pre-v1 PID gains in torque extension by @mmmorks
|
|
||||||
* MADS safety: enable heartbeat and lateral controls mismatch checks by @sunnyhaibin
|
|
||||||
* [MICI] ui: models panel enhancements by @nayan8teen
|
|
||||||
* [TIZI/TICI] ui: fix unintended selection while scrolling in TreeOptionDialog by @TheSecurityDev
|
|
||||||
* tools: script for video concatenation by @Discountchubbs
|
|
||||||
* tools: profile memory usage by @Discountchubbs
|
|
||||||
* [TIZI/TICI] ui: remove per-frame param sync by @sunnyhaibin
|
|
||||||
* [MICI] ui: always offroad by @nayan8teen
|
|
||||||
* controls: always default Torque Lateral Control to v0 Tune by @sunnyhaibin
|
|
||||||
* Revert "controls: always default Torque Lateral Control to v0 Tune" by @sunnyhaibin
|
|
||||||
* Reapply "controls: always default Torque Lateral Control to v0 Tune" (#1806) by @sunnyhaibin
|
|
||||||
* [MICI] ui: add sunnylink info & connectivity check by @nayan8teen
|
|
||||||
* sunnylink: Remove unused API endpoint by @devtekve
|
|
||||||
* DM: wheel touch enforcement in MADS by @sunnyhaibin
|
|
||||||
* torque: show static override values in Dev UI & gate `useParams` on custom torque tune by @sunnyhaibin
|
|
||||||
* MADS: suppress espActive event when long is not engaged by @sunnyhaibin
|
|
||||||
* sunnylink: SDUI by @sunnyhaibin
|
|
||||||
* [MICI] ui: align upstream changes with sunnypilot settings buttons by @nayan8teen
|
|
||||||
* ui: fix cellular toggles by @AmyJeanes
|
|
||||||
* sunnylink: switch athena domain by @devtekve
|
|
||||||
* Platform List: dynamically migrate CarPlatformBundle by @sunnyhaibin
|
|
||||||
* What's Changed (sunnypilot/opendbc)
|
* What's Changed (sunnypilot/opendbc)
|
||||||
* Honda: DBC for Accord 9th Generation by @mvl-boston
|
* Honda: DBC for Accord 9th Generation by @mvl-boston
|
||||||
* FCA: update tire stiffness values for `RAM_HD` by @dparring
|
* FCA: update tire stiffness values for `RAM_HD` by @dparring
|
||||||
@@ -145,25 +84,12 @@ sunnypilot Version 2026.001.000 (2026-05-06)
|
|||||||
* Honda: add missing `GasInterceptor` messages to Taiwan Odyssey DBC by @mvl-boston
|
* Honda: add missing `GasInterceptor` messages to Taiwan Odyssey DBC by @mvl-boston
|
||||||
* GM: remove `CHEVROLET_EQUINOX_NON_ACC_3RD_GEN` from `dashcamOnly` by @sunnyhaibin
|
* GM: remove `CHEVROLET_EQUINOX_NON_ACC_3RD_GEN` from `dashcamOnly` by @sunnyhaibin
|
||||||
* GM: remove `CHEVROLET_BOLT_NON_ACC_2ND_GEN` from `dashcamOnly` by @sunnyhaibin
|
* GM: remove `CHEVROLET_BOLT_NON_ACC_2ND_GEN` from `dashcamOnly` by @sunnyhaibin
|
||||||
* Hyundai Longitudinal: deprecate ramp update for dynamic tune by @Discountchubbs
|
|
||||||
* Rivian: long upgrade messages on bus 1 by @lukasloetkolben
|
|
||||||
* Toyota: Stop and Go Hack (Alpha) by @sunnyhaibin
|
|
||||||
* Toyota: gate Smart DSU behind Alpha Longitudinal by @sunnyhaibin
|
|
||||||
* Toyota: Gas Interceptor always set `standstill_req` by @sunnyhaibin
|
|
||||||
* MADS safety: dedicated `controls_allowed_lateral` by @sunnyhaibin
|
|
||||||
* Platform List: include community supported platforms by @sunnyhaibin
|
|
||||||
* New Contributors (sunnypilot/sunnypilot)
|
* New Contributors (sunnypilot/sunnypilot)
|
||||||
* @TheSecurityDev made their first contribution in "ui: fix sidebar scroll in UI screenshots"
|
* @TheSecurityDev made their first contribution in "ui: fix sidebar scroll in UI screenshots"
|
||||||
* @zikeji made their first contribution in "sunnylink: block remote modification of SSH key parameters"
|
* @zikeji made their first contribution in "sunnylink: block remote modification of SSH key parameters"
|
||||||
* @Candy0707 made their first contribution in "[TIZI/TICI] ui: Fix misaligned turn signals and blindspot indicators with sidebar"
|
* @Candy0707 made their first contribution in "[TIZI/TICI] ui: Fix misaligned turn signals and blindspot indicators with sidebar"
|
||||||
* @CHaucke89 made their first contribution in "Pause Lateral Control with Blinker: Post-Blinker Delay"
|
* @CHaucke89 made their first contribution in "Pause Lateral Control with Blinker: Post-Blinker Delay"
|
||||||
* @yasu-oh made their first contribution in "SCC-V: Use p97 for predicted lateral accel"
|
* @yasu-oh made their first contribution in "SCC-V: Use p97 for predicted lateral accel"
|
||||||
* @angaz made their first contribution in "[TIZI/TICI] visuals: Improved speed limit"
|
|
||||||
* @jamesmikesell made their first contribution in "ICBM: ensure button timers update on disable to clear stale presses"
|
|
||||||
* @zephleggett made their first contribution in "controlsd: fix steer_limited_by_safety not updating under MADS"
|
|
||||||
* @lukasloetkolben made their first contribution in "pandad: flasher for Rivian long upgrade module"
|
|
||||||
* @didlawowo made their first contribution in "i18n(fr): Add French translations"
|
|
||||||
* @mmmorks made their first contribution in "NNLC: restore pre-v1 PID gains in torque extension"
|
|
||||||
* New Contributors (sunnypilot/opendbc)
|
* New Contributors (sunnypilot/opendbc)
|
||||||
* @AmyJeanes made their first contribution in "Tesla: Fix stock LKAS being blocked when MADS is enabled"
|
* @AmyJeanes made their first contribution in "Tesla: Fix stock LKAS being blocked when MADS is enabled"
|
||||||
* @mvl-boston made their first contribution in "Honda: Update Clarity brake to renamed DBC message name"
|
* @mvl-boston made their first contribution in "Honda: Update Clarity brake to renamed DBC message name"
|
||||||
@@ -173,20 +99,6 @@ sunnypilot Version 2026.001.000 (2026-05-06)
|
|||||||
* @royjr made their first contribution in "HKG: add KIA_FORTE_2019_NON_SCC fingerprint"
|
* @royjr made their first contribution in "HKG: add KIA_FORTE_2019_NON_SCC fingerprint"
|
||||||
* @ssysm made their first contribution in "Tesla: remove `TESLA_MODEL_X` from `dashcamOnly`"
|
* @ssysm made their first contribution in "Tesla: remove `TESLA_MODEL_X` from `dashcamOnly`"
|
||||||
* Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2025.002.000...v2026.001.000
|
* Full Changelog: https://github.com/sunnypilot/sunnypilot/compare/v2025.002.000...v2026.001.000
|
||||||
************************
|
|
||||||
* Synced with commaai's openpilot (v0.11.1)
|
|
||||||
* master commit c001f3c9b490a80e69539f0af6022f6e07ceb721 (April 16, 2026)
|
|
||||||
* New driver monitoring model
|
|
||||||
* Improved image processing pipeline for driver camera
|
|
||||||
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
|
|
||||||
* New driving model #36798
|
|
||||||
* Fully trained using a learned simulator
|
|
||||||
* Improved longitudinal performance in Experimental mode
|
|
||||||
* Reduce comma four standby power usage by 77% to 52 mW
|
|
||||||
* Kia K7 2017 support thanks to royjr!
|
|
||||||
* Lexus LS 2018 support thanks to Hacheoy!
|
|
||||||
* Improved inter-process communication memory efficiency
|
|
||||||
* comma four support
|
|
||||||
|
|
||||||
sunnypilot Version 2025.002.000 (2025-11-06)
|
sunnypilot Version 2025.002.000 (2025-11-06)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+1
-2
@@ -1,8 +1,7 @@
|
|||||||
Version 0.11.1 (2026-04-22)
|
Version 0.11.1 (2026-04-08)
|
||||||
========================
|
========================
|
||||||
* New driver monitoring model
|
* New driver monitoring model
|
||||||
* Improved image processing pipeline for driver camera
|
* Improved image processing pipeline for driver camera
|
||||||
* Rivian R1S and R1T 2025 support thanks to lukasloetkolben!
|
|
||||||
|
|
||||||
Version 0.11.0 (2026-03-17)
|
Version 0.11.0 (2026-03-17)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+3
-9
@@ -47,8 +47,7 @@ pkgs = [importlib.import_module(name) for name in pkg_names]
|
|||||||
# be distributed with all Linux distros and macOS, or
|
# be distributed with all Linux distros and macOS, or
|
||||||
# vendored in commaai/dependencies.
|
# vendored in commaai/dependencies.
|
||||||
allowed_system_libs = {
|
allowed_system_libs = {
|
||||||
"EGL", "GLESv2", "GL",
|
"EGL", "GLESv2", "GL", "Qt5Charts", "Qt5Core", "Qt5Gui", "Qt5Widgets",
|
||||||
"Qt5Charts", "Qt5Core", "Qt5Gui", "Qt5Widgets",
|
|
||||||
"dl", "drm", "gbm", "m", "pthread",
|
"dl", "drm", "gbm", "m", "pthread",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,13 +256,8 @@ SConscript([
|
|||||||
|
|
||||||
SConscript(['sunnypilot/SConscript'])
|
SConscript(['sunnypilot/SConscript'])
|
||||||
|
|
||||||
# Build tools
|
if Dir('#tools/cabana/').exists() and arch != "larch64":
|
||||||
if arch != "larch64":
|
SConscript(['tools/cabana/SConscript'])
|
||||||
SConscript([
|
|
||||||
'tools/replay/SConscript',
|
|
||||||
'tools/cabana/SConscript',
|
|
||||||
'tools/jotpluggler/SConscript',
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
env.CompilationDatabase('compile_commands.json')
|
env.CompilationDatabase('compile_commands.json')
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ cereal_dir = Dir('.')
|
|||||||
gen_dir = Dir('gen')
|
gen_dir = Dir('gen')
|
||||||
|
|
||||||
# Build cereal
|
# Build cereal
|
||||||
schema_files = ['log.capnp', 'car.capnp', 'deprecated.capnp', 'custom.capnp']
|
schema_files = ['log.capnp', 'car.capnp', 'legacy.capnp', 'custom.capnp']
|
||||||
env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files],
|
env.Command([f'gen/cpp/{s}.c++' for s in schema_files] + [f'gen/cpp/{s}.h' for s in schema_files],
|
||||||
schema_files,
|
schema_files,
|
||||||
f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/")
|
f"capnpc --src-prefix={cereal_dir.path} $SOURCES -o c++:{gen_dir.path}/cpp/")
|
||||||
|
|||||||
@@ -154,7 +154,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
|
|||||||
vision @2;
|
vision @2;
|
||||||
policy @3;
|
policy @3;
|
||||||
offPolicy @4;
|
offPolicy @4;
|
||||||
onPolicy @5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,13 +193,6 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
|
|||||||
aTarget @5 :Float32;
|
aTarget @5 :Float32;
|
||||||
events @6 :List(OnroadEventSP.Event);
|
events @6 :List(OnroadEventSP.Event);
|
||||||
e2eAlerts @7 :E2eAlerts;
|
e2eAlerts @7 :E2eAlerts;
|
||||||
accelPersonality @8 :AccelerationPersonality;
|
|
||||||
|
|
||||||
enum AccelerationPersonality {
|
|
||||||
sport @0;
|
|
||||||
normal @1;
|
|
||||||
eco @2;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DynamicExperimentalControl {
|
struct DynamicExperimentalControl {
|
||||||
state @0 :DynamicExperimentalControlState;
|
state @0 :DynamicExperimentalControlState;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ $Cxx.namespace("cereal");
|
|||||||
|
|
||||||
@0x80ef1ec4889c2a63;
|
@0x80ef1ec4889c2a63;
|
||||||
|
|
||||||
# deprecated.capnp: a home for deprecated structs
|
# legacy.capnp: a home for deprecated structs
|
||||||
|
|
||||||
struct LogRotate @0x9811e1f38f62f2d1 {
|
struct LogRotate @0x9811e1f38f62f2d1 {
|
||||||
segmentNum @0 :Int32;
|
segmentNum @0 :Int32;
|
||||||
@@ -571,219 +571,4 @@ struct LidarPts @0xe3d6685d4e9d8f7a {
|
|||||||
pkt @4 :Data;
|
pkt @4 :Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LiveTracksDEPRECATED @0xb16f60103159415a {
|
|
||||||
trackId @0 :Int32;
|
|
||||||
dRel @1 :Float32;
|
|
||||||
yRel @2 :Float32;
|
|
||||||
vRel @3 :Float32;
|
|
||||||
aRel @4 :Float32;
|
|
||||||
timeStamp @5 :Float32;
|
|
||||||
status @6 :Float32;
|
|
||||||
currentTime @7 :Float32;
|
|
||||||
stationary @8 :Bool;
|
|
||||||
oncoming @9 :Bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LiveMpcData @0x92a5e332a85f32a0 {
|
|
||||||
x @0 :List(Float32);
|
|
||||||
y @1 :List(Float32);
|
|
||||||
psi @2 :List(Float32);
|
|
||||||
curvature @3 :List(Float32);
|
|
||||||
qpIterations @4 :UInt32;
|
|
||||||
calculationTime @5 :UInt64;
|
|
||||||
cost @6 :Float64;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LiveLongitudinalMpcData @0xe7e17c434f865ae2 {
|
|
||||||
xEgo @0 :List(Float32);
|
|
||||||
vEgo @1 :List(Float32);
|
|
||||||
aEgo @2 :List(Float32);
|
|
||||||
xLead @3 :List(Float32);
|
|
||||||
vLead @4 :List(Float32);
|
|
||||||
aLead @5 :List(Float32);
|
|
||||||
aLeadTau @6 :Float32; # lead accel time constant
|
|
||||||
qpIterations @7 :UInt32;
|
|
||||||
mpcId @8 :UInt32;
|
|
||||||
calculationTime @9 :UInt64;
|
|
||||||
cost @10 :Float64;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
|
||||||
frameId @0 :UInt32;
|
|
||||||
modelExecutionTime @14 :Float32;
|
|
||||||
dspExecutionTime @16 :Float32;
|
|
||||||
rawPredictions @15 :Data;
|
|
||||||
|
|
||||||
faceOrientation @3 :List(Float32);
|
|
||||||
facePosition @4 :List(Float32);
|
|
||||||
faceProb @5 :Float32;
|
|
||||||
leftEyeProb @6 :Float32;
|
|
||||||
rightEyeProb @7 :Float32;
|
|
||||||
leftBlinkProb @8 :Float32;
|
|
||||||
rightBlinkProb @9 :Float32;
|
|
||||||
faceOrientationStd @11 :List(Float32);
|
|
||||||
facePositionStd @12 :List(Float32);
|
|
||||||
sunglassesProb @13 :Float32;
|
|
||||||
poorVision @17 :Float32;
|
|
||||||
partialFace @18 :Float32;
|
|
||||||
distractedPose @19 :Float32;
|
|
||||||
distractedEyes @20 :Float32;
|
|
||||||
eyesOnRoad @21 :Float32;
|
|
||||||
phoneUse @22 :Float32;
|
|
||||||
occludedProb @23 :Float32;
|
|
||||||
|
|
||||||
readyProb @24 :List(Float32);
|
|
||||||
notReadyProb @25 :List(Float32);
|
|
||||||
|
|
||||||
irPwrDEPRECATED @10 :Float32;
|
|
||||||
descriptorDEPRECATED @1 :List(Float32);
|
|
||||||
stdDEPRECATED @2 :Float32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NavModelData @0xac3de5c437be057a {
|
|
||||||
frameId @0 :UInt32;
|
|
||||||
locationMonoTime @6 :UInt64;
|
|
||||||
modelExecutionTime @1 :Float32;
|
|
||||||
dspExecutionTime @2 :Float32;
|
|
||||||
features @3 :List(Float32);
|
|
||||||
# predicted future position
|
|
||||||
position @4 :XYData;
|
|
||||||
desirePrediction @5 :List(Float32);
|
|
||||||
|
|
||||||
# All SI units and in device frame
|
|
||||||
struct XYData @0xbe09e615b2507e26 {
|
|
||||||
x @0 :List(Float32);
|
|
||||||
y @1 :List(Float32);
|
|
||||||
xStd @2 :List(Float32);
|
|
||||||
yStd @3 :List(Float32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AndroidBuildInfo @0xfe2919d5c21f426c {
|
|
||||||
board @0 :Text;
|
|
||||||
bootloader @1 :Text;
|
|
||||||
brand @2 :Text;
|
|
||||||
device @3 :Text;
|
|
||||||
display @4 :Text;
|
|
||||||
fingerprint @5 :Text;
|
|
||||||
hardware @6 :Text;
|
|
||||||
host @7 :Text;
|
|
||||||
id @8 :Text;
|
|
||||||
manufacturer @9 :Text;
|
|
||||||
model @10 :Text;
|
|
||||||
product @11 :Text;
|
|
||||||
radioVersion @12 :Text;
|
|
||||||
serial @13 :Text;
|
|
||||||
supportedAbis @14 :List(Text);
|
|
||||||
tags @15 :Text;
|
|
||||||
time @16 :Int64;
|
|
||||||
type @17 :Text;
|
|
||||||
user @18 :Text;
|
|
||||||
|
|
||||||
versionCodename @19 :Text;
|
|
||||||
versionRelease @20 :Text;
|
|
||||||
versionSdk @21 :Int32;
|
|
||||||
versionSecurityPatch @22 :Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AndroidSensor @0x9b513b93a887dbcd {
|
|
||||||
id @0 :Int32;
|
|
||||||
name @1 :Text;
|
|
||||||
vendor @2 :Text;
|
|
||||||
version @3 :Int32;
|
|
||||||
handle @4 :Int32;
|
|
||||||
type @5 :Int32;
|
|
||||||
maxRange @6 :Float32;
|
|
||||||
resolution @7 :Float32;
|
|
||||||
power @8 :Float32;
|
|
||||||
minDelay @9 :Int32;
|
|
||||||
fifoReservedEventCount @10 :UInt32;
|
|
||||||
fifoMaxEventCount @11 :UInt32;
|
|
||||||
stringType @12 :Text;
|
|
||||||
maxDelay @13 :Int32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IosBuildInfo @0xd97e3b28239f5580 {
|
|
||||||
appVersion @0 :Text;
|
|
||||||
appBuild @1 :UInt32;
|
|
||||||
osVersion @2 :Text;
|
|
||||||
deviceModel @3 :Text;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FrameTypeDEPRECATED @0xa37f0d8558e193fd {
|
|
||||||
unknown @0;
|
|
||||||
neo @1;
|
|
||||||
chffrAndroid @2;
|
|
||||||
front @3;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AndroidCaptureResult @0xbcc3efbac41d2048 {
|
|
||||||
sensitivity @0 :Int32;
|
|
||||||
frameDuration @1 :Int64;
|
|
||||||
exposureTime @2 :Int64;
|
|
||||||
rollingShutterSkew @3 :UInt64;
|
|
||||||
colorCorrectionTransform @4 :List(Int32);
|
|
||||||
colorCorrectionGains @5 :List(Float32);
|
|
||||||
displayRotation @6 :Int8;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UsbPowerModeDEPRECATED @0xa8883583b32c9877 {
|
|
||||||
none @0;
|
|
||||||
client @1;
|
|
||||||
cdp @2;
|
|
||||||
dcp @3;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LateralINDIState @0x939463348632375e {
|
|
||||||
active @0 :Bool;
|
|
||||||
steeringAngleDeg @1 :Float32;
|
|
||||||
steeringRateDeg @2 :Float32;
|
|
||||||
steeringAccelDeg @3 :Float32;
|
|
||||||
rateSetPoint @4 :Float32;
|
|
||||||
accelSetPoint @5 :Float32;
|
|
||||||
accelError @6 :Float32;
|
|
||||||
delayedOutput @7 :Float32;
|
|
||||||
delta @8 :Float32;
|
|
||||||
output @9 :Float32;
|
|
||||||
saturated @10 :Bool;
|
|
||||||
steeringAngleDesiredDeg @11 :Float32;
|
|
||||||
steeringRateDesiredDeg @12 :Float32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LateralLQRState @0x9024e2d790c82ade {
|
|
||||||
active @0 :Bool;
|
|
||||||
steeringAngleDeg @1 :Float32;
|
|
||||||
i @2 :Float32;
|
|
||||||
output @3 :Float32;
|
|
||||||
lqrOutput @4 :Float32;
|
|
||||||
saturated @5 :Bool;
|
|
||||||
steeringAngleDesiredDeg @6 :Float32;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LateralCurvatureState @0xad9d8095c06f7c61 {
|
|
||||||
active @0 :Bool;
|
|
||||||
actualCurvature @1 :Float32;
|
|
||||||
desiredCurvature @2 :Float32;
|
|
||||||
error @3 :Float32;
|
|
||||||
p @4 :Float32;
|
|
||||||
i @5 :Float32;
|
|
||||||
f @6 :Float32;
|
|
||||||
output @7 :Float32;
|
|
||||||
saturated @8 :Bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LateralPlannerSolution @0x84caeca5a6b4acfe {
|
|
||||||
x @0 :List(Float32);
|
|
||||||
y @1 :List(Float32);
|
|
||||||
yaw @2 :List(Float32);
|
|
||||||
yawRate @3 :List(Float32);
|
|
||||||
xStd @4 :List(Float32);
|
|
||||||
yStd @5 :List(Float32);
|
|
||||||
yawStd @6 :List(Float32);
|
|
||||||
yawRateStd @7 :List(Float32);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GpsTrajectory @0x8cfeb072f5301000 {
|
|
||||||
x @0 :List(Float32);
|
|
||||||
y @1 :List(Float32);
|
|
||||||
}
|
|
||||||
+454
-304
File diff suppressed because it is too large
Load Diff
@@ -1,256 +1,222 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Schema-level cereal compat check between sunnypilot and upstream openpilot.
|
|
||||||
|
|
||||||
Rules (per struct matched across sides by typeId):
|
|
||||||
R1 shared ordinal must reference the same type.
|
|
||||||
R2 sunnypilot-only ordinal in a union -> FAIL (unknown discriminant upstream).
|
|
||||||
R3 sunnypilot-only ordinal on a regular field -> OK (additive struct evolution).
|
|
||||||
R4 upstream-only ordinal -> OK.
|
|
||||||
R5 sunnypilot-only struct referenced via an upstream-shared field -> FAIL.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Any
|
from typing import Any, List, Tuple
|
||||||
|
|
||||||
NO_DISCRIMINANT = 0xFFFF
|
DEBUG = False
|
||||||
|
|
||||||
|
|
||||||
def hex_id(value: int) -> str:
|
def print_debug(string: str) -> None:
|
||||||
return f"0x{value:016x}"
|
if DEBUG:
|
||||||
|
print(string)
|
||||||
|
|
||||||
|
|
||||||
def encode_type(type_node: Any) -> dict:
|
def create_schema_instance(struct: Any, prop: Tuple[str, Any]) -> Any:
|
||||||
which = type_node.which()
|
"""
|
||||||
if which == "struct":
|
Create a new instance of a schema type, handling different field types.
|
||||||
return {"kind": "struct", "typeId": hex_id(type_node.struct.typeId)}
|
|
||||||
if which == "enum":
|
Args:
|
||||||
return {"kind": "enum", "typeId": hex_id(type_node.enum.typeId)}
|
struct: The Cap'n Proto schema structure
|
||||||
if which == "interface":
|
prop: A tuple containing the field name and field metadata
|
||||||
return {"kind": "interface", "typeId": hex_id(type_node.interface.typeId)}
|
|
||||||
if which == "list":
|
Returns:
|
||||||
return {"kind": "list", "element": encode_type(type_node.list.elementType)}
|
A new initialized schema instance
|
||||||
if which == "anyPointer":
|
"""
|
||||||
return {"kind": "anyPointer"}
|
struct_instance = struct.new_message()
|
||||||
return {"kind": which}
|
field_name, field_metadata = prop
|
||||||
|
|
||||||
|
try:
|
||||||
|
field_type = field_metadata.proto.slot.type.which()
|
||||||
|
|
||||||
|
# Initialize different types of fields
|
||||||
|
if field_type in ('list', 'text', 'data'):
|
||||||
|
struct_instance.init(field_name, 1)
|
||||||
|
print_debug(f"Initialized list/text/data field: {field_name}")
|
||||||
|
elif field_type in ('struct', 'object'):
|
||||||
|
struct_instance.init(field_name)
|
||||||
|
print_debug(f"Initialized struct/object field: {field_name}")
|
||||||
|
|
||||||
|
return struct_instance
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating instance for {field_name}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def encode_field(name: str, field: Any) -> dict:
|
def get_schema_fields(schema_struct: Any) -> List[Tuple[str, Any]]:
|
||||||
proto = field.proto
|
"""
|
||||||
ordinal = proto.ordinal.explicit if proto.ordinal.which() == "explicit" else None
|
Retrieve all fields from a given schema structure.
|
||||||
discriminant = proto.discriminantValue if proto.discriminantValue != NO_DISCRIMINANT else None
|
|
||||||
|
|
||||||
if proto.which() == "group":
|
Args:
|
||||||
type_desc = {"kind": "group", "typeId": hex_id(proto.group.typeId)}
|
schema_struct: The Cap'n Proto schema structure
|
||||||
else:
|
|
||||||
type_desc = encode_type(proto.slot.type)
|
|
||||||
|
|
||||||
return {
|
Returns:
|
||||||
"name": name,
|
A list of field names and their metadata
|
||||||
"ordinal": ordinal,
|
"""
|
||||||
"discriminant": discriminant,
|
try:
|
||||||
"type": type_desc,
|
# Get all fields from the schema
|
||||||
}
|
schema_fields = list(schema_struct.schema.fields.items())
|
||||||
|
|
||||||
|
print_debug("Discovered schema fields:")
|
||||||
|
for field_name, field_metadata in schema_fields:
|
||||||
|
print_debug(f"- {field_name}")
|
||||||
|
|
||||||
|
return schema_fields
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error retrieving schema fields: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def encode_struct(schema: Any) -> dict:
|
def generate_schema_instances(schema_struct: Any) -> List[Any]:
|
||||||
node = schema.node
|
"""
|
||||||
return {
|
Generate instances for all fields in a given schema.
|
||||||
"typeId": hex_id(node.id),
|
|
||||||
"displayName": node.displayName,
|
Args:
|
||||||
"hasUnion": node.struct.discriminantCount > 0,
|
schema_struct: The Cap'n Proto schema structure
|
||||||
"fields": [encode_field(name, field) for name, field in schema.fields.items()],
|
|
||||||
}
|
Returns:
|
||||||
|
A list of schema instances
|
||||||
|
"""
|
||||||
|
schema_fields = get_schema_fields(schema_struct)
|
||||||
|
instances = []
|
||||||
|
|
||||||
|
for field_prop in schema_fields:
|
||||||
|
try:
|
||||||
|
instance = create_schema_instance(schema_struct, field_prop)
|
||||||
|
if instance is not None:
|
||||||
|
instances.append(instance)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Skipping field due to error: {e}")
|
||||||
|
|
||||||
|
print(f"Generated {len(instances)} schema instances")
|
||||||
|
return instances
|
||||||
|
|
||||||
|
|
||||||
def _child_struct_schema(field: Any) -> Any:
|
def persist_instances(instances: List[Any], filename: str) -> None:
|
||||||
proto = field.proto
|
"""
|
||||||
if proto.which() == "group":
|
Write schema instances to a binary file.
|
||||||
return field.schema
|
|
||||||
type_node = proto.slot.type
|
Args:
|
||||||
which = type_node.which()
|
instances: List of schema instances
|
||||||
if which == "struct":
|
filename: Output file path
|
||||||
return field.schema
|
"""
|
||||||
if which == "list":
|
try:
|
||||||
container = field.schema
|
with open(filename, 'wb') as f:
|
||||||
element_type = type_node.list.elementType
|
for instance in instances:
|
||||||
while element_type.which() == "list":
|
f.write(instance.to_bytes())
|
||||||
container = container.elementType
|
|
||||||
element_type = element_type.list.elementType
|
print(f"Successfully wrote {len(instances)} instances to {filename}")
|
||||||
if element_type.which() == "struct":
|
|
||||||
return container.elementType
|
except Exception as e:
|
||||||
return None
|
print(f"Error persisting instances: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def collect_schema(root: Any) -> dict[str, dict]:
|
def read_instances(filename: str, schema_type: Any) -> List[Any]:
|
||||||
structs: dict[str, dict] = {}
|
"""
|
||||||
stack = [root]
|
Read schema instances from a binary file.
|
||||||
while stack:
|
|
||||||
schema = stack.pop()
|
Args:
|
||||||
type_id = hex_id(schema.node.id)
|
filename: Input file path
|
||||||
if type_id in structs:
|
schema_type: The schema type to use for reading
|
||||||
continue
|
|
||||||
structs[type_id] = encode_struct(schema)
|
Returns:
|
||||||
for _name, field in schema.fields.items():
|
A list of read schema instances
|
||||||
try:
|
"""
|
||||||
child = _child_struct_schema(field)
|
try:
|
||||||
except Exception:
|
with open(filename, 'rb') as f:
|
||||||
child = None
|
data = f.read()
|
||||||
if child is not None:
|
|
||||||
stack.append(child)
|
instances = list(schema_type.read_multiple_bytes(data))
|
||||||
return structs
|
|
||||||
|
print(f"Read {len(instances)} instances from {filename}")
|
||||||
|
return instances
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading instances: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def load_log(cereal_dir: str) -> Any:
|
def compare_schemas(original_instances: List[Any], read_instances: List[Any]) -> bool:
|
||||||
import capnp
|
"""
|
||||||
cereal_dir = os.path.abspath(cereal_dir)
|
Compare original and read-back instances to detect potential breaking changes.
|
||||||
capnp.remove_import_hook()
|
|
||||||
return capnp.load(os.path.join(cereal_dir, "log.capnp"), imports=[cereal_dir])
|
|
||||||
|
|
||||||
|
Args:
|
||||||
|
original_instances: List of originally generated instances
|
||||||
|
read_instances: List of instances read back from file
|
||||||
|
|
||||||
def dump_schema(cereal_dir: str, path: str) -> None:
|
Returns:
|
||||||
log = load_log(cereal_dir)
|
Boolean indicating whether schemas appear compatible
|
||||||
payload = {
|
"""
|
||||||
"root": hex_id(log.Event.schema.node.id),
|
if len(original_instances) != len(read_instances):
|
||||||
"structs": collect_schema(log.Event.schema),
|
print("❌ Schema Compatibility Warning: Instance count mismatch")
|
||||||
}
|
|
||||||
with open(path, "w", encoding="utf-8") as handle:
|
|
||||||
json.dump(payload, handle, indent=2, sort_keys=True)
|
|
||||||
print(f"wrote schema dump with {len(payload['structs'])} structs to {path}")
|
|
||||||
|
|
||||||
|
|
||||||
def types_equal(a: dict, b: dict) -> bool:
|
|
||||||
if a.get("kind") != b.get("kind"):
|
|
||||||
return False
|
return False
|
||||||
kind = a["kind"]
|
|
||||||
if kind in ("struct", "enum", "interface", "group"):
|
compatible = True
|
||||||
return a.get("typeId") == b.get("typeId")
|
for struct in read_instances:
|
||||||
if kind == "list":
|
try:
|
||||||
return types_equal(a["element"], b["element"])
|
getattr(struct, struct.which()) # Attempting to access the field to validate readability
|
||||||
return True
|
except Exception as e:
|
||||||
|
print(f"❌ Structural change detected: {struct.which()} is not readable.\nFull error: {e}")
|
||||||
|
compatible = False
|
||||||
|
|
||||||
|
return compatible
|
||||||
|
|
||||||
|
|
||||||
def type_repr(t: dict) -> str:
|
def main():
|
||||||
kind = t.get("kind", "?")
|
"""
|
||||||
if kind in ("struct", "enum", "interface", "group"):
|
CLI entry point for schema compatibility testing.
|
||||||
return f"{kind}({t.get('typeId')})"
|
"""
|
||||||
if kind == "list":
|
# Setup argument parser
|
||||||
return f"list<{type_repr(t['element'])}>"
|
|
||||||
return kind
|
|
||||||
|
|
||||||
|
|
||||||
def field_is_union_variant(field: dict) -> bool:
|
|
||||||
return field.get("discriminant") is not None
|
|
||||||
|
|
||||||
|
|
||||||
def index_fields_by_ordinal(struct: dict) -> dict[int, dict]:
|
|
||||||
indexed: dict[int, dict] = {}
|
|
||||||
for field in struct["fields"]:
|
|
||||||
ordinal = field.get("ordinal")
|
|
||||||
if ordinal is None:
|
|
||||||
continue
|
|
||||||
indexed[ordinal] = field
|
|
||||||
return indexed
|
|
||||||
|
|
||||||
|
|
||||||
def compare(sunnypilot_dump: dict, upstream_dump: dict) -> list[str]:
|
|
||||||
violations: list[str] = []
|
|
||||||
sunnypilot_structs: dict[str, dict] = sunnypilot_dump["structs"]
|
|
||||||
upstream_structs: dict[str, dict] = upstream_dump["structs"]
|
|
||||||
|
|
||||||
sunnypilot_struct_referenced_from_shared: set[str] = set()
|
|
||||||
|
|
||||||
for type_id, sunnypilot_struct in sunnypilot_structs.items():
|
|
||||||
upstream_struct = upstream_structs.get(type_id)
|
|
||||||
if upstream_struct is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
sunnypilot_fields = index_fields_by_ordinal(sunnypilot_struct)
|
|
||||||
upstream_fields = index_fields_by_ordinal(upstream_struct)
|
|
||||||
display = sunnypilot_struct["displayName"]
|
|
||||||
|
|
||||||
for ordinal, sunnypilot_field in sunnypilot_fields.items():
|
|
||||||
upstream_field = upstream_fields.get(ordinal)
|
|
||||||
if upstream_field is None:
|
|
||||||
if field_is_union_variant(sunnypilot_field):
|
|
||||||
violations.append(
|
|
||||||
f"[R2] {display} @{ordinal} ('{sunnypilot_field['name']}', {type_repr(sunnypilot_field['type'])}): "
|
|
||||||
f"union variant not present upstream. upstream cannot parse this discriminant."
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not types_equal(sunnypilot_field["type"], upstream_field["type"]):
|
|
||||||
violations.append(
|
|
||||||
f"[R1] {display} @{ordinal}: type mismatch. "
|
|
||||||
f"sunnypilot='{sunnypilot_field['name']}' {type_repr(sunnypilot_field['type'])} vs "
|
|
||||||
f"upstream='{upstream_field['name']}' {type_repr(upstream_field['type'])}."
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
cursor = sunnypilot_field["type"]
|
|
||||||
while cursor.get("kind") == "list":
|
|
||||||
cursor = cursor["element"]
|
|
||||||
if cursor.get("kind") in ("struct", "group", "interface") and cursor.get("typeId"):
|
|
||||||
sunnypilot_struct_referenced_from_shared.add(cursor["typeId"])
|
|
||||||
|
|
||||||
for type_id, sunnypilot_struct in sunnypilot_structs.items():
|
|
||||||
if type_id in upstream_structs:
|
|
||||||
continue
|
|
||||||
if type_id in sunnypilot_struct_referenced_from_shared:
|
|
||||||
violations.append(
|
|
||||||
f"[R5] struct {sunnypilot_struct['displayName']} ({type_id}) exists only on sunnypilot "
|
|
||||||
f"but is referenced from an upstream-shared field. upstream cannot resolve this type."
|
|
||||||
)
|
|
||||||
|
|
||||||
return violations
|
|
||||||
|
|
||||||
|
|
||||||
def load_peer(path: str) -> dict:
|
|
||||||
with open(path, "r", encoding="utf-8") as handle:
|
|
||||||
return json.load(handle)
|
|
||||||
|
|
||||||
|
|
||||||
def run_read(cereal_dir: str, peer_path: str) -> int:
|
|
||||||
log = load_log(cereal_dir)
|
|
||||||
peer_dump = load_peer(peer_path)
|
|
||||||
local_dump = {
|
|
||||||
"root": hex_id(log.Event.schema.node.id),
|
|
||||||
"structs": collect_schema(log.Event.schema),
|
|
||||||
}
|
|
||||||
violations = compare(sunnypilot_dump=peer_dump, upstream_dump=local_dump)
|
|
||||||
|
|
||||||
if not violations:
|
|
||||||
print("cereal compat OK: upstream openpilot can parse sunnypilot routes "
|
|
||||||
"(no leaked structs, no ordinal collisions).")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
print(f"cereal compat FAIL: upstream openpilot would misparse sunnypilot routes "
|
|
||||||
f"({len(violations)} violation(s)):")
|
|
||||||
for v in violations:
|
|
||||||
print(f" {v}")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="sunnypilot <-> upstream cereal compatibility validator (schema-level)."
|
description='Cap\'n Proto Schema Compatibility Testing Tool',
|
||||||
|
epilog='Test schema compatibility by generating and reading back instances.'
|
||||||
)
|
)
|
||||||
mode = parser.add_mutually_exclusive_group(required=True)
|
|
||||||
mode.add_argument("-g", "--generate", action="store_true", help="dump local schema to JSON")
|
# Add mutually exclusive group for generation or reading mode
|
||||||
mode.add_argument("-r", "--read", action="store_true", help="load peer JSON and diff against local")
|
mode_group = parser.add_mutually_exclusive_group(required=True)
|
||||||
parser.add_argument("-f", "--file", default="schema.json", help="JSON file path (default: schema.json)")
|
mode_group.add_argument('-g', '--generate', action='store_true',
|
||||||
parser.add_argument("--cereal-dir", required=True, help="path to cereal directory containing log.capnp")
|
help='Generate schema instances')
|
||||||
|
mode_group.add_argument('-r', '--read', action='store_true',
|
||||||
|
help='Read and validate schema instances')
|
||||||
|
|
||||||
|
# Common arguments
|
||||||
|
parser.add_argument('-f', '--file',
|
||||||
|
default='schema_instances.bin',
|
||||||
|
help='Output/input binary file (default: schema_instances.bin)')
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Import the schema dynamically
|
||||||
|
try:
|
||||||
|
from cereal import log
|
||||||
|
schema_type = log.Event
|
||||||
|
except ImportError:
|
||||||
|
print("Error: Unable to import schema. Ensure 'cereal' is installed.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Execute based on mode
|
||||||
if args.generate:
|
if args.generate:
|
||||||
dump_schema(args.cereal_dir, args.file)
|
print("🔧 Generating Schema Instances")
|
||||||
return 0
|
instances = generate_schema_instances(schema_type)
|
||||||
return run_read(args.cereal_dir, args.file)
|
persist_instances(instances, args.file)
|
||||||
|
print("✅ Instance generation complete")
|
||||||
|
|
||||||
|
elif args.read:
|
||||||
|
print("🔍 Reading and Validating Schema Instances")
|
||||||
|
generated_instances = generate_schema_instances(schema_type)
|
||||||
|
read_back_instances = read_instances(args.file, schema_type)
|
||||||
|
|
||||||
|
# Compare schemas
|
||||||
|
if compare_schemas(generated_instances, read_back_instances):
|
||||||
|
print("✅ Schema Compatibility: No breaking changes detected")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print("❌ Potential Schema Breaking Changes Detected")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
main()
|
||||||
|
|||||||
+2
-3
@@ -39,8 +39,8 @@ _services: dict[str, tuple] = {
|
|||||||
"roadEncodeIdx": (False, 20., 1),
|
"roadEncodeIdx": (False, 20., 1),
|
||||||
"liveTracks": (True, 20.),
|
"liveTracks": (True, 20.),
|
||||||
"sendcan": (True, 100., 139, QueueSize.MEDIUM),
|
"sendcan": (True, 100., 139, QueueSize.MEDIUM),
|
||||||
"logMessage": (True, 0., None, QueueSize.BIG),
|
"logMessage": (True, 0.),
|
||||||
"errorLogMessage": (True, 0., 1, QueueSize.BIG),
|
"errorLogMessage": (True, 0., 1),
|
||||||
"liveCalibration": (True, 4., 4),
|
"liveCalibration": (True, 4., 4),
|
||||||
"liveTorqueParameters": (True, 4., 1),
|
"liveTorqueParameters": (True, 4., 1),
|
||||||
"liveDelay": (True, 4., 1),
|
"liveDelay": (True, 4., 1),
|
||||||
@@ -49,7 +49,6 @@ _services: dict[str, tuple] = {
|
|||||||
"carControl": (True, 100., 10),
|
"carControl": (True, 100., 10),
|
||||||
"carOutput": (True, 100., 10),
|
"carOutput": (True, 100., 10),
|
||||||
"longitudinalPlan": (True, 20., 10),
|
"longitudinalPlan": (True, 20., 10),
|
||||||
"lateralManeuverPlan": (True, 20.),
|
|
||||||
"driverAssistance": (True, 20., 20),
|
"driverAssistance": (True, 20., 20),
|
||||||
"procLog": (True, 0.5, 15, QueueSize.BIG),
|
"procLog": (True, 0.5, 15, QueueSize.BIG),
|
||||||
"gpsLocationExternal": (True, 10., 10),
|
"gpsLocationExternal": (True, 10., 10),
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
#define DEFAULT_MODEL "CD210 (Default)"
|
||||||
@@ -82,7 +82,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"LiveParametersV2", {PERSISTENT, BYTES}},
|
{"LiveParametersV2", {PERSISTENT, BYTES}},
|
||||||
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
|
{"LiveTorqueParameters", {PERSISTENT | DONT_LOG, BYTES}},
|
||||||
{"LocationFilterInitialState", {PERSISTENT, BYTES}},
|
{"LocationFilterInitialState", {PERSISTENT, BYTES}},
|
||||||
{"LateralManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
|
||||||
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
{"LongitudinalManeuverMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||||
{"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
|
{"LongitudinalPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPersonality::STANDARD))}},
|
||||||
{"NetworkMetered", {PERSISTENT | BACKUP, BOOL}},
|
{"NetworkMetered", {PERSISTENT | BACKUP, BOOL}},
|
||||||
@@ -135,8 +134,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"Version", {PERSISTENT, STRING}},
|
{"Version", {PERSISTENT, STRING}},
|
||||||
|
|
||||||
// --- sunnypilot params --- //
|
// --- sunnypilot params --- //
|
||||||
{"AccelPersonality", {PERSISTENT | BACKUP, INT, std::to_string(static_cast<int>(cereal::LongitudinalPlanSP::AccelerationPersonality::NORMAL))}},
|
|
||||||
{"AccelPersonalityEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
|
||||||
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"AutoLaneChangeBsmDelay", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
{"AutoLaneChangeTimer", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
@@ -206,7 +203,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
// sunnylink params
|
// sunnylink params
|
||||||
{"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}},
|
{"EnableSunnylinkUploader", {PERSISTENT | BACKUP, BOOL}},
|
||||||
{"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
{"LastSunnylinkPingTime", {CLEAR_ON_MANAGER_START, INT}},
|
||||||
{"ParamsVersion", {PERSISTENT, INT}},
|
|
||||||
{"SunnylinkCache_Roles", {PERSISTENT, STRING}},
|
{"SunnylinkCache_Roles", {PERSISTENT, STRING}},
|
||||||
{"SunnylinkCache_Users", {PERSISTENT, STRING}},
|
{"SunnylinkCache_Users", {PERSISTENT, STRING}},
|
||||||
{"SunnylinkDongleId", {PERSISTENT, STRING}},
|
{"SunnylinkDongleId", {PERSISTENT, STRING}},
|
||||||
|
|||||||
@@ -65,7 +65,10 @@ DEVICE_CAMERAS = {
|
|||||||
("unknown", "ox03c10"): _ar_ox_config,
|
("unknown", "ox03c10"): _ar_ox_config,
|
||||||
|
|
||||||
# simulator (emulates a tici)
|
# simulator (emulates a tici)
|
||||||
("pc", "unknown"): _ar_ox_config,
|
("pc", "unknown"): _os_config,
|
||||||
|
# ("pc", "ar0231"): _ar_ox_config,
|
||||||
|
# ("pc", "ox03c10"): _ar_ox_config,
|
||||||
|
# ("pc", "os04c10"): _os_config,
|
||||||
}
|
}
|
||||||
prods = itertools.product(('tici', 'tizi', 'mici'), (('ar0231', _ar_ox_config), ('ox03c10', _ar_ox_config), ('os04c10', _os_config)))
|
prods = itertools.product(('tici', 'tizi', 'mici'), (('ar0231', _ar_ox_config), ('ox03c10', _ar_ox_config), ('os04c10', _os_config)))
|
||||||
DEVICE_CAMERAS.update({(d, c[0]): c[1] for d, c in prods})
|
DEVICE_CAMERAS.update({(d, c[0]): c[1] for d, c in prods})
|
||||||
|
|||||||
+2
-2
@@ -131,11 +131,11 @@ def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.Buffered
|
|||||||
return compressed_stream, compressed_size
|
return compressed_stream, compressed_size
|
||||||
|
|
||||||
|
|
||||||
# remove all keys that end in DEPRECATED, plus any "deprecated" group
|
# remove all keys that end in DEPRECATED
|
||||||
def strip_deprecated_keys(d):
|
def strip_deprecated_keys(d):
|
||||||
for k in list(d.keys()):
|
for k in list(d.keys()):
|
||||||
if isinstance(k, str):
|
if isinstance(k, str):
|
||||||
if k.endswith('DEPRECATED') or k == 'deprecated':
|
if k.endswith('DEPRECATED'):
|
||||||
d.pop(k)
|
d.pop(k)
|
||||||
elif isinstance(d[k], dict):
|
elif isinstance(d[k], dict):
|
||||||
strip_deprecated_keys(d[k])
|
strip_deprecated_keys(d[k])
|
||||||
|
|||||||
+122
-127
@@ -4,25 +4,24 @@
|
|||||||
|
|
||||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||||
|
|
||||||
# 340 Supported Cars
|
# 336 Supported Cars
|
||||||
|
|
||||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
||||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2016-18">Buy Here</a></sub></details>|||
|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2016-18">Buy Here</a></sub></details>|||
|
||||||
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2019">Buy Here</a></sub></details>|||
|
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura ILX 2019">Buy Here</a></sub></details>|||
|
||||||
|Acura|MDX 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|43 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura MDX 2022-24">Buy Here</a></sub></details>|||
|
|
||||||
|Acura|MDX 2025-26|All except Type S|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura MDX 2025-26">Buy Here</a></sub></details>|||
|
|Acura|MDX 2025-26|All except Type S|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura MDX 2025-26">Buy Here</a></sub></details>|||
|
||||||
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2016-18">Buy Here</a></sub></details>|||
|
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2016-18">Buy Here</a></sub></details>|||
|
||||||
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2019-21">Buy Here</a></sub></details>|||
|
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura RDX 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Acura|TLX 2021-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2021-22">Buy Here</a></sub></details>|||
|
|Acura|TLX 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2021">Buy Here</a></sub></details>|||
|
||||||
|Acura|TLX 2025|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2025">Buy Here</a></sub></details>|||
|
|Acura|TLX 2025|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Acura TLX 2025">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 2014-19">Buy Here</a></sub></details>|||
|
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 2014-19">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|||
|
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q2 2018">Buy Here</a></sub></details>|||
|
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q2 2018">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q3 2019-24">Buy Here</a></sub></details>|||
|
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi Q3 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi RS3 2018">Buy Here</a></sub></details>|||
|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi RS3 2018">Buy Here</a></sub></details>|||
|
||||||
|Audi[<sup>11</sup>](#footnotes)|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Audi S3 2015-17">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EUV 2022-23">Buy Here</a></sub></details>|<a href="https://youtu.be/xvwzGMUA210" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Bolt EV 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">Buy Here</a></sub></details>|||
|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Equinox 2019-22">Buy Here</a></sub></details>|||
|
||||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">Buy Here</a></sub></details>|||
|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chevrolet Silverado 1500 2020-21">Buy Here</a></sub></details>|||
|
||||||
@@ -32,34 +31,34 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Chrysler|Pacifica 2021-23|All|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2021-23">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica 2021-23|All|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2017-18">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2019-25">Buy Here</a></sub></details>|||
|
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Chrysler Pacifica Hybrid 2019-25">Buy Here</a></sub></details>|||
|
||||||
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None|<a href="https://youtu.be/VT-i3yRsX2s?t=2736" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|comma|body|All|openpilot|0 mph|0 mph|[](##)|[](##)|None|<a href="https://youtu.be/VT-i3yRsX2s?t=2736" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|CUPRA[<sup>11</sup>](#footnotes)|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=CUPRA Ateca 2018-23">Buy Here</a></sub></details>|||
|
|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=CUPRA Ateca 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Dodge Durango 2020-21">Buy Here</a></sub></details>|||
|
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Dodge Durango 2020-21">Buy Here</a></sub></details>|||
|
||||||
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Bronco Sport 2021-24">Buy Here</a></sub></details>|||
|
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Bronco Sport 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Expedition 2022-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Expedition 2022-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer 2020-24">Buy Here</a></sub></details>|||
|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer Hybrid 2020-24">Buy Here</a></sub></details>|||
|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Explorer Hybrid 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 Hybrid 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford F-150 Hybrid 2021-23">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=MewJc9LYp9M" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Focus 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus 2018">Buy Here</a></sub></details>|||
|
|Ford|Focus 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus 2018">Buy Here</a></sub></details>|||
|
||||||
|Ford|Focus Hybrid 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus Hybrid 2018">Buy Here</a></sub></details>|||
|
|Ford|Focus Hybrid 2018[<sup>2</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Focus Hybrid 2018">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Kuga Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2022">Buy Here</a></sub></details>|||
|
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2022">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2023-24">Buy Here</a></sub></details>|||
|
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2022">Buy Here</a></sub></details>|||
|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2022">Buy Here</a></sub></details>|||
|
||||||
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2023-24">Buy Here</a></sub></details>|||
|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Maverick Hybrid 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Ford|Mustang Mach-E 2021-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Mustang Mach-E 2021-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Mustang Mach-E 2021-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Mustang Mach-E 2021-24">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Ranger 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ford Ranger 2024">Buy Here</a></sub></details>||<a href="https://www.youtube.com/watch?v=uUGkH6C_EQU" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Genesis|G70 2018|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2018">Buy Here</a></sub></details>|||
|
|Genesis|G70 2018|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2018">Buy Here</a></sub></details>|||
|
||||||
|Genesis|G70 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2019-21">Buy Here</a></sub></details>|||
|
|Genesis|G70 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Genesis|G70 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2022-23">Buy Here</a></sub></details>|||
|
|Genesis|G70 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis G70 2022-23">Buy Here</a></sub></details>|||
|
||||||
@@ -74,18 +73,18 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Genesis|GV70 Electrified (Australia Only) 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV70 Electrified (Australia Only) 2022">Buy Here</a></sub></details>|||
|
|Genesis|GV70 Electrified (Australia Only) 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV70 Electrified (Australia Only) 2022">Buy Here</a></sub></details>|||
|
||||||
|Genesis|GV70 Electrified (with HDA II) 2023-24|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV70 Electrified (with HDA II) 2023-24">Buy Here</a></sub></details>|||
|
|Genesis|GV70 Electrified (with HDA II) 2023-24|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV70 Electrified (with HDA II) 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Genesis|GV80 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV80 2023">Buy Here</a></sub></details>|||
|
|Genesis|GV80 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Genesis GV80 2023">Buy Here</a></sub></details>|||
|
||||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=GMC Sierra 1500 2020-21">Buy Here</a></sub></details>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=GMC Sierra 1500 2020-21">Buy Here</a></sub></details>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord 2018-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord 2018-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Accord 2023-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord 2023-25">Buy Here</a></sub></details>|||
|
|Honda|Accord 2023-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord 2023-25">Buy Here</a></sub></details>|||
|
||||||
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord Hybrid 2018-22">Buy Here</a></sub></details>|||
|
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord Hybrid 2018-22">Buy Here</a></sub></details>|||
|
||||||
|Honda|Accord Hybrid 2023-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord Hybrid 2023-25">Buy Here</a></sub></details>|||
|
|Honda|Accord Hybrid 2023-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Accord Hybrid 2023-25">Buy Here</a></sub></details>|||
|
||||||
|Honda|City (Brazil only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|14 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda City (Brazil only) 2023">Buy Here</a></sub></details>|||
|
|Honda|City (Brazil only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|14 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda City (Brazil only) 2023">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2016-18">Buy Here</a></sub></details>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2016-18">Buy Here</a></sub></details>|<a href="https://youtu.be/-IkImTe1NYE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>4</sup>](#footnotes)|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Honda|Civic 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>4</sup>](#footnotes)|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=4Iz1Mz5LGF8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Civic 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Honda|Civic 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Civic Hatchback 2017-18|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2017-18">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback 2017-18|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hatchback 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2019-21">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hatchback 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Honda|Civic Hatchback 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback 2022-24">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Honda|Civic Hatchback Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid 2025-26">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hatchback Hybrid (Europe only) 2023">Buy Here</a></sub></details>|||
|
||||||
|Honda|Civic Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hybrid 2025-26">Buy Here</a></sub></details>|||
|
|Honda|Civic Hybrid 2025-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Civic Hybrid 2025-26">Buy Here</a></sub></details>|||
|
||||||
@@ -104,7 +103,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Honda|N-Box 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|11 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda N-Box 2018">Buy Here</a></sub></details>|||
|
|Honda|N-Box 2018|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|11 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda N-Box 2018">Buy Here</a></sub></details>|||
|
||||||
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2018-20">Buy Here</a></sub></details>|||
|
|Honda|Odyssey 2018-20|Honda Sensing|openpilot|26 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2018-20">Buy Here</a></sub></details>|||
|
||||||
|Honda|Odyssey 2021-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|43 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2021-26">Buy Here</a></sub></details>|||
|
|Honda|Odyssey 2021-26|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|43 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey 2021-26">Buy Here</a></sub></details>|||
|
||||||
|Honda|Odyssey (Singapore) 2021|Honda Sensing|openpilot|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey (Singapore) 2021">Buy Here</a></sub></details>|||
|
|
||||||
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|openpilot|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey (Taiwan) 2018-19">Buy Here</a></sub></details>|||
|
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|openpilot|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Odyssey (Taiwan) 2018-19">Buy Here</a></sub></details>|||
|
||||||
|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2019-25">Buy Here</a></sub></details>|||
|
|Honda|Passport 2019-25|All|openpilot|26 mph|12 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2019-25">Buy Here</a></sub></details>|||
|
||||||
|Honda|Passport 2026|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2026">Buy Here</a></sub></details>|||
|
|Honda|Passport 2026|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Honda Passport 2026">Buy Here</a></sub></details>|||
|
||||||
@@ -117,9 +115,9 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Custin 2023">Buy Here</a></sub></details>|||
|
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Custin 2023">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2017-18">Buy Here</a></sub></details>|||
|
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2019">Buy Here</a></sub></details>|||
|
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2019">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra GT 2017-20">Buy Here</a></sub></details>|||
|
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra GT 2017-20">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra Hybrid 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/_EdYQtV52-c" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Genesis 2015-16">Buy Here</a></sub></details>|||
|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai J connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Genesis 2015-16">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai i30 2017-19">Buy Here</a></sub></details>|||
|
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai i30 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24">Buy Here</a></sub></details>|||
|
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24">Buy Here</a></sub></details>|||
|
||||||
@@ -136,17 +134,17 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Hyundai|Kona 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona 2022-23">Buy Here</a></sub></details>|||
|
|Hyundai|Kona 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2018-21">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2018-21">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2022-23">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai O connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Electric (with HDA II, Korea only) 2023">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=U2fOCmcQ8hw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Hybrid 2020">Buy Here</a></sub></details>|||
|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai I connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Kona Hybrid 2020">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Nexo 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Nexo 2021">Buy Here</a></sub></details>|||
|
|Hyundai|Nexo 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Nexo 2021">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Palisade 2020-22">Buy Here</a></sub></details>|<a href="https://youtu.be/TAnDqjF4fDY?t=456" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Cruz 2022-24">Buy Here</a></sub></details>|||
|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Cruz 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe 2019-20">Buy Here</a></sub></details>|<a href="https://youtu.be/bjDR0YjM__s" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe 2019-20">Buy Here</a></sub></details>|<a href="https://youtu.be/bjDR0YjM__s" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Santa Fe 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Santa Fe 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/VnHzSTygTS4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>|||
|
|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe Plug-in Hybrid 2022-23">Buy Here</a></sub></details>|||
|
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Santa Fe Plug-in Hybrid 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2018-19">Buy Here</a></sub></details>|||
|
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2018-19">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Staria 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Staria 2023">Buy Here</a></sub></details>|||
|
|Hyundai|Staria 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Staria 2023">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson 2021">Buy Here</a></sub></details>|||
|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson 2021">Buy Here</a></sub></details>|||
|
||||||
@@ -156,8 +154,8 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Hyundai|Tucson Hybrid 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Hybrid 2022-24">Buy Here</a></sub></details>|||
|
|Hyundai|Tucson Hybrid 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Hybrid 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Tucson Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Plug-in Hybrid 2024">Buy Here</a></sub></details>|||
|
|Hyundai|Tucson Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Plug-in Hybrid 2024">Buy Here</a></sub></details>|||
|
||||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Veloster 2019-20">Buy Here</a></sub></details>|||
|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Veloster 2019-20">Buy Here</a></sub></details>|||
|
||||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|
||||||
@@ -170,16 +168,16 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Kia|K7 2017|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K7 2017">Buy Here</a></sub></details>|||
|
|Kia|K7 2017|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K7 2017">Buy Here</a></sub></details>|||
|
||||||
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Niro EV 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2021">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2022">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Niro EV 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2022">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Niro EV (with HDA II) 2024-25|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV (with HDA II) 2024-25">Buy Here</a></sub></details>|||
|
|Kia|Niro EV (with HDA II) 2025|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai R connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV (with HDA II) 2025">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro EV (without HDA II) 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV (without HDA II) 2023-25">Buy Here</a></sub></details>|||
|
|Kia|Niro EV (without HDA II) 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV (without HDA II) 2023-25">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Hybrid 2018|Smart Cruise Control (SCC)|Stock|10 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2018">Buy Here</a></sub></details>|||
|
|Kia|Niro Hybrid 2018|Smart Cruise Control (SCC)|Stock|10 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2018">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2021">Buy Here</a></sub></details>|||
|
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2021">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2022">Buy Here</a></sub></details>|||
|
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2022">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Hybrid 2023-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2023-24">Buy Here</a></sub></details>|||
|
|Kia|Niro Hybrid 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Hybrid 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>|||
|
|Kia|Niro Plug-in Hybrid 2018-19|All|Stock|10 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>|||
|
|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2021">Buy Here</a></sub></details>|||
|
|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai D connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro Plug-in Hybrid 2021">Buy Here</a></sub></details>|||
|
||||||
@@ -188,21 +186,21 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Optima 2019-20">Buy Here</a></sub></details>|||
|
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Optima 2019-20">Buy Here</a></sub></details>|||
|
||||||
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Optima Hybrid 2019">Buy Here</a></sub></details>|||
|
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Optima Hybrid 2019">Buy Here</a></sub></details>|||
|
||||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Seltos 2021">Buy Here</a></sub></details>|||
|
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Seltos 2021">Buy Here</a></sub></details>|||
|
||||||
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Sorento 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2021-23">Buy Here</a></sub></details>|||
|
|Kia|Sorento 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|Sorento Hybrid 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento Hybrid 2021-23">Buy Here</a></sub></details>|||
|
|Kia|Sorento Hybrid 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento Hybrid 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|Sorento Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>|||
|
|Kia|Sorento Plug-in Hybrid 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|Sportage 2023-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sportage 2023-24">Buy Here</a></sub></details>|||
|
|Kia|Sportage 2023-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sportage 2023-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sportage Hybrid 2023">Buy Here</a></sub></details>|||
|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Sportage Hybrid 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Stinger 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Stinger 2022-23">Buy Here</a></sub></details>|||
|
|Kia|Stinger 2022-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Stinger 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Telluride 2020-22">Buy Here</a></sub></details>|||
|
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Telluride 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus CT Hybrid 2017-18">Buy Here</a></sub></details>|||
|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus CT Hybrid 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Lexus|ES 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES 2017-18">Buy Here</a></sub></details>|||
|
|Lexus|ES 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Lexus|ES 2019-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES 2019-25">Buy Here</a></sub></details>|||
|
|Lexus|ES 2019-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES 2019-25">Buy Here</a></sub></details>|||
|
||||||
|Lexus|ES Hybrid 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES Hybrid 2017-18">Buy Here</a></sub></details>|||
|
|Lexus|ES Hybrid 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES Hybrid 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Lexus|ES Hybrid 2019-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES Hybrid 2019-25">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Lexus|ES Hybrid 2019-25|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus ES Hybrid 2019-25">Buy Here</a></sub></details>|<a href="https://youtu.be/BZ29osRVJeg?t=12" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus GS F 2016">Buy Here</a></sub></details>|||
|
|Lexus|GS F 2016|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus GS F 2016">Buy Here</a></sub></details>|||
|
||||||
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2017-19">Buy Here</a></sub></details>|||
|
|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Lexus|IS 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2022-24">Buy Here</a></sub></details>|||
|
|Lexus|IS 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus IS 2022-24">Buy Here</a></sub></details>|||
|
||||||
@@ -223,25 +221,23 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus UX Hybrid 2019-24">Buy Here</a></sub></details>|||
|
|Lexus|UX Hybrid 2019-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lexus UX Hybrid 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator 2020-24">Buy Here</a></sub></details>|||
|
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator Plug-in Hybrid 2020-24">Buy Here</a></sub></details>|||
|
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Lincoln Aviator Plug-in Hybrid 2020-24">Buy Here</a></sub></details>|||
|
||||||
|MAN[<sup>11</sup>](#footnotes)|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN eTGE 2020-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN eTGE 2020-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|MAN[<sup>11</sup>](#footnotes)|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN TGE 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=MAN TGE 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-5 2022-25">Buy Here</a></sub></details>|||
|
|Mazda|CX-5 2022-25|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-5 2022-25">Buy Here</a></sub></details>|||
|
||||||
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Mazda connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Mazda CX-9 2021-23">Buy Here</a></sub></details>|<a href="https://youtu.be/dA3duO4a0O4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Altima 2019-20, 2024|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Altima 2019-20, 2024">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|Altima 2019-20, 2024|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Altima 2019-20, 2024">Buy Here</a></sub></details>|||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Nissan[<sup>5</sup>](#footnotes)|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Leaf 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/vaMbtAh_0cY" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Rogue 2018-20">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan Rogue 2018-20">Buy Here</a></sub></details>|||
|
||||||
|Nissan[<sup>5</sup>](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan X-Trail 2017">Buy Here</a></sub></details>|||
|
|Nissan[<sup>5</sup>](#footnotes)|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Nissan A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Nissan X-Trail 2017">Buy Here</a></sub></details>|||
|
||||||
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 1500 2019-24">Buy Here</a></sub></details>|||
|
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 1500 2019-24">Buy Here</a></sub></details>|||
|
||||||
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 2500 2020-24">Buy Here</a></sub></details>|||
|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 2500 2020-24">Buy Here</a></sub></details>|||
|
||||||
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 3500 2019-22">Buy Here</a></sub></details>|||
|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Ram connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Ram 3500 2019-22">Buy Here</a></sub></details>|||
|
||||||
|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1S 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|Rivian|R1S 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1S 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Rivian|R1S 2025|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1S 2025">Buy Here</a></sub></details>|||
|
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||||
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2022-24">Buy Here</a></sub></details>||<a href="https://youtu.be/uaISd1j7Z4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>|
|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
||||||
|Rivian|R1T 2025|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Rivian B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Rivian R1T 2025">Buy Here</a></sub></details>|||
|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
||||||
|SEAT[<sup>11</sup>](#footnotes)|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
|
||||||
|SEAT[<sup>11</sup>](#footnotes)|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
|
||||||
|Subaru|Ascent 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Ascent 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Forester 2017-18|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2017-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Forester 2017-18|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2017-18">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Forester 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Forester 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
@@ -252,17 +248,17 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Subaru|Outback 2015-17|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2015-17">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2015-17|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2015-17">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Outback 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|Outback 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|Outback 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||||
|Škoda|Fabia 2022-23[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Škoda|Fabia 2022-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda|Kamiq 2021-23[<sup>12,14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Škoda|Kamiq 2021-23[<sup>11,13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kamiq 2021-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Karoq 2019-23[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
|Škoda|Karoq 2019-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Karoq 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Kodiaq 2017-23[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kodiaq 2017-23">Buy Here</a></sub></details>|||
|
|Škoda|Kodiaq 2017-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Kodiaq 2017-23">Buy Here</a></sub></details>|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Octavia 2015-19[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia 2015-19">Buy Here</a></sub></details>|||
|
|Škoda|Octavia 2015-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Octavia RS 2016[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia RS 2016">Buy Here</a></sub></details>|||
|
|Škoda|Octavia RS 2016[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia RS 2016">Buy Here</a></sub></details>|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Octavia Scout 2017-19[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia Scout 2017-19">Buy Here</a></sub></details>|||
|
|Škoda|Octavia Scout 2017-19[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Octavia Scout 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Škoda|Scala 2020-23[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Scala 2020-23">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Škoda|Scala 2020-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Scala 2020-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Škoda[<sup>11</sup>](#footnotes)|Superb 2015-22[<sup>14</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Superb 2015-22">Buy Here</a></sub></details>|||
|
|Škoda|Superb 2015-22[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Superb 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW3) 2019-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW3) 2019-23">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW3) 2019-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW3) 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model 3 (with HW4) 2024-25[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla B connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model 3 (with HW4) 2024-25">Buy Here</a></sub></details>|||
|
||||||
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW3) 2020-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW3) 2020-23">Buy Here</a></sub></details>|||
|
|Tesla[<sup>9</sup>](#footnotes)|Model Y (with HW3) 2020-23[<sup>8</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Tesla A connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Tesla Model Y (with HW3) 2020-23">Buy Here</a></sub></details>|||
|
||||||
@@ -279,75 +275,75 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Toyota|C-HR 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR 2021">Buy Here</a></sub></details>|||
|
|Toyota|C-HR 2021|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR 2021">Buy Here</a></sub></details>|||
|
||||||
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR Hybrid 2017-20">Buy Here</a></sub></details>|||
|
|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR Hybrid 2017-20">Buy Here</a></sub></details>|||
|
||||||
|Toyota|C-HR Hybrid 2021-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR Hybrid 2021-22">Buy Here</a></sub></details>|||
|
|Toyota|C-HR Hybrid 2021-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota C-HR Hybrid 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>10</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Camry 2018-20|All|Stock|0 mph[<sup>10</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=fkcjviZY9CM" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Camry 2021-24|All|openpilot|0 mph[<sup>10</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry 2021-24">Buy Here</a></sub></details>|||
|
|Toyota|Camry 2021-24|All|openpilot|0 mph[<sup>10</sup>](#footnotes)|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry Hybrid 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry Hybrid 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Q2DYY0AWKgk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry Hybrid 2021-24">Buy Here</a></sub></details>|||
|
|Toyota|Camry Hybrid 2021-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Camry Hybrid 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Corolla 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla 2017-19">Buy Here</a></sub></details>|||
|
|Toyota|Corolla 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla 2020-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla 2020-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Cross (Non-US only) 2020-23">Buy Here</a></sub></details>|||
|
|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Cross (Non-US only) 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Cross Hybrid (Non-US only) 2020-22">Buy Here</a></sub></details>|||
|
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Cross Hybrid (Non-US only) 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hatchback 2019-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hatchback 2019-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=_66pXk0CBYA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Corolla Hybrid (South America only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hybrid (South America only) 2020-23">Buy Here</a></sub></details>|||
|
|Toyota|Corolla Hybrid (South America only) 2020-23|All|openpilot|17 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Corolla Hybrid (South America only) 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Highlander 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Highlander 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander 2017-19">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0wS0wXSLzoo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander 2020-23">Buy Here</a></sub></details>|||
|
|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Highlander Hybrid 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander Hybrid 2017-19">Buy Here</a></sub></details>|||
|
|Toyota|Highlander Hybrid 2017-19|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander Hybrid 2017-19">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander Hybrid 2020-23">Buy Here</a></sub></details>|||
|
|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Highlander Hybrid 2020-23">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Mirai 2021">Buy Here</a></sub></details>|||
|
|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Mirai 2021">Buy Here</a></sub></details>|||
|
||||||
|Toyota|Prius 2016|Toyota Safety Sense P|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2016">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Prius 2016|Toyota Safety Sense P|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2016">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Prius 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Prius 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Prius Prime 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius Prime 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Prius Prime 2017-20|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius Prime 2017-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=8zopPJI8XQ0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius Prime 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius Prime 2021-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=J58TvCpUd4U" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Prius v 2017|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius v 2017">Buy Here</a></sub></details>|||
|
|Toyota|Prius v 2017|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Prius v 2017">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2016">Buy Here</a></sub></details>|||
|
|Toyota|RAV4 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2016">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2017-18">Buy Here</a></sub></details>|||
|
|Toyota|RAV4 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2017-18">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=wJxjDd42gGA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2022">Buy Here</a></sub></details>|||
|
|Toyota|RAV4 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2022">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2023-25">Buy Here</a></sub></details>|||
|
|Toyota|RAV4 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 2023-25">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2016">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 Hybrid 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|RAV4 Hybrid 2017-18|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/LhT5VzJVfNI?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2019-21">Buy Here</a></sub></details>|||
|
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2019-21">Buy Here</a></sub></details>|||
|
||||||
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|RAV4 Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/U0nH9cnrFB0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota RAV4 Hybrid 2023-25">Buy Here</a></sub></details>|<a href="https://youtu.be/4eIsEq4L4Ng" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Toyota|Sienna 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Toyota|Sienna 2018-20|All|Stock|19 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Toyota A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Toyota Sienna 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=q1UPOo4Sh68" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon 2018-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon eHybrid 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon R 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon Shooting Brake 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Arteon Shooting Brake 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas 2018-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas Cross Sport 2020-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Atlas Cross Sport 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen California 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen California 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Caravelle 2020">Buy Here</a></sub></details>|||
|
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Caravelle 2020">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen CC 2018-22">Buy Here</a></sub></details>|<a href="https://youtu.be/FAomFKPFlDA" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Crafter 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Crafter 2017-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Crafter 2018-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Crafter 2018-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Golf 2014-20">Buy Here</a></sub></details>|||
|
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen e-Golf 2014-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf Alltrack 2015-19">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf Alltrack 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTD 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTD 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTE 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTE 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTI 2015-21">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf GTI 2015-21">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf R 2015-19">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf R 2015-19">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf SportsVan 2015-20">Buy Here</a></sub></details>|||
|
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Golf SportsVan 2015-20">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Grand California 2019-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Grand California 2019-24">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta 2019-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta 2019-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta GLI 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Jetta GLI 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Passat 2015-22[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat 2015-22[<sup>12</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat Alltrack 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat Alltrack 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat GTE 2015-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Passat GTE 2015-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo 2018-23">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Cross 2021">Buy Here</a></sub></details>[<sup>16</sup>](#footnotes)|||
|
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Cross 2021">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Roc 2018-23">Buy Here</a></sub></details>|||
|
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen T-Roc 2018-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Taos 2022-24">Buy Here</a></sub></details>|||
|
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Taos 2022-24">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont 2018-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont 2018-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont Cross Sport 2021-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont Cross Sport 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont X 2021-22">Buy Here</a></sub></details>|||
|
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Teramont X 2021-22">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan 2018-24">Buy Here</a></sub></details>|||
|
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan 2018-24">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan eHybrid 2021-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Tiguan eHybrid 2021-23">Buy Here</a></sub></details>|||
|
||||||
|Volkswagen[<sup>11</sup>](#footnotes)|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,15</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
||||||
|
|
||||||
### Footnotes
|
### Footnotes
|
||||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `nightly-dev`. <br />
|
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `nightly-dev`. <br />
|
||||||
@@ -360,12 +356,11 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
<sup>8</sup>Some 2023 model years have HW4. To check which hardware type your vehicle has, look for <b>Autopilot computer</b> under <b>Software -> Additional Vehicle Information</b> on your vehicle's touchscreen. See <a href="https://www.notateslaapp.com/news/2173/how-to-check-if-your-tesla-has-hardware-4-ai4-or-hardware-3">this page</a> for more information. <br />
|
<sup>8</sup>Some 2023 model years have HW4. To check which hardware type your vehicle has, look for <b>Autopilot computer</b> under <b>Software -> Additional Vehicle Information</b> on your vehicle's touchscreen. See <a href="https://www.notateslaapp.com/news/2173/how-to-check-if-your-tesla-has-hardware-4-ai4-or-hardware-3">this page</a> for more information. <br />
|
||||||
<sup>9</sup>See more setup details for <a href="https://github.com/commaai/openpilot/wiki/tesla" target="_blank">Tesla</a>. <br />
|
<sup>9</sup>See more setup details for <a href="https://github.com/commaai/openpilot/wiki/tesla" target="_blank">Tesla</a>. <br />
|
||||||
<sup>10</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
<sup>10</sup>openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||||
<sup>11</sup>The J533 harness plugs in at the CAN gateway under the dashboard, just above the steering column. More information can be found at <a href="https://docs.howtocomma.com/docs/j533-harness-install" target="_blank">this guide</a>. <br />
|
<sup>11</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||||
<sup>12</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
<sup>12</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||||
<sup>13</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
<sup>13</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma four functionality. <br />
|
||||||
<sup>14</sup>Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma four functionality. <br />
|
<sup>14</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
||||||
<sup>15</sup>Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC. <br />
|
<sup>15</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
||||||
<sup>16</sup>Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store. <br />
|
|
||||||
|
|
||||||
## Community Maintained Cars
|
## Community Maintained Cars
|
||||||
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
|
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ All of these are examples of good PRs:
|
|||||||
### First contribution
|
### First contribution
|
||||||
|
|
||||||
[Projects / openpilot bounties](https://github.com/orgs/commaai/projects/26/views/1?pane=info) is the best place to get started and goes in-depth on what's expected when working on a bounty.
|
[Projects / openpilot bounties](https://github.com/orgs/commaai/projects/26/views/1?pane=info) is the best place to get started and goes in-depth on what's expected when working on a bounty.
|
||||||
There are a lot of bounties that don't require a comma four or a car.
|
There are a lot of bounties that don't require a comma 3X or a car.
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# Docs development
|
|
||||||
|
|
||||||
The `docs/` tree is the source for [docs.comma.ai](https://docs.comma.ai).
|
|
||||||
The site is updated on pushes to master by this [workflow](../.github/workflows/docs.yaml).
|
|
||||||
|
|
||||||
Those commands must be run in the root directory of openpilot, **not /docs**
|
|
||||||
|
|
||||||
**1. Install the docs dependencies**
|
|
||||||
``` bash
|
|
||||||
uv pip install .[docs]
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Build the new site**
|
|
||||||
``` bash
|
|
||||||
docs build
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Run the new site locally**
|
|
||||||
``` bash
|
|
||||||
docs serve
|
|
||||||
```
|
|
||||||
|
|
||||||
References:
|
|
||||||
* https://zensical.org/docs/
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# openpilot docs
|
||||||
|
|
||||||
|
This is the source for [docs.comma.ai](https://docs.comma.ai).
|
||||||
|
The site is updated on pushes to master by this [workflow](../.github/workflows/docs.yaml).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
NOTE: Those commands must be run in the root directory of openpilot, **not /docs**
|
||||||
|
|
||||||
|
**1. Install the docs dependencies**
|
||||||
|
``` bash
|
||||||
|
uv pip install .[docs]
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Build the new site**
|
||||||
|
``` bash
|
||||||
|
mkdocs build
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Run the new site locally**
|
||||||
|
``` bash
|
||||||
|
mkdocs serve
|
||||||
|
```
|
||||||
|
|
||||||
|
References:
|
||||||
|
* https://www.mkdocs.org/getting-started/
|
||||||
|
* https://github.com/ntno/mkdocs-terminal
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../selfdrive/assets/icons_mici/settings/comma_icon.png
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Developing a car brand port
|
||||||
|
|
||||||
|
A brand port is a port of openpilot to a substantially new car brand or platform within a brand.
|
||||||
|
|
||||||
|
Here's an example of one: https://github.com/commaai/openpilot/pull/23331.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# CarState signals
|
||||||
|
|
||||||
|
## Required for basic lateral control
|
||||||
|
|
||||||
|
* `brakePressed`
|
||||||
|
* `cruiseState`
|
||||||
|
* `doorOpen`
|
||||||
|
* `espDisabled`
|
||||||
|
* `gasPressed`
|
||||||
|
* `gearShifter`
|
||||||
|
* `leftBlinker` / `rightBlinker`
|
||||||
|
* `seatbeltUnlatched`
|
||||||
|
* `standstill`
|
||||||
|
* `steeringAngleDeg`
|
||||||
|
* `steeringPressed`
|
||||||
|
* `steeringTorque`
|
||||||
|
* `steerFaultPermanent`
|
||||||
|
* `steerFaultTemporary`
|
||||||
|
* `vCruise`
|
||||||
|
* `wheelSpeeds.[fl|fr|rl|rr]`: Speed of each of the car's four wheels, in m/s. The car's CAN bus often broadcasts the
|
||||||
|
speed in kph, so the helper function `parse_wheel_speeds` performs this conversion by default.
|
||||||
|
|
||||||
|
## Recommended / Required for openpilot longitudinal control
|
||||||
|
|
||||||
|
* `accFaulted`
|
||||||
|
* `espActive`
|
||||||
|
* `parkingBrake`
|
||||||
|
|
||||||
|
## Application Dependent
|
||||||
|
|
||||||
|
* `blockPcmEnable`
|
||||||
|
* `buttonEnable`
|
||||||
|
* `brakeHoldActive`
|
||||||
|
* `carFaultedNonCritical`
|
||||||
|
* `invalidLkasSetting`
|
||||||
|
* `lowSpeedAlert`
|
||||||
|
* `regenBraking`
|
||||||
|
* `steeringAngleOffsetDeg`
|
||||||
|
* `steeringDisengage`
|
||||||
|
* `steeringTorqueEps`
|
||||||
|
* `stockLkas`
|
||||||
|
* `vCruiseCluster`
|
||||||
|
* `vEgoCluster`
|
||||||
|
* `vehicleSensorsInvalid`
|
||||||
|
|
||||||
|
## Automatically populated
|
||||||
|
|
||||||
|
* `buttonEvents`
|
||||||
|
|
||||||
|
These values are populated automatically by `parse_wheel_speeds`:
|
||||||
|
|
||||||
|
* `aEgo`: Acceleration of the ego vehicle, Kalman filtered derivative of `vEgo`.
|
||||||
|
* `vEgo`: Speed of the ego vehicle, Kalman filtered from `vEgoRaw`.
|
||||||
|
* `vEgoRaw`: Speed of the ego vehicle, based on the average of all four wheel speeds, unfiltered.
|
||||||
|
|
||||||
|
## Optional
|
||||||
|
|
||||||
|
* `brake`
|
||||||
|
* `charging`
|
||||||
|
* `fuelGauge`
|
||||||
|
* `leftBlindspot` / `rightBlindspot`
|
||||||
|
* `steeringRateDeg`
|
||||||
|
* `stockAeb`
|
||||||
|
* `stockFcw`
|
||||||
|
* `yawRate`
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Developing a car model port
|
||||||
|
|
||||||
|
A model port is a port of openpilot to a new car model within an already supported brand. Model ports are easier than brand ports because the car's existing APIs are already known.
|
||||||
|
|
||||||
|
Here's an example of one: https://github.com/commaai/openpilot/pull/30672/.
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# Stimulus-Response Tests
|
||||||
|
|
||||||
|
These are example test drives that can help identify the CAN bus messaging necessary for ADAS control. Each scripted
|
||||||
|
test should be done in a separate route (ignition cycle). These tests are a guide, not necessarily exhaustive.
|
||||||
|
|
||||||
|
While testing, constant power to the comma device is highly recommended, using [comma power](https://comma.ai/shop/comma-power) if
|
||||||
|
necessary to make sure all test activity is fully captured and for ease of uploading. If constant power isn't
|
||||||
|
available, keep the ignition on for at least one minute after your test to make sure power loss doesn't result
|
||||||
|
in loss of the last minute of testing data.
|
||||||
|
|
||||||
|
## Stationary ignition-only tests, part 1
|
||||||
|
|
||||||
|
1. Ignition on, but don't start engine, remain in Park
|
||||||
|
2. Open and close each door in a defined order: driver, passenger, rear left, rear right
|
||||||
|
3. Re-enter the vehicle, close the driver's door, and fasten the driver's seatbelt
|
||||||
|
4. Slowly press and release the accelerator pedal 3 times
|
||||||
|
5. Slowly press and release the brake pedal 3 times
|
||||||
|
6. Hold the brake and move the gearshift to reverse, then neutral, then drive, then sport/eco/etc if applicable
|
||||||
|
7. Return to Park, ignition off
|
||||||
|
|
||||||
|
Brake-pressed information may show up in several messages and signals, both as on/off states and as a percentage or
|
||||||
|
pressure. It may reflect a switch on the driver's brake pedal, or a pressure-threshold state, or signals to turn on
|
||||||
|
the rear brake lights. Start by identifying all the potential signals, and confirm while driving with ACC later.
|
||||||
|
|
||||||
|
Locate signals for all four door states if possible, but some cars only expose the driver's door state on the ADAS bus.
|
||||||
|
Driver/passenger door signals may or may not change positions for LHD vs RHD cars. For cars where only the driver's
|
||||||
|
door signal is available, the same signal may follow the driver.
|
||||||
|
|
||||||
|
## Stationary ignition-only tests, part 2
|
||||||
|
|
||||||
|
1. Ignition on, but don't start engine, remain in Park
|
||||||
|
2. Press each ACC button in a defined order: main switch on/off, set, resume, cancel, accel, decel, gap adjust
|
||||||
|
3. Set the left turn signal for about five seconds
|
||||||
|
4. Operate the left turn signal one time in its touch-to-pass mode
|
||||||
|
5. Set the right turn signal for about five seconds
|
||||||
|
6. Operate the right turn signal one time in its touch-to-pass mode
|
||||||
|
7. Set the hazard / emergency indicator switch for about five seconds
|
||||||
|
8. Ignition off
|
||||||
|
|
||||||
|
Your vehicle may have a momentary-press main ACC switch or a physical toggle that remains set. Actual ACC engagement
|
||||||
|
isn't necessary for purposes of detecting the ACC button presses.
|
||||||
|
|
||||||
|
## Steering angle and steering torque tests
|
||||||
|
|
||||||
|
Power steering should be available. On ICE cars, engine RPM may be present.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, remain in Park
|
||||||
|
2. Rotate the steering wheel as follows, with a few seconds pause between each step
|
||||||
|
* Start as close to exact center as possible
|
||||||
|
* Turn to 45 degrees right and hold
|
||||||
|
* Turn to 90 degrees right and hold
|
||||||
|
* Turn to 180 degrees right and hold
|
||||||
|
* Turn to full lock right and hold, with firm pressure against lock
|
||||||
|
* Release the wheel and allow it to bounce back slightly from lock
|
||||||
|
* Turn to 180 degrees left and hold
|
||||||
|
* Return to center and release
|
||||||
|
3. Ignition off
|
||||||
|
|
||||||
|
Performing the full test to the right, followed by an abbreviated test to the left, helps give additional confirmation
|
||||||
|
of signal scale, and sign/direction for both the steering wheel angle and driver input torque signals.
|
||||||
|
|
||||||
|
## Low speed / parking lot driving tests
|
||||||
|
|
||||||
|
Before this test, drive to a place like an empty parking lot where you are free to drive in a series of curves.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, prepare to drive
|
||||||
|
2. Slowly (10-20mph at most) drive a figure-8 if possible, or at least one sharp left and one sharp right.
|
||||||
|
3. Come to a complete stop
|
||||||
|
4. When and where safe, drive in reverse for a short distance (10-15 feet)
|
||||||
|
5. Park the car in a safe place, ignition off
|
||||||
|
|
||||||
|
## High speed / highway driving tests
|
||||||
|
|
||||||
|
Select a place and time where you can safely set cruise control at normal travel speeds with little interference from
|
||||||
|
traffic ahead, and safely test the response of your factory lane guidance system.
|
||||||
|
|
||||||
|
1. Ignition on, start engine if applicable, prepare to drive
|
||||||
|
2. When safely able, engage adaptive cruise control below 50 mph
|
||||||
|
3. When safely able, use the ACC buttons to accelerate to 50mph, then 55mph, then 60mph
|
||||||
|
4. Disengage adaptive cruise
|
||||||
|
5. When safely able, allow your factory lane guidance to prevent lane departures, 2-3 times on both the left and right
|
||||||
|
|
||||||
|
The series of setpoints can be adjusted to local traffic regulations, and of course metric units. The specific cruise
|
||||||
|
setpoints are useful for locating the ACC HUD signals later, and confirming their precise scaling. When the car reaches
|
||||||
|
and holds the setpoint, that can also provide additional confirmation of wheel speed scaling.
|
||||||
@@ -8,7 +8,7 @@ A car port enables openpilot support on a particular car. Each car model openpil
|
|||||||
|
|
||||||
# Structure of a car port
|
# Structure of a car port
|
||||||
|
|
||||||
All car-specific code is contained in the [opendbc](https://github.com/commaai/opendbc) project.
|
Virtually all car-specific code is contained in two other repositories: [opendbc](https://github.com/commaai/opendbc) and [panda](https://github.com/commaai/panda).
|
||||||
|
|
||||||
## opendbc
|
## opendbc
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@ Each car brand is supported by a standard interface structure in `opendbc/car/[b
|
|||||||
|
|
||||||
## safety
|
## safety
|
||||||
|
|
||||||
* `opendbc/safety/modes/[brand].h`: Brand-specific safety logic
|
* `opendbc_repo/opendbc/safety/modes/[brand].h`: Brand-specific safety logic
|
||||||
* `opendbc/safety/tests/test_[brand].py`: Brand-specific safety CI tests
|
* `opendbc_repo/opendbc/safety/tests/test_[brand].py`: Brand-specific safety CI tests
|
||||||
|
|
||||||
## openpilot
|
## openpilot
|
||||||
|
|
||||||
@@ -32,20 +32,8 @@ For historical reasons, openpilot still contains a small amount of car-specific
|
|||||||
|
|
||||||
* `selfdrive/car/car_specific.py`: Brand-specific event logic
|
* `selfdrive/car/car_specific.py`: Brand-specific event logic
|
||||||
|
|
||||||
# How do I port car?
|
# Overview
|
||||||
|
|
||||||
[Jason Young](https://github.com/jyoung8607) gave a talk at COMMA_CON with an overview of the car porting process. The talk is available on YouTube:
|
[Jason Young](https://github.com/jyoung8607) gave a talk at COMMA_CON with an overview of the car porting process. The talk is available on YouTube:
|
||||||
|
|
||||||
https://www.youtube.com/watch?v=XxPS5TpTUnI
|
https://www.youtube.com/watch?v=XxPS5TpTUnI
|
||||||
|
|
||||||
## Brand Port
|
|
||||||
|
|
||||||
A brand port is a port of openpilot to a substantially new car brand or platform within a brand.
|
|
||||||
|
|
||||||
Here's an example of one: https://github.com/commaai/openpilot/pull/23331.
|
|
||||||
|
|
||||||
## Model Port
|
|
||||||
|
|
||||||
A model port is a port of openpilot to a new car model within an already supported brand. Model ports are easier than brand ports because the car's existing APIs are already known.
|
|
||||||
|
|
||||||
Here's an example of one: https://github.com/commaai/openpilot/pull/30672/.
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
# openpilot glossary
|
# openpilot glossary
|
||||||
|
|
||||||
{{GLOSSARY_DEFINITIONS}}
|
* **onroad**: openpilot's system state while ignition is on
|
||||||
|
* **offroad**: openpilot's system state while ignition is off
|
||||||
|
* **route**: a route is a recording of an onroad session
|
||||||
|
* **segment**: routes are split into one minute chunks called segments.
|
||||||
|
* **comma connect**: the web viewer for all your routes; check it out at [connect.comma.ai](https://connect.comma.ai).
|
||||||
|
* **panda**: this is the secondary processor on the device that implements the functional safety and directly talks to the car over CAN. See the [panda repo](https://github.com/commaai/panda).
|
||||||
|
* **comma 3X**: the latest hardware by comma.ai for running openpilot. more info at [comma.ai/shop](https://comma.ai/shop).
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ Check out our [Python library](https://github.com/commaai/openpilot/blob/master/
|
|||||||
|
|
||||||
For each segment, openpilot records the following log types:
|
For each segment, openpilot records the following log types:
|
||||||
|
|
||||||
## rlog.zst
|
## rlog.bz2
|
||||||
|
|
||||||
rlogs contain all the messages passed amongst openpilot's processes. See [cereal/services.py](https://github.com/commaai/openpilot/blob/master/cereal/services.py) for a list of all the logged services. They're a zstd archive of the serialized [Cap’n Proto](https://capnproto.org/) messages.
|
rlogs contain all the messages passed amongst openpilot's processes. See [cereal/services.py](https://github.com/commaai/cereal/blob/master/services.py) for a list of all the logged services. They're a bzip2 archive of the serialized capnproto messages.
|
||||||
|
|
||||||
## {f,e,d}camera.hevc
|
## {f,e,d}camera.hevc
|
||||||
|
|
||||||
@@ -18,10 +18,12 @@ Each camera stream is H.265 encoded and written to its respective file.
|
|||||||
* `ecamera.hevc` is the wide road camera
|
* `ecamera.hevc` is the wide road camera
|
||||||
* `dcamera.hevc` is the driver camera
|
* `dcamera.hevc` is the driver camera
|
||||||
|
|
||||||
## qlog.zst & qcamera.ts
|
## qlog.bz2 & qcamera.ts
|
||||||
|
|
||||||
qlogs are a decimated subset of the rlogs. Check out [cereal/services.py](https://github.com/commaai/cereal/blob/master/services.py) for the decimation.
|
qlogs are a decimated subset of the rlogs. Check out [cereal/services.py](https://github.com/commaai/cereal/blob/master/services.py) for the decimation.
|
||||||
|
|
||||||
|
|
||||||
qcameras are H.264 encoded, lower res versions of the fcamera.hevc. The video shown in [comma connect](https://connect.comma.ai/) is from the qcameras.
|
qcameras are H.264 encoded, lower res versions of the fcamera.hevc. The video shown in [comma connect](https://connect.comma.ai/) is from the qcameras.
|
||||||
|
|
||||||
qlogs and qcameras are designed to be small enough to upload instantly on slow internet, yet useful enough for most analysis and debugging.
|
|
||||||
|
qlogs and qcameras are designed to be small enough to upload instantly on slow internet and store forever, yet useful enough for most analysis and debugging.
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
# How to Give Feedback
|
|
||||||
|
|
||||||
Feedback is one of the highest leverage ways to contribute to openpilot as a user.
|
|
||||||
|
|
||||||
## Driving
|
|
||||||
|
|
||||||
Got feedback about how your car drives?
|
|
||||||
Join the community Discord, then use the form in `#submit-feedback`.
|
|
||||||
|
|
||||||
Before posting feedback, please ensure:
|
|
||||||
|
|
||||||
- **openpilot is up to date** you should be on the latest openpilot release or nightly
|
|
||||||
- **both road-facing cameras have a clear view** your windshield is clean, lenses are clean, etc.
|
|
||||||
- **your device is mounted properly** your device must be mounted horizontally center and relatively high on the windshield
|
|
||||||
|
|
||||||
## Driver Monitoring
|
|
||||||
|
|
||||||
If you find DM annoying while being perfectly attentive, these are likely false positives and we want to fix them!
|
|
||||||
In general, driver monitoring feedback is very actionable, and we can fix your complaint within a release cycle.
|
|
||||||
|
|
||||||
To post your feedback:
|
|
||||||
|
|
||||||
1. Join the [community Discord](https://discord.comma.ai).
|
|
||||||
2. If driver camera recording is toggled off, temporarily enable driver camera recording in the settings until you reproduce the issue.
|
|
||||||
3. Using comma connect, identify the relevant segment and upload the segment's logs and driver camera.
|
|
||||||
4. Post the segment in the `#openpilot-experience` channel on Discord with a good description.
|
|
||||||
|
|
||||||
Before posting feedback, please ensure:
|
|
||||||
|
|
||||||
- **openpilot is up to date** you should be on the latest openpilot release or nightly
|
|
||||||
- **the driver camera has a clear view of the driver** ensure nothing blocks view of the driver (e.g. a cable), the lens is clean, etc.
|
|
||||||
- **your device is mounted properly** your device must be mounted horizontally center and relatively high on the windshield
|
|
||||||
|
|
||||||
## Other bugs
|
|
||||||
|
|
||||||
Got an issue with something else? Open an issue on our [GitHub issue tracker](https://github.com/commaai/openpilot/issues/new/choose).
|
|
||||||
@@ -7,11 +7,25 @@ This is the roadmap for the next major openpilot releases. Also check out
|
|||||||
* [Bounties](https://comma.ai/bounties) for paid individual issues
|
* [Bounties](https://comma.ai/bounties) for paid individual issues
|
||||||
* [#current-projects](https://discord.com/channels/469524606043160576/1249579909739708446) in Discord for discussion on work-in-progress projects
|
* [#current-projects](https://discord.com/channels/469524606043160576/1249579909739708446) in Discord for discussion on work-in-progress projects
|
||||||
|
|
||||||
|
## openpilot 0.10
|
||||||
|
|
||||||
|
openpilot 0.10 will be the first release with a driving policy trained in
|
||||||
|
a [learned simulator](https://youtu.be/EqQNZXqzFSI).
|
||||||
|
|
||||||
|
* Driving model trained in a learned simulator
|
||||||
|
* Always-on driver monitoring (behind a toggle)
|
||||||
|
* GPS removed from the driving stack
|
||||||
|
* 100KB qlogs
|
||||||
|
* `nightly` pushed after 1000 hours of hardware-in-the-loop testing
|
||||||
|
* Car interface code moved into [opendbc](https://github.com/commaai/opendbc)
|
||||||
|
* openpilot on PC for Linux x86, Linux arm64, and Mac (Apple Silicon)
|
||||||
|
|
||||||
## openpilot 1.0
|
## openpilot 1.0
|
||||||
|
|
||||||
openpilot 1.0 will feature a fully end-to-end driving policy.
|
openpilot 1.0 will feature a fully end-to-end driving policy.
|
||||||
|
|
||||||
* End-to-end longitudinal control in Chill mode
|
* End-to-end longitudinal control in Chill mode
|
||||||
|
* Automatic Emergency Braking (AEB)
|
||||||
* Driver monitoring with sleep detection
|
* Driver monitoring with sleep detection
|
||||||
* Rolling updates/releases pushed out by CI
|
* Rolling updates/releases pushed out by CI
|
||||||
* [panda safety 1.0](https://github.com/orgs/commaai/projects/27)
|
* [panda safety 1.0](https://github.com/orgs/commaai/projects/27)
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
[data-tooltip] {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
border-bottom: 1px dotted black;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-tooltip] .tooltip-content {
|
||||||
|
width: max-content;
|
||||||
|
max-width: 25em;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: white;
|
||||||
|
color: #404040;
|
||||||
|
box-shadow: 0 4px 14px 0 rgba(0,0,0,.2), 0 0 0 1px rgba(0,0,0,.05);
|
||||||
|
padding: 10px;
|
||||||
|
font: 14px/1.5 Lato, proxima-nova, Helvetica Neue, Arial, sans-serif;
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity 0.1s, visibility 0s;
|
||||||
|
z-index: 1000;
|
||||||
|
pointer-events: none; /* Prevent accidental interaction */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-tooltip]:hover .tooltip-content {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto; /* Allow interaction when visible */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-content .tooltip-glossary-link {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-content .tooltip-glossary-link:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
import posixpath
|
|
||||||
import re
|
|
||||||
import tomllib
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from markdown.extensions import Extension
|
|
||||||
from markdown.preprocessors import Preprocessor
|
|
||||||
from markdown.treeprocessors import Treeprocessor
|
|
||||||
|
|
||||||
from zensical.extensions.links import LinksProcessor
|
|
||||||
|
|
||||||
GlossaryTerm = tuple[str, re.Pattern[str], str]
|
|
||||||
|
|
||||||
GLOSSARY_FILE = Path(__file__).with_name("glossary.toml")
|
|
||||||
GLOSSARY_PAGE = "concepts/glossary.md"
|
|
||||||
GLOSSARY_PLACEHOLDER = "{{GLOSSARY_DEFINITIONS}}"
|
|
||||||
|
|
||||||
SKIP_TAGS = {
|
|
||||||
"a",
|
|
||||||
"code",
|
|
||||||
"h1",
|
|
||||||
"h2",
|
|
||||||
"h3",
|
|
||||||
"h4",
|
|
||||||
"h5",
|
|
||||||
"h6",
|
|
||||||
"kbd",
|
|
||||||
"pre",
|
|
||||||
"script",
|
|
||||||
"style",
|
|
||||||
}
|
|
||||||
|
|
||||||
def clean_tooltip(description: str) -> str:
|
|
||||||
text = re.sub(r"\[([^\]]+)]\([^)]+\)", r"\1", description)
|
|
||||||
text = re.sub(r"`([^`]+)`", r"\1", text)
|
|
||||||
text = re.sub(r"[*_~]", "", text)
|
|
||||||
return re.sub(r"\s+", " ", text).strip()
|
|
||||||
|
|
||||||
|
|
||||||
def load_glossary() -> tuple[list[GlossaryTerm], str]:
|
|
||||||
with GLOSSARY_FILE.open("rb") as f:
|
|
||||||
glossary_data = tomllib.load(f).get("glossary", {})
|
|
||||||
|
|
||||||
glossary: list[GlossaryTerm] = []
|
|
||||||
rendered = []
|
|
||||||
for key, value in glossary_data.items():
|
|
||||||
label = str(key).strip().replace("_", " ")
|
|
||||||
description = str(value).strip()
|
|
||||||
if not description:
|
|
||||||
continue
|
|
||||||
|
|
||||||
slug = label.replace(" ", "-").replace("_", "-").lower()
|
|
||||||
glossary.append((slug, re.compile(rf"(?<!\w){re.escape(label)}(?!\w)", re.IGNORECASE), clean_tooltip(description)))
|
|
||||||
rendered.append(f'* <span id="{slug}"></span>**{label}**: {description}')
|
|
||||||
|
|
||||||
return glossary, "\n".join(rendered)
|
|
||||||
|
|
||||||
|
|
||||||
class GlossaryPreprocessor(Preprocessor):
|
|
||||||
def __init__(self, md, glossary: str):
|
|
||||||
super().__init__(md)
|
|
||||||
self.glossary = glossary
|
|
||||||
|
|
||||||
def run(self, lines: list[str]) -> list[str]:
|
|
||||||
markdown = "\n".join(lines)
|
|
||||||
if GLOSSARY_PLACEHOLDER not in markdown:
|
|
||||||
return lines
|
|
||||||
return markdown.replace(GLOSSARY_PLACEHOLDER, self.glossary).splitlines()
|
|
||||||
|
|
||||||
|
|
||||||
class GlossaryTreeprocessor(Treeprocessor):
|
|
||||||
def __init__(self, md, glossary: list[GlossaryTerm]):
|
|
||||||
super().__init__(md)
|
|
||||||
self.glossary = glossary
|
|
||||||
self.seen: set[str] = set()
|
|
||||||
|
|
||||||
def run(self, root: ET.Element) -> None:
|
|
||||||
at = self.md.treeprocessors.get_index_for_name("zrelpath")
|
|
||||||
processor = self.md.treeprocessors[at]
|
|
||||||
if not isinstance(processor, LinksProcessor):
|
|
||||||
raise TypeError("Links processor not registered")
|
|
||||||
if processor.path == GLOSSARY_PAGE:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.seen.clear()
|
|
||||||
glossary_href = f"{posixpath.relpath(GLOSSARY_PAGE, posixpath.dirname(processor.path) or '.')}#"
|
|
||||||
self._walk(root, glossary_href)
|
|
||||||
|
|
||||||
def _walk(self, element: ET.Element, glossary_href: str) -> None:
|
|
||||||
if element.tag in SKIP_TAGS or element.attrib.get("data-glossary-skip") is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._replace(element, glossary_href)
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
while idx < len(element):
|
|
||||||
child = element[idx]
|
|
||||||
self._walk(child, glossary_href)
|
|
||||||
idx = self._replace(element, glossary_href, idx) + 1
|
|
||||||
|
|
||||||
def _replace(self, parent: ET.Element, glossary_href: str, index: int | None = None) -> int:
|
|
||||||
child = None if index is None else parent[index]
|
|
||||||
text = parent.text if child is None else child.tail
|
|
||||||
pieces = self._pieces(text or "", glossary_href)
|
|
||||||
if not pieces:
|
|
||||||
return -1 if index is None else index
|
|
||||||
|
|
||||||
if child is None:
|
|
||||||
parent.text = pieces[0] if isinstance(pieces[0], str) else ""
|
|
||||||
# Insert replacements for parent.text before the first existing child.
|
|
||||||
insert_at = -1
|
|
||||||
else:
|
|
||||||
assert index is not None
|
|
||||||
child.tail = pieces[0] if isinstance(pieces[0], str) else ""
|
|
||||||
insert_at = index
|
|
||||||
|
|
||||||
start = 1 if isinstance(pieces[0], str) else 0
|
|
||||||
previous = child
|
|
||||||
|
|
||||||
for piece in pieces[start:]:
|
|
||||||
if isinstance(piece, str):
|
|
||||||
previous.tail = (previous.tail or "") + piece
|
|
||||||
continue
|
|
||||||
|
|
||||||
insert_at += 1
|
|
||||||
parent.insert(insert_at, piece)
|
|
||||||
previous = piece
|
|
||||||
|
|
||||||
return insert_at
|
|
||||||
|
|
||||||
def _pieces(self, text: str, glossary_href: str) -> list[str | ET.Element]:
|
|
||||||
if not text.strip():
|
|
||||||
return []
|
|
||||||
|
|
||||||
pieces: list[str | ET.Element] = []
|
|
||||||
cursor = 0
|
|
||||||
|
|
||||||
while True:
|
|
||||||
best = None
|
|
||||||
for slug, pattern, tooltip in self.glossary:
|
|
||||||
if slug in self.seen:
|
|
||||||
continue
|
|
||||||
|
|
||||||
found = pattern.search(text, cursor)
|
|
||||||
if found is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
candidate = (slug, tooltip, found.start(), found.end())
|
|
||||||
if best is None:
|
|
||||||
best = candidate
|
|
||||||
continue
|
|
||||||
|
|
||||||
_, _, best_start, best_end = best
|
|
||||||
_, _, current_start, current_end = candidate
|
|
||||||
if current_start < best_start:
|
|
||||||
best = candidate
|
|
||||||
continue
|
|
||||||
|
|
||||||
if current_start == best_start and current_end - current_start > best_end - best_start:
|
|
||||||
best = candidate
|
|
||||||
|
|
||||||
if best is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
slug, tooltip, start, end = best
|
|
||||||
if start > cursor:
|
|
||||||
pieces.append(text[cursor:start])
|
|
||||||
|
|
||||||
link = ET.Element(
|
|
||||||
"a",
|
|
||||||
{
|
|
||||||
"class": "glossary-term",
|
|
||||||
"data-glossary-term": "",
|
|
||||||
"href": f"{glossary_href}{slug}",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
ET.SubElement(link, "span", {"class": "glossary-term__label"}).text = text[start:end]
|
|
||||||
ET.SubElement(
|
|
||||||
link,
|
|
||||||
"span",
|
|
||||||
{
|
|
||||||
"class": "glossary-term__tooltip",
|
|
||||||
"data-search-exclude": "",
|
|
||||||
},
|
|
||||||
).text = tooltip
|
|
||||||
pieces.append(link)
|
|
||||||
self.seen.add(slug)
|
|
||||||
cursor = end
|
|
||||||
|
|
||||||
if not pieces:
|
|
||||||
return []
|
|
||||||
if cursor < len(text):
|
|
||||||
pieces.append(text[cursor:])
|
|
||||||
return pieces
|
|
||||||
|
|
||||||
|
|
||||||
class GlossaryExtension(Extension):
|
|
||||||
def extendMarkdown(self, md) -> None:
|
|
||||||
md.registerExtension(self)
|
|
||||||
glossary, rendered = load_glossary()
|
|
||||||
|
|
||||||
md.preprocessors.register(
|
|
||||||
GlossaryPreprocessor(md, rendered),
|
|
||||||
"docs-ext-glossary-preprocessor",
|
|
||||||
27,
|
|
||||||
)
|
|
||||||
md.treeprocessors.register(
|
|
||||||
GlossaryTreeprocessor(md, glossary),
|
|
||||||
"docs-ext-glossary-treeprocessor",
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def makeExtension(**kwargs) -> GlossaryExtension:
|
|
||||||
return GlossaryExtension(**kwargs)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[glossary]
|
|
||||||
onroad = "openpilot's system state while ignition is on."
|
|
||||||
offroad = "openpilot's system state while ignition is off."
|
|
||||||
route = "A route is a recording of an onroad session."
|
|
||||||
segment = "Routes are split into one minute chunks called segments."
|
|
||||||
"comma connect" = "The web viewer for all your routes; check it out at [connect.comma.ai](https://connect.comma.ai)."
|
|
||||||
panda = "The secondary processor on the device that implements the functional safety and directly talks to the car over CAN. See the [panda repo](https://github.com/commaai/panda)."
|
|
||||||
"comma four" = "The latest hardware by comma.ai for running openpilot. More info at [comma.ai/shop/comma-four](https://www.comma.ai/shop/comma-four)."
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# What is openpilot?
|
||||||
|
|
||||||
|
[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW), and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models, and model years](https://github.com/commaai/openpilot/blob/master/docs/CARS.md). In addition, while openpilot is engaged, a camera-based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](https://github.com/commaai/openpilot/blob/master/docs/INTEGRATION.md) and [limitations](https://github.com/commaai/openpilot/blob/master/docs/LIMITATIONS.md).
|
||||||
|
|
||||||
|
|
||||||
|
## How do I use it?
|
||||||
|
|
||||||
|
openpilot is designed to be used on the comma 3X.
|
||||||
|
|
||||||
|
## How does it work?
|
||||||
|
|
||||||
|
In short, openpilot uses the car's existing APIs for the built-in [ADAS](https://en.wikipedia.org/wiki/Advanced_driver-assistance_system) system and simply provides better acceleration, braking, and steering inputs than the stock system.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import re
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
def load_glossary(file_path="docs/glossary.toml"):
|
||||||
|
with open(file_path, "rb") as f:
|
||||||
|
glossary_data = tomllib.load(f)
|
||||||
|
return glossary_data.get("glossary", {})
|
||||||
|
|
||||||
|
def generate_anchor_id(name):
|
||||||
|
return name.replace(" ", "-").replace("_", "-").lower()
|
||||||
|
|
||||||
|
def format_markdown_term(name, definition):
|
||||||
|
anchor_id = generate_anchor_id(name)
|
||||||
|
markdown = f"* [**{name.replace('_', ' ').title()}**](#{anchor_id})"
|
||||||
|
if definition.get("abbreviation"):
|
||||||
|
markdown += f" *({definition['abbreviation']})*"
|
||||||
|
if definition.get("description"):
|
||||||
|
markdown += f": {definition['description']}\n"
|
||||||
|
return markdown
|
||||||
|
|
||||||
|
def glossary_markdown(vocabulary):
|
||||||
|
markdown = ""
|
||||||
|
for category, terms in vocabulary.items():
|
||||||
|
markdown += f"## {category.replace('_', ' ').title()}\n\n"
|
||||||
|
for name, definition in terms.items():
|
||||||
|
markdown += format_markdown_term(name, definition)
|
||||||
|
return markdown
|
||||||
|
|
||||||
|
def format_tooltip_html(term_key, definition, html):
|
||||||
|
display_term = term_key.replace("_", " ").title()
|
||||||
|
clean_description = re.sub(r"\[(.+)]\(.+\)", r"\1", definition["description"])
|
||||||
|
glossary_link = (
|
||||||
|
f"<a href='/concepts/glossary#{term_key}' class='tooltip-glossary-link' title='View in glossary'>Glossary🔗</a>"
|
||||||
|
)
|
||||||
|
return re.sub(
|
||||||
|
re.escape(display_term),
|
||||||
|
lambda
|
||||||
|
match: f"<span data-tooltip>{match.group(0)}<span class='tooltip-content'>{clean_description} {glossary_link}</span></span>",
|
||||||
|
html,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply_tooltip(_term_key, _definition, pattern, html):
|
||||||
|
return re.sub(
|
||||||
|
pattern,
|
||||||
|
lambda match: format_tooltip_html(_term_key, _definition, match.group(0)),
|
||||||
|
html,
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def tooltip_html(vocabulary, html):
|
||||||
|
for _category, terms in vocabulary.items():
|
||||||
|
for term_key, definition in terms.items():
|
||||||
|
if definition.get("description"):
|
||||||
|
pattern = rf"(?<!\w){re.escape(term_key.replace('_', ' ').title())}(?![^<]*<\/a>)(?!\([^)]*\))"
|
||||||
|
html = apply_tooltip(term_key, definition, pattern, html)
|
||||||
|
return html
|
||||||
|
|
||||||
|
# Page Hooks
|
||||||
|
def on_page_markdown(markdown, **kwargs):
|
||||||
|
glossary = load_glossary()
|
||||||
|
return markdown.replace("{{GLOSSARY_DEFINITIONS}}", glossary_markdown(glossary))
|
||||||
|
|
||||||
|
def on_page_content(html, **kwargs):
|
||||||
|
if kwargs.get("page").title == "Glossary":
|
||||||
|
return html
|
||||||
|
glossary = load_glossary()
|
||||||
|
return tooltip_html(glossary, html)
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
# connect to a comma 3X or comma four
|
# connect to a comma 3X
|
||||||
|
|
||||||
A comma device is a normal [Linux](https://github.com/commaai/agnos-builder) computer that exposes [SSH](https://wiki.archlinux.org/title/Secure_Shell) and a [serial console](https://wiki.archlinux.org/title/Working_with_the_serial_console).
|
A comma 3X is a normal [Linux](https://github.com/commaai/agnos-builder) computer that exposes [SSH](https://wiki.archlinux.org/title/Secure_Shell) and a [serial console](https://wiki.archlinux.org/title/Working_with_the_serial_console).
|
||||||
|
|
||||||
## Serial Console
|
## Serial Console
|
||||||
|
|
||||||
On the comma 3X, the serial console is accessible from the main OBD-C port, forwarded through the panda.
|
On both the comma three and 3X, the serial console is accessible from the main OBD-C port.
|
||||||
Access it using `panda/scripts/som_debug.sh`.
|
Connect the comma 3X to your computer with a normal USB C cable, or use a [comma serial](https://comma.ai/shop/comma-serial) for steady 12V power.
|
||||||
|
|
||||||
comma four also exposes a serial console, albeit through an internal debug connector. Dedicated debug hardware coming soon to the comma shop.
|
On the comma three, the serial console is exposed through a UART-to-USB chip, and `tools/scripts/serial.sh` can be used to connect.
|
||||||
|
|
||||||
Login to the default user with:
|
On the comma 3X, the serial console is accessible through the [panda](https://github.com/commaai/panda) using the `panda/tests/som_debug.sh` script.
|
||||||
|
|
||||||
* Username: `comma`
|
* Username: `comma`
|
||||||
* Password: `comma`
|
* Password: `comma`
|
||||||
@@ -25,7 +25,7 @@ In order to SSH into your device, you'll need a GitHub account with SSH keys. Se
|
|||||||
* Port: `22`
|
* Port: `22`
|
||||||
|
|
||||||
Here's an example command for connecting to your device using its tethered connection:<br />
|
Here's an example command for connecting to your device using its tethered connection:<br />
|
||||||
`ssh comma@192.168.43.1 -i ~/.ssh/my_github_key`
|
`ssh comma@192.168.43.1`
|
||||||
|
|
||||||
For doing development work on device, it's recommended to use [SSH agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding).
|
For doing development work on device, it's recommended to use [SSH agent forwarding](https://docs.github.com/en/developers/overview/using-ssh-agent-forwarding).
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ In order to use ADB on your device, you'll need to perform the following steps u
|
|||||||
* Here's an example command for connecting to your device using its tethered connection: `adb connect 192.168.43.1:5555`
|
* Here's an example command for connecting to your device using its tethered connection: `adb connect 192.168.43.1:5555`
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The default port for ADB is 5555.
|
> The default port for ADB is 5555 on the comma 3X.
|
||||||
|
|
||||||
For more info on ADB, see the [Android Debug Bridge (ADB) documentation](https://developer.android.com/tools/adb).
|
For more info on ADB, see the [Android Debug Bridge (ADB) documentation](https://developer.android.com/tools/adb).
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ The public keys are only fetched from your GitHub account once. In order to upda
|
|||||||
|
|
||||||
The `id_rsa` key in this directory only works while your device is in the setup state with no software installed. After installation, that default key will be removed.
|
The `id_rsa` key in this directory only works while your device is in the setup state with no software installed. After installation, that default key will be removed.
|
||||||
|
|
||||||
## ssh.comma.ai proxy
|
#### ssh.comma.ai proxy
|
||||||
|
|
||||||
With a [comma prime subscription](https://comma.ai/connect), you can SSH into your comma device from anywhere.
|
With a [comma prime subscription](https://comma.ai/connect), you can SSH into your comma device from anywhere.
|
||||||
|
|
||||||
@@ -79,7 +79,6 @@ Host ssh.comma.ai
|
|||||||
```
|
```
|
||||||
ssh -i ~/.ssh/my_github_key -o ProxyCommand="ssh -i ~/.ssh/my_github_key -W %h:%p -p %p %h@ssh.comma.ai" comma@ffffffffffffffff
|
ssh -i ~/.ssh/my_github_key -o ProxyCommand="ssh -i ~/.ssh/my_github_key -W %h:%p -p %p %h@ssh.comma.ai" comma@ffffffffffffffff
|
||||||
```
|
```
|
||||||
|
|
||||||
(Replace `ffffffffffffffff` with your dongle_id)
|
(Replace `ffffffffffffffff` with your dongle_id)
|
||||||
|
|
||||||
### ssh.comma.ai host key fingerprint
|
### ssh.comma.ai host key fingerprint
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Replaying is a critical tool for openpilot development and debugging.
|
|||||||
Just run `tools/replay/replay --demo`.
|
Just run `tools/replay/replay --demo`.
|
||||||
|
|
||||||
## Replaying CAN data
|
## Replaying CAN data
|
||||||
*Hardware required: jungle and comma four*
|
*Hardware required: jungle and comma 3X*
|
||||||
|
|
||||||
1. Connect your PC to a jungle.
|
1. Connect your PC to a jungle.
|
||||||
2.
|
2.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
In 30 minutes, we'll get an openpilot development environment set up on your computer and make some changes to openpilot's UI.
|
In 30 minutes, we'll get an openpilot development environment set up on your computer and make some changes to openpilot's UI.
|
||||||
|
|
||||||
And if you have a comma four, we'll deploy the change to your device for testing.
|
And if you have a comma 3X, we'll deploy the change to your device for testing.
|
||||||
|
|
||||||
## 1. Set up your development environment
|
## 1. Set up your development environment
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
# What is openpilot?
|
|
||||||
|
|
||||||
[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, openpilot performs the functions of Adaptive Cruise Control (ACC), Automated Lane Centering (ALC), Forward Collision Warning (FCW), and Lane Departure Warning (LDW) for a growing variety of [supported car makes, models, and model years](https://github.com/commaai/openpilot/blob/master/docs/CARS.md). In addition, while openpilot is engaged, a camera-based Driver Monitoring (DM) feature alerts distracted and asleep drivers. See more about [the vehicle integration](https://github.com/commaai/openpilot/blob/master/docs/INTEGRATION.md) and [limitations](https://github.com/commaai/openpilot/blob/master/docs/LIMITATIONS.md).
|
|
||||||
|
|
||||||
|
|
||||||
## How do I use it?
|
|
||||||
|
|
||||||
openpilot is designed to be used on the comma four.
|
|
||||||
|
|
||||||
## How does it work?
|
|
||||||
|
|
||||||
In short, openpilot uses the car's existing APIs for the built-in [ADAS](https://en.wikipedia.org/wiki/Advanced_driver-assistance_system) system and simply provides better acceleration, braking, and steering inputs than the stock system.
|
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
getting-started/what-is-openpilot.md
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
.md-logo img {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.glossary-term {
|
|
||||||
position: relative;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glossary-term__label {
|
|
||||||
border-bottom: 1px dotted currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glossary-term__tooltip {
|
|
||||||
position: absolute;
|
|
||||||
top: calc(100% + 0.4rem);
|
|
||||||
left: 50%;
|
|
||||||
width: max-content;
|
|
||||||
max-width: min(30rem, 80vw);
|
|
||||||
padding: 0.65rem 0.8rem;
|
|
||||||
border-radius: 0.6rem;
|
|
||||||
background: rgb(26 26 26 / 96%);
|
|
||||||
color: white;
|
|
||||||
box-shadow: 0 0.6rem 1.8rem rgb(0 0 0 / 22%);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.45;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
transform: translateX(-50%) translateY(-0.15rem);
|
|
||||||
transition: opacity 120ms ease, transform 120ms ease;
|
|
||||||
visibility: hidden;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.glossary-term:hover .glossary-term__tooltip,
|
|
||||||
.glossary-term:focus-visible .glossary-term__tooltip,
|
|
||||||
.glossary-term:focus-within .glossary-term__tooltip {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(-50%) translateY(0);
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ source "$DIR/launch_env.sh"
|
|||||||
function agnos_init {
|
function agnos_init {
|
||||||
# TODO: move this to agnos
|
# TODO: move this to agnos
|
||||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||||
rm -f /data/scons_cache/config.lock
|
|
||||||
|
|
||||||
# set success flag for current boot slot
|
# set success flag for current boot slot
|
||||||
sudo abctl --set_success
|
sudo abctl --set_success
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
site_name: openpilot docs
|
||||||
|
repo_url: https://github.com/commaai/openpilot/
|
||||||
|
site_url: https://docs.comma.ai
|
||||||
|
|
||||||
|
exclude_docs: README.md
|
||||||
|
|
||||||
|
strict: true
|
||||||
|
docs_dir: docs
|
||||||
|
site_dir: docs_site/
|
||||||
|
|
||||||
|
hooks:
|
||||||
|
- docs/hooks/glossary.py
|
||||||
|
extra_css:
|
||||||
|
- css/tooltip.css
|
||||||
|
theme:
|
||||||
|
name: readthedocs
|
||||||
|
navigation_depth: 3
|
||||||
|
|
||||||
|
nav:
|
||||||
|
- Getting Started:
|
||||||
|
- What is openpilot?: getting-started/what-is-openpilot.md
|
||||||
|
- How-to:
|
||||||
|
- Turn the speed blue: how-to/turn-the-speed-blue.md
|
||||||
|
- Connect to a comma 3X: how-to/connect-to-comma.md
|
||||||
|
# - Make your first pull request: how-to/make-first-pr.md
|
||||||
|
#- Replay a drive: how-to/replay-a-drive.md
|
||||||
|
- Concepts:
|
||||||
|
- Logs: concepts/logs.md
|
||||||
|
- Safety: concepts/safety.md
|
||||||
|
- Glossary: concepts/glossary.md
|
||||||
|
- Car Porting:
|
||||||
|
- What is a car port?: car-porting/what-is-a-car-port.md
|
||||||
|
- Porting a car brand: car-porting/brand-port.md
|
||||||
|
- Porting a car model: car-porting/model-port.md
|
||||||
|
- Contributing:
|
||||||
|
- Roadmap: contributing/roadmap.md
|
||||||
|
#- Architecture: contributing/architecture.md
|
||||||
|
- Contributing Guide →: https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md
|
||||||
|
- Links:
|
||||||
|
- Blog →: https://blog.comma.ai
|
||||||
|
- Bounties →: https://comma.ai/bounties
|
||||||
|
- GitHub →: https://github.com/commaai
|
||||||
|
- Discord →: https://discord.comma.ai
|
||||||
|
- X →: https://x.com/comma_ai
|
||||||
+1
-1
Submodule msgq_repo updated: b7688b9bd7...ed2777747d
+1
-1
Submodule opendbc_repo updated: 4dad7b09dd...b178bc5d4e
+1
-1
Submodule panda updated: 0a9ef7ab54...6ddc631bdd
+2
-3
@@ -82,7 +82,7 @@ dependencies = [
|
|||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
docs = [
|
docs = [
|
||||||
"Jinja2",
|
"Jinja2",
|
||||||
"zensical",
|
"mkdocs",
|
||||||
]
|
]
|
||||||
|
|
||||||
testing = [
|
testing = [
|
||||||
@@ -107,7 +107,6 @@ dev = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
"imgui @ git+https://github.com/commaai/dependencies.git@release-imgui#subdirectory=imgui",
|
|
||||||
"metadrive-simulator @ git+https://github.com/commaai/metadrive.git@minimal ; (platform_machine != 'aarch64')",
|
"metadrive-simulator @ git+https://github.com/commaai/metadrive.git@minimal ; (platform_machine != 'aarch64')",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -150,7 +149,7 @@ quiet-level = 3
|
|||||||
# if you've got a short variable name that's getting flagged, add it here
|
# if you've got a short variable name that's getting flagged, add it here
|
||||||
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite,ser"
|
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite,ser"
|
||||||
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
||||||
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, *.pem, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
||||||
|
|
||||||
# https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml
|
# https://docs.astral.sh/ruff/configuration/#using-pyprojecttoml
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
|||||||
+1
-1
Submodule rednose_repo updated: 7ffefa3d88...6ccb8d0556
@@ -104,7 +104,7 @@ def generate_metadata(model_path: Path, output_dir: Path, short_name: str):
|
|||||||
metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl")
|
metadata_file = metadata_file.rename(output_path / f"{base}_{short_name.lower()}_metadata.pkl")
|
||||||
|
|
||||||
# Build the metadata structure
|
# Build the metadata structure
|
||||||
model_type = "offPolicy" if "off_policy" in base else "onPolicy" if "on_policy" in base else base.split("_")[-1]
|
model_type = "offPolicy" if "off_policy" in base else base.split("_")[-1]
|
||||||
|
|
||||||
model_metadata = {
|
model_metadata = {
|
||||||
"type": model_type,
|
"type": model_type,
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
wrapper that materializes symlinks in docs/ before build
|
|
||||||
|
|
||||||
we can delete this once zensical supports symlinks:
|
|
||||||
https://github.com/zensical/backlog/issues/55
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
||||||
DOCS_DIR = REPO_ROOT / "docs"
|
|
||||||
SITE_DIR = REPO_ROOT / "docs_site"
|
|
||||||
sys.path.insert(0, str(REPO_ROOT))
|
|
||||||
# Local docs build helpers live under docs/ so they stay near the content
|
|
||||||
# source. The wrapper prunes them from docs_site/ after build.
|
|
||||||
sys.path.insert(0, str(DOCS_DIR))
|
|
||||||
|
|
||||||
|
|
||||||
def _materialize(docs: Path) -> dict[Path, str]:
|
|
||||||
originals: dict[Path, str] = {}
|
|
||||||
for link in docs.rglob("*"):
|
|
||||||
if not link.is_symlink():
|
|
||||||
continue
|
|
||||||
target = link.resolve()
|
|
||||||
if not target.is_file():
|
|
||||||
continue
|
|
||||||
originals[link] = os.readlink(link)
|
|
||||||
link.unlink()
|
|
||||||
shutil.copy2(target, link)
|
|
||||||
return originals
|
|
||||||
|
|
||||||
|
|
||||||
def _restore(originals: dict[Path, str]) -> None:
|
|
||||||
for link, target in originals.items():
|
|
||||||
link.unlink(missing_ok=True)
|
|
||||||
os.symlink(target, link)
|
|
||||||
|
|
||||||
|
|
||||||
def _raise_interrupt(*_):
|
|
||||||
raise KeyboardInterrupt
|
|
||||||
|
|
||||||
|
|
||||||
def _prune_site_output() -> None:
|
|
||||||
shutil.rmtree(SITE_DIR / "ext", ignore_errors=True)
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
signal.signal(signal.SIGTERM, _raise_interrupt)
|
|
||||||
originals = _materialize(DOCS_DIR)
|
|
||||||
try:
|
|
||||||
from zensical.main import cli
|
|
||||||
cli(standalone_mode=False)
|
|
||||||
if len(sys.argv) > 1 and sys.argv[1] == "build":
|
|
||||||
_prune_site_output()
|
|
||||||
finally:
|
|
||||||
_restore(originals)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
+5
-1
@@ -33,7 +33,11 @@ if __name__ == "__main__":
|
|||||||
print("|-| ----- | --------- |")
|
print("|-| ----- | --------- |")
|
||||||
|
|
||||||
for f in glob.glob(BASEDIR + MODEL_PATH + "/*.onnx"):
|
for f in glob.glob(BASEDIR + MODEL_PATH + "/*.onnx"):
|
||||||
|
# TODO: add checkpoint to DM
|
||||||
|
if "dmonitoring" in f:
|
||||||
|
continue
|
||||||
|
|
||||||
fn = os.path.basename(f)
|
fn = os.path.basename(f)
|
||||||
master = get_checkpoint(MASTER_PATH + MODEL_PATH + fn)
|
master = get_checkpoint(MASTER_PATH + MODEL_PATH + fn)
|
||||||
pr = get_checkpoint(BASEDIR + MODEL_PATH + fn)
|
pr = get_checkpoint(BASEDIR + MODEL_PATH + fn)
|
||||||
print("|", fn, "|", f"[{master}](https://reporterv2.comma.life/{master})", "|", f"[{pr}](https://reporterv2.comma.life/{pr})", "|")
|
print("|", fn, "|", f"[{master}](https://reporter.comma.life/experiment/{master})", "|", f"[{pr}](https://reporter.comma.life/experiment/{pr})", "|")
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:4ce1d357acadd798939b398cce1761ceb05564b44f2a5bc6865c7842e60e79f2
|
|
||||||
size 1474
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:b3fe73cd1a24c05346a9b4a02e4f900a314c83a422beb38b0f88f91389582cd4
|
|
||||||
size 3960
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b5aee9f6cec03f1967014cd2ea2a23982b262e7d86dadca602ecfa8875b38101
|
||||||
|
size 5875
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{% set footnote_tag = '[<sup>{}</sup>](#footnotes)' %}
|
{% set footnote_tag = '[<sup>{}</sup>](#footnotes)' %}
|
||||||
{% set star_icon = '[](##)' %}
|
{% set star_icon = '[](##)' %}
|
||||||
{% set video_icon = '<a href="{}" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>' %}
|
{% set video_icon = '<a href="{}" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>' %}
|
||||||
{# Force hardware column wider by using a blank image with max width. #}
|
{# Force hardware column wider by using a blank image with max width. #}
|
||||||
{% set width_tag = '<a href="##"><img width=2000></a>%s<br> ' %}
|
{% set width_tag = '<a href="##"><img width=2000></a>%s<br> ' %}
|
||||||
{% set hardware_col_name = 'Hardware Needed' %}
|
{% set hardware_col_name = 'Hardware Needed' %}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class Car:
|
|||||||
|
|
||||||
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
|
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
|
||||||
|
|
||||||
is_release = False # self.params.get_bool("IsReleaseBranch")
|
is_release = self.params.get_bool("IsReleaseBranch")
|
||||||
is_release_sp = self.params.get_bool("IsReleaseSpBranch")
|
is_release_sp = self.params.get_bool("IsReleaseSpBranch")
|
||||||
|
|
||||||
if CI is None:
|
if CI is None:
|
||||||
@@ -98,6 +98,7 @@ class Car:
|
|||||||
break
|
break
|
||||||
|
|
||||||
alpha_long_allowed = self.params.get_bool("AlphaLongitudinalEnabled")
|
alpha_long_allowed = self.params.get_bool("AlphaLongitudinalEnabled")
|
||||||
|
num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates)
|
||||||
|
|
||||||
cached_params = None
|
cached_params = None
|
||||||
cached_params_raw = self.params.get("CarParamsCache")
|
cached_params_raw = self.params.get("CarParamsCache")
|
||||||
@@ -108,7 +109,7 @@ class Car:
|
|||||||
fixed_fingerprint = (self.params.get("CarPlatformBundle") or {}).get("platform", None)
|
fixed_fingerprint = (self.params.get("CarPlatformBundle") or {}).get("platform", None)
|
||||||
init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params)
|
init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params)
|
||||||
|
|
||||||
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, cached_params,
|
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params,
|
||||||
fixed_fingerprint, init_params_list_sp, is_release_sp)
|
fixed_fingerprint, init_params_list_sp, is_release_sp)
|
||||||
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
|
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
|
||||||
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
|
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Controls(ControlsExt):
|
|||||||
self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP)
|
self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP)
|
||||||
|
|
||||||
self.sm = messaging.SubMaster(['liveDelay', 'liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
|
self.sm = messaging.SubMaster(['liveDelay', 'liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
|
||||||
'liveCalibration', 'livePose', 'longitudinalPlan', 'lateralManeuverPlan', 'carState', 'carOutput',
|
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
|
||||||
'driverMonitoringState', 'onroadEvents', 'driverAssistance', 'liveDelay'] + self.sm_services_ext,
|
'driverMonitoringState', 'onroadEvents', 'driverAssistance', 'liveDelay'] + self.sm_services_ext,
|
||||||
poll='selfdriveState')
|
poll='selfdriveState')
|
||||||
self.pm = messaging.PubMaster(['carControl', 'controlsState'] + self.pm_services_ext)
|
self.pm = messaging.PubMaster(['carControl', 'controlsState'] + self.pm_services_ext)
|
||||||
@@ -135,10 +135,7 @@ class Controls(ControlsExt):
|
|||||||
|
|
||||||
# Steering PID loop and lateral MPC
|
# Steering PID loop and lateral MPC
|
||||||
# Reset desired curvature to current to avoid violating the limits on engage
|
# Reset desired curvature to current to avoid violating the limits on engage
|
||||||
if self.sm.valid['lateralManeuverPlan']:
|
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
|
||||||
new_desired_curvature = self.sm['lateralManeuverPlan'].desiredCurvature if CC.latActive else self.curvature
|
|
||||||
else:
|
|
||||||
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
|
|
||||||
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)
|
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)
|
||||||
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
|
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
|
||||||
|
|
||||||
|
|||||||
@@ -39,17 +39,19 @@ def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, b
|
|||||||
return float(new_curvature), limited_accel or limited_max_curv
|
return float(new_curvature), limited_accel or limited_max_curv
|
||||||
|
|
||||||
|
|
||||||
def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.3):
|
def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.05):
|
||||||
if len(speeds) == len(t_idxs):
|
if len(speeds) == len(t_idxs):
|
||||||
v_now = speeds[0]
|
v_now = speeds[0]
|
||||||
a_now = accels[0]
|
a_now = accels[0]
|
||||||
v_target = np.interp(action_t, t_idxs, speeds)
|
v_target = np.interp(action_t, t_idxs, speeds)
|
||||||
a_target = 2 * (v_target - v_now) / (action_t) - a_now
|
a_target = 2 * (v_target - v_now) / (action_t) - a_now
|
||||||
|
v_target_1sec = np.interp(action_t + 1.0, t_idxs, speeds)
|
||||||
else:
|
else:
|
||||||
v_now = 0.0
|
|
||||||
v_target = 0.0
|
v_target = 0.0
|
||||||
|
v_target_1sec = 0.0
|
||||||
a_target = 0.0
|
a_target = 0.0
|
||||||
should_stop = (v_now < vEgoStopping and a_target < 0.1)
|
should_stop = (v_target < vEgoStopping and
|
||||||
|
v_target_1sec < vEgoStopping)
|
||||||
return a_target, should_stop
|
return a_target, should_stop
|
||||||
|
|
||||||
def curv_from_psis(psi_target, psi_rate, vego, action_t):
|
def curv_from_psis(psi_target, psi_rate, vego, action_t):
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class LatControlAngle(LatControl):
|
|||||||
def __init__(self, CP, CP_SP, CI, dt):
|
def __init__(self, CP, CP_SP, CI, dt):
|
||||||
super().__init__(CP, CP_SP, CI, dt)
|
super().__init__(CP, CP_SP, CI, dt)
|
||||||
self.sat_check_min_speed = 5.
|
self.sat_check_min_speed = 5.
|
||||||
self.use_steer_limited_by_safety = CP.brand in ("tesla", "hyundai")
|
self.use_steer_limited_by_safety = CP.brand == "tesla"
|
||||||
|
|
||||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
|
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
|
||||||
angle_log = log.ControlsState.LateralAngleState.new_message()
|
angle_log = log.ControlsState.LateralAngleState.new_message()
|
||||||
|
|||||||
@@ -313,14 +313,11 @@ class LongitudinalMpc:
|
|||||||
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
|
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
|
||||||
return lead_xv
|
return lead_xv
|
||||||
|
|
||||||
def update(self, radarstate, v_cruise, personality=log.LongitudinalPersonality.standard, a_cruise_min=None):
|
def update(self, radarstate, v_cruise, personality=log.LongitudinalPersonality.standard):
|
||||||
t_follow = get_T_FOLLOW(personality)
|
t_follow = get_T_FOLLOW(personality)
|
||||||
v_ego = self.x0[1]
|
v_ego = self.x0[1]
|
||||||
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
|
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
|
||||||
|
|
||||||
if a_cruise_min is None:
|
|
||||||
a_cruise_min = CRUISE_MIN_ACCEL
|
|
||||||
|
|
||||||
lead_xv_0 = self.process_lead(radarstate.leadOne)
|
lead_xv_0 = self.process_lead(radarstate.leadOne)
|
||||||
lead_xv_1 = self.process_lead(radarstate.leadTwo)
|
lead_xv_1 = self.process_lead(radarstate.leadTwo)
|
||||||
|
|
||||||
@@ -332,7 +329,7 @@ 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.
|
||||||
v_lower = v_ego + (T_IDXS * a_cruise_min * 1.05)
|
v_lower = v_ego + (T_IDXS * CRUISE_MIN_ACCEL * 1.05)
|
||||||
# TODO does this make sense when max_a is negative?
|
# TODO does this make sense when max_a is negative?
|
||||||
v_upper = v_ego + (T_IDXS * CRUISE_MAX_ACCEL * 1.05)
|
v_upper = v_ego + (T_IDXS * CRUISE_MAX_ACCEL * 1.05)
|
||||||
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, v_upper)
|
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, v_upper)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
|
|||||||
# No change cost when user is controlling the speed, or when standstill
|
# No change cost when user is controlling the speed, or when standstill
|
||||||
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
|
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
|
||||||
|
|
||||||
accel_clip = self.get_accel_clip(v_ego) or [ACCEL_MIN, get_max_accel(v_ego)]
|
accel_clip = [ACCEL_MIN, get_max_accel(v_ego)]
|
||||||
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
|
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
|
||||||
accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP)
|
accel_clip = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_clip, self.CP)
|
||||||
|
|
||||||
@@ -138,8 +138,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
|
|||||||
|
|
||||||
self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality)
|
self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality)
|
||||||
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
|
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
|
||||||
self.mpc.update(sm['radarState'], v_cruise, personality=sm['selfdriveState'].personality,
|
self.mpc.update(sm['radarState'], v_cruise, personality=sm['selfdriveState'].personality)
|
||||||
a_cruise_min=self.get_cruise_min_accel(v_ego))
|
|
||||||
|
|
||||||
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
|
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
|
||||||
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
|
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
|
||||||
|
|||||||
@@ -50,10 +50,8 @@ def simulate_straight_road_msgs(est):
|
|||||||
lat_accels = TORQUE_TUNE.latAccelFactor * steer_torques
|
lat_accels = TORQUE_TUNE.latAccelFactor * steer_torques
|
||||||
for t, steer_torque, lat_accel in zip(ts, steer_torques, lat_accels, strict=True):
|
for t, steer_torque, lat_accel in zip(ts, steer_torques, lat_accels, strict=True):
|
||||||
carOutput.actuatorsOutput.torque = float(-steer_torque)
|
carOutput.actuatorsOutput.torque = float(-steer_torque)
|
||||||
livePose.orientationNED = {'x': float(np.deg2rad(ROLL_BIAS_DEG)), 'valid': True}
|
livePose.orientationNED.x = float(np.deg2rad(ROLL_BIAS_DEG))
|
||||||
livePose.angularVelocityDevice = {'z': float(lat_accel / V_EGO), 'valid': True}
|
livePose.angularVelocityDevice.z = float(lat_accel / V_EGO)
|
||||||
livePose.inputsOK, livePose.sensorsOK, livePose.posenetOK = True, True, True
|
|
||||||
livePose.timestamp = int(t * 1e9)
|
|
||||||
for which, msg in (('carControl', carControl), ('carOutput', carOutput), ('carState', carState), ('livePose', livePose)):
|
for which, msg in (('carControl', carControl), ('carOutput', carOutput), ('carState', carState), ('livePose', livePose)):
|
||||||
est.handle_log(t, which, msg)
|
est.handle_log(t, which, msg)
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ if __name__ == "__main__":
|
|||||||
extra[(Ecu.unknown, 0x750, i)] = []
|
extra[(Ecu.unknown, 0x750, i)] = []
|
||||||
extra = {"any": {"debug": extra}}
|
extra = {"any": {"debug": extra}}
|
||||||
|
|
||||||
|
num_pandas = len(messaging.recv_one_retry(pandaStates_sock).pandaStates)
|
||||||
|
|
||||||
t = time.monotonic()
|
t = time.monotonic()
|
||||||
print("Getting vin...")
|
print("Getting vin...")
|
||||||
set_obd_multiplexing(True)
|
set_obd_multiplexing(True)
|
||||||
@@ -54,7 +56,7 @@ if __name__ == "__main__":
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
t = time.monotonic()
|
t = time.monotonic()
|
||||||
fw_vers = get_fw_versions(*can_callbacks, set_obd_multiplexing, query_brand=args.brand, extra=extra, progress=True)
|
fw_vers = get_fw_versions(*can_callbacks, set_obd_multiplexing, query_brand=args.brand, extra=extra, num_pandas=num_pandas, progress=True)
|
||||||
_, candidates = match_fw_to_car(fw_vers, vin)
|
_, candidates = match_fw_to_car(fw_vers, vin)
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ def cycle_alerts(duration=200, is_metric=False):
|
|||||||
(EventName.accFaulted, ET.IMMEDIATE_DISABLE),
|
(EventName.accFaulted, ET.IMMEDIATE_DISABLE),
|
||||||
|
|
||||||
# DM sequence
|
# DM sequence
|
||||||
(EventName.driverDistracted1, ET.WARNING),
|
(EventName.preDriverDistracted, ET.WARNING),
|
||||||
(EventName.driverDistracted2, ET.WARNING),
|
(EventName.promptDriverDistracted, ET.WARNING),
|
||||||
(EventName.driverDistracted3, ET.WARNING),
|
(EventName.driverDistracted, ET.WARNING),
|
||||||
]
|
]
|
||||||
|
|
||||||
# debug alerts
|
# debug alerts
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ if __name__ == "__main__":
|
|||||||
parser = argparse.ArgumentParser(description='View back and forth ISO-TP communication between various ECUs given an address')
|
parser = argparse.ArgumentParser(description='View back and forth ISO-TP communication between various ECUs given an address')
|
||||||
parser.add_argument('route', nargs='?', help='Route name, live if not specified')
|
parser.add_argument('route', nargs='?', help='Route name, live if not specified')
|
||||||
parser.add_argument('--addrs', nargs='*', default=[], help='List of tx address to view (0x7e0 for engine)')
|
parser.add_argument('--addrs', nargs='*', default=[], help='List of tx address to view (0x7e0 for engine)')
|
||||||
parser.add_argument('--rxoffset', default='0x8')
|
parser.add_argument('--rxoffset', default='')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
addrs = [int(addr, base=16) if addr.startswith('0x') else int(addr) for addr in args.addrs]
|
addrs = [int(addr, base=16) if addr.startswith('0x') else int(addr) for addr in args.addrs]
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ FOOTNOTE_TAG = "<sup>{}</sup>"
|
|||||||
STAR_ICON = '<a href="##"><img valign="top" ' + \
|
STAR_ICON = '<a href="##"><img valign="top" ' + \
|
||||||
'src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-star-{}.svg" width="22" /></a>'
|
'src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-star-{}.svg" width="22" /></a>'
|
||||||
VIDEO_ICON = '<a href="{}" target="_blank">' + \
|
VIDEO_ICON = '<a href="{}" target="_blank">' + \
|
||||||
'<img height="18px" src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-youtube.svg" /></a>'
|
'<img height="18px" src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-youtube.svg"></img></a>'
|
||||||
COLUMNS = "|" + "|".join([column.value for column in Column]) + "|"
|
COLUMNS = "|" + "|".join([column.value for column in Column]) + "|"
|
||||||
COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3)))
|
COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3)))
|
||||||
ARROW_SYMBOL = "➡️"
|
ARROW_SYMBOL = "➡️"
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ INPUT_INVALID_LIMIT = 2.0 # 1 (camodo) / 9 (sensor) bad input[s] ignored
|
|||||||
INPUT_INVALID_RECOVERY = 10.0 # ~10 secs to resume after exceeding allowed bad inputs by one
|
INPUT_INVALID_RECOVERY = 10.0 # ~10 secs to resume after exceeding allowed bad inputs by one
|
||||||
POSENET_STD_INITIAL_VALUE = 10.0
|
POSENET_STD_INITIAL_VALUE = 10.0
|
||||||
POSENET_STD_HIST_HALF = 20
|
POSENET_STD_HIST_HALF = 20
|
||||||
CAM_ODO_POSE_DELAY = 0.1 # dependent on the vision model context frames and temporal frequency (current model is 5 fps with 2 context frames)
|
|
||||||
CAM_ODO_ROT_STD_MULT = 10
|
|
||||||
CAM_ODO_TRANS_STD_MULT = 4
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_invalid_input_decay(invalid_limit, recovery_time, frequency):
|
def calculate_invalid_input_decay(invalid_limit, recovery_time, frequency):
|
||||||
@@ -158,8 +155,6 @@ class LocationEstimator:
|
|||||||
self.device_from_calib = rot_from_euler(calib)
|
self.device_from_calib = rot_from_euler(calib)
|
||||||
|
|
||||||
elif which == "cameraOdometry":
|
elif which == "cameraOdometry":
|
||||||
# camera odometry is delayed depending on the model context frames and temporal frequency
|
|
||||||
t = msg.timestampEof * 1e-9 - CAM_ODO_POSE_DELAY
|
|
||||||
if not self._validate_timestamp(t):
|
if not self._validate_timestamp(t):
|
||||||
return HandleLogResult.TIMING_INVALID
|
return HandleLogResult.TIMING_INVALID
|
||||||
|
|
||||||
@@ -182,8 +177,8 @@ class LocationEstimator:
|
|||||||
self.posenet_stds[-1] = trans_calib_std[0]
|
self.posenet_stds[-1] = trans_calib_std[0]
|
||||||
|
|
||||||
# Multiply by N to avoid to high certainty in kalman filter because of temporally correlated noise
|
# Multiply by N to avoid to high certainty in kalman filter because of temporally correlated noise
|
||||||
rot_calib_std *= CAM_ODO_ROT_STD_MULT
|
rot_calib_std *= 10
|
||||||
trans_calib_std *= CAM_ODO_TRANS_STD_MULT
|
trans_calib_std *= 2
|
||||||
|
|
||||||
rot_device_std = rotate_std(self.device_from_calib, rot_calib_std)
|
rot_device_std = rotate_std(self.device_from_calib, rot_calib_std)
|
||||||
trans_device_std = rotate_std(self.device_from_calib, trans_calib_std)
|
trans_device_std = rotate_std(self.device_from_calib, trans_calib_std)
|
||||||
@@ -239,7 +234,6 @@ class LocationEstimator:
|
|||||||
livePose.inputsOK = inputs_valid
|
livePose.inputsOK = inputs_valid
|
||||||
livePose.posenetOK = not std_spike or self.car_speed <= 5.0
|
livePose.posenetOK = not std_spike or self.car_speed <= 5.0
|
||||||
livePose.sensorsOK = sensors_valid
|
livePose.sensorsOK = sensors_valid
|
||||||
livePose.timestamp = int(np.nan_to_num(self.kf.t) * 1e9)
|
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ class PoseKalman(KalmanFilter):
|
|||||||
# process noise
|
# process noise
|
||||||
Q = np.diag([0.001**2, 0.001**2, 0.001**2,
|
Q = np.diag([0.001**2, 0.001**2, 0.001**2,
|
||||||
0.01**2, 0.01**2, 0.01**2,
|
0.01**2, 0.01**2, 0.01**2,
|
||||||
0.085**2, 0.085**2, 0.085**2,
|
0.1**2, 0.1**2, 0.1**2,
|
||||||
(0.005 / 100)**2, (0.005 / 100)**2, (0.005 / 100)**2,
|
(0.005 / 100)**2, (0.005 / 100)**2, (0.005 / 100)**2,
|
||||||
3**2, 3**2, 3**2,
|
3**2, 3**2, 3**2,
|
||||||
0.005**2, 0.005**2, 0.005**2])
|
0.005**2, 0.005**2, 0.005**2])
|
||||||
|
|
||||||
obs_noise = {ObservationKind.PHONE_GYRO: np.diag([0.025**2, 0.025**2, 0.025**2]),
|
obs_noise = {ObservationKind.PHONE_GYRO: np.diag([0.025**2, 0.025**2, 0.025**2]),
|
||||||
ObservationKind.PHONE_ACCEL: np.diag([0.75**2, 0.75**2, 0.75**2]),
|
ObservationKind.PHONE_ACCEL: np.diag([.5**2, .5**2, .5**2]),
|
||||||
ObservationKind.CAMERA_ODO_TRANSLATION: np.diag([0.5**2, 0.5**2, 0.5**2]),
|
ObservationKind.CAMERA_ODO_TRANSLATION: np.diag([0.5**2, 0.5**2, 0.5**2]),
|
||||||
ObservationKind.CAMERA_ODO_ROTATION: np.diag([0.05**2, 0.05**2, 0.05**2])}
|
ObservationKind.CAMERA_ODO_ROTATION: np.diag([0.05**2, 0.05**2, 0.05**2])}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ class VehicleParamsLearner:
|
|||||||
|
|
||||||
def handle_log(self, t: float, which: str, msg: capnp._DynamicStructReader):
|
def handle_log(self, t: float, which: str, msg: capnp._DynamicStructReader):
|
||||||
if which == 'livePose':
|
if which == 'livePose':
|
||||||
t = msg.timestamp * 1e-9
|
|
||||||
device_pose = Pose.from_live_pose(msg)
|
device_pose = Pose.from_live_pose(msg)
|
||||||
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
|
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from collections import defaultdict
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from openpilot.tools.lib.logreader import LogReader
|
from openpilot.tools.lib.logreader import LogReader
|
||||||
from openpilot.selfdrive.locationd.lagd import masked_symmetric_moving_average
|
|
||||||
from openpilot.selfdrive.test.process_replay.migration import migrate_all
|
from openpilot.selfdrive.test.process_replay.migration import migrate_all
|
||||||
from openpilot.selfdrive.test.process_replay.process_replay import replay_process_with_name
|
from openpilot.selfdrive.test.process_replay.process_replay import replay_process_with_name
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ SELECT_COMPARE_FIELDS = {
|
|||||||
'inputs_flag': ['inputsOK'],
|
'inputs_flag': ['inputsOK'],
|
||||||
'sensors_flag': ['sensorsOK'],
|
'sensors_flag': ['sensorsOK'],
|
||||||
}
|
}
|
||||||
SMOOTH_FIELDS = ['yaw_rate', 'roll']
|
|
||||||
JUNK_IDX = 100
|
JUNK_IDX = 100
|
||||||
CONSISTENT_SPIKES_COUNT = 10
|
CONSISTENT_SPIKES_COUNT = 10
|
||||||
|
|
||||||
@@ -34,8 +32,6 @@ class Scenario(Enum):
|
|||||||
|
|
||||||
|
|
||||||
def get_select_fields_data(logs):
|
def get_select_fields_data(logs):
|
||||||
def sig_smooth(signal):
|
|
||||||
return masked_symmetric_moving_average(signal, np.ones_like(signal), 5, 1.0)
|
|
||||||
def get_nested_keys(msg, keys):
|
def get_nested_keys(msg, keys):
|
||||||
val = None
|
val = None
|
||||||
for key in keys:
|
for key in keys:
|
||||||
@@ -48,8 +44,6 @@ def get_select_fields_data(logs):
|
|||||||
data[key].append(get_nested_keys(msg, fields))
|
data[key].append(get_nested_keys(msg, fields))
|
||||||
for key in data:
|
for key in data:
|
||||||
data[key] = np.array(data[key][JUNK_IDX:], dtype=float)
|
data[key] = np.array(data[key][JUNK_IDX:], dtype=float)
|
||||||
if key in SMOOTH_FIELDS:
|
|
||||||
data[key] = sig_smooth(data[key])
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +110,7 @@ class TestLocationdScenarios:
|
|||||||
"""
|
"""
|
||||||
orig_data, replayed_data = run_scenarios(Scenario.BASE, self.logs)
|
orig_data, replayed_data = run_scenarios(Scenario.BASE, self.logs)
|
||||||
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
||||||
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.55))
|
||||||
|
|
||||||
def test_gyro_off(self):
|
def test_gyro_off(self):
|
||||||
"""
|
"""
|
||||||
@@ -141,7 +135,7 @@ class TestLocationdScenarios:
|
|||||||
"""
|
"""
|
||||||
orig_data, replayed_data = run_scenarios(Scenario.GYRO_SPIKE_MIDWAY, self.logs)
|
orig_data, replayed_data = run_scenarios(Scenario.GYRO_SPIKE_MIDWAY, self.logs)
|
||||||
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
||||||
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.55))
|
||||||
assert np.all(replayed_data['inputs_flag'] == orig_data['inputs_flag'])
|
assert np.all(replayed_data['inputs_flag'] == orig_data['inputs_flag'])
|
||||||
assert np.all(replayed_data['sensors_flag'] == orig_data['sensors_flag'])
|
assert np.all(replayed_data['sensors_flag'] == orig_data['sensors_flag'])
|
||||||
|
|
||||||
@@ -175,7 +169,7 @@ class TestLocationdScenarios:
|
|||||||
"""
|
"""
|
||||||
orig_data, replayed_data = run_scenarios(Scenario.ACCEL_SPIKE_MIDWAY, self.logs)
|
orig_data, replayed_data = run_scenarios(Scenario.ACCEL_SPIKE_MIDWAY, self.logs)
|
||||||
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['yaw_rate'], replayed_data['yaw_rate'], atol=np.radians(0.35))
|
||||||
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.35))
|
assert np.allclose(orig_data['roll'], replayed_data['roll'], atol=np.radians(0.55))
|
||||||
|
|
||||||
def test_single_timing_spike(self):
|
def test_single_timing_spike(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -188,9 +188,7 @@ class TorqueEstimator(ParameterEstimator, TorqueEstimatorExt):
|
|||||||
self.lag = get_lat_delay(self.params, msg.lateralDelay)
|
self.lag = get_lat_delay(self.params, msg.lateralDelay)
|
||||||
# calculate lateral accel from past steering torque
|
# calculate lateral accel from past steering torque
|
||||||
elif which == "livePose":
|
elif which == "livePose":
|
||||||
is_valid = msg.angularVelocityDevice.valid and msg.orientationNED.valid and msg.inputsOK and msg.sensorsOK and msg.posenetOK
|
if len(self.raw_points['steer_torque']) == self.hist_len:
|
||||||
if len(self.raw_points['steer_torque']) == self.hist_len and is_valid:
|
|
||||||
t = msg.timestamp * 1e-9
|
|
||||||
device_pose = Pose.from_live_pose(msg)
|
device_pose = Pose.from_live_pose(msg)
|
||||||
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
|
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
|
||||||
angular_velocity_calibrated = calibrated_pose.angular_velocity
|
angular_velocity_calibrated = calibrated_pose.angular_velocity
|
||||||
|
|||||||
+24
-35
@@ -1,9 +1,6 @@
|
|||||||
import glob
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
from SCons.Script import Value
|
import glob
|
||||||
from openpilot.common.file_chunker import chunk_file, get_chunk_paths
|
from openpilot.common.file_chunker import chunk_file, get_chunk_paths
|
||||||
from tinygrad import Device
|
|
||||||
|
|
||||||
Import('env', 'arch')
|
Import('env', 'arch')
|
||||||
chunker_file = File("#common/file_chunker.py")
|
chunker_file = File("#common/file_chunker.py")
|
||||||
@@ -16,52 +13,31 @@ tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "
|
|||||||
def estimate_pickle_max_size(onnx_size):
|
def estimate_pickle_max_size(onnx_size):
|
||||||
return 1.2 * onnx_size + 10 * 1024 * 1024 # 20% + 10MB is plenty
|
return 1.2 * onnx_size + 10 * 1024 * 1024 # 20% + 10MB is plenty
|
||||||
|
|
||||||
|
# compile warp
|
||||||
# THREADS=0 is need to prevent bug: https://github.com/tinygrad/tinygrad/issues/14689
|
# THREADS=0 is need to prevent bug: https://github.com/tinygrad/tinygrad/issues/14689
|
||||||
# get fastest TG config
|
tg_flags = {
|
||||||
available = set(Device.get_available_devices())
|
'larch64': 'DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0',
|
||||||
# FIXME-SP: reset when we bump tg
|
'Darwin': f'DEV=CPU THREADS=0 HOME={os.path.expanduser("~")} IMAGE=0', # tinygrad calls brew which needs a $HOME in the env
|
||||||
if False: # 'CUDA' in available:
|
}.get(arch, 'DEV=CPU CPU_LLVM=1 THREADS=0 IMAGE=0')
|
||||||
tg_backend = 'CUDA'
|
|
||||||
tg_flags = f'DEV={tg_backend}'
|
|
||||||
elif 'QCOM' in available:
|
|
||||||
tg_backend = 'QCOM'
|
|
||||||
tg_flags = f'DEV={tg_backend} FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0'
|
|
||||||
else:
|
|
||||||
tg_backend = 'CPU' if arch == 'Darwin' else 'CPU CPU_LLVM=1' # FIXME-SP: reset when we bump tg
|
|
||||||
tg_flags = f'DEV={tg_backend} THREADS=0'
|
|
||||||
|
|
||||||
def write_tg_compiled_flags(target, source, env):
|
|
||||||
with open(str(target[0]), "w") as f:
|
|
||||||
json.dump({"DEV": tg_backend}, f)
|
|
||||||
f.write("\n")
|
|
||||||
|
|
||||||
compiled_flags_node = lenv.Command(
|
|
||||||
File("models/tg_compiled_flags.json").abspath,
|
|
||||||
tinygrad_files + [Value(tg_backend)],
|
|
||||||
write_tg_compiled_flags,
|
|
||||||
)
|
|
||||||
|
|
||||||
# tinygrad calls brew which needs a $HOME in the env
|
|
||||||
mac_brew_string = f'HOME={os.path.expanduser("~")}' if arch == 'Darwin' else ''
|
|
||||||
|
|
||||||
# Get model metadata
|
# Get model metadata
|
||||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||||
fn = File(f"models/{model_name}").abspath
|
fn = File(f"models/{model_name}").abspath
|
||||||
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
||||||
cmd = f'{tg_flags} {mac_brew_string} python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
cmd = f'{tg_flags} python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||||
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files + [compiled_flags_node], cmd)
|
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
||||||
|
|
||||||
image_flag = {
|
image_flag = {
|
||||||
'larch64': 'IMAGE=2',
|
'larch64': 'IMAGE=2',
|
||||||
}.get(arch, 'IMAGE=0')
|
}.get(arch, 'IMAGE=0')
|
||||||
script_files = [File(Dir("#selfdrive/modeld").File("compile_warp.py").abspath)]
|
script_files = [File(Dir("#selfdrive/modeld").File("compile_warp.py").abspath)]
|
||||||
compile_warp_cmd = f'{tg_flags} {mac_brew_string} python3 {Dir("#selfdrive/modeld").abspath}/compile_warp.py '
|
compile_warp_cmd = f'{tg_flags} python3 {Dir("#selfdrive/modeld").abspath}/compile_warp.py '
|
||||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
||||||
warp_targets = []
|
warp_targets = []
|
||||||
for cam in [_ar_ox_fisheye, _os_fisheye]:
|
for cam in [_ar_ox_fisheye, _os_fisheye]:
|
||||||
w, h = cam.width, cam.height
|
w, h = cam.width, cam.height
|
||||||
warp_targets += [File(f"models/warp_{w}x{h}_tinygrad.pkl").abspath, File(f"models/dm_warp_{w}x{h}_tinygrad.pkl").abspath]
|
warp_targets += [File(f"models/warp_{w}x{h}_tinygrad.pkl").abspath, File(f"models/dm_warp_{w}x{h}_tinygrad.pkl").abspath]
|
||||||
lenv.Command(warp_targets, tinygrad_files + script_files + [compiled_flags_node], compile_warp_cmd)
|
lenv.Command(warp_targets, tinygrad_files + script_files, compile_warp_cmd)
|
||||||
|
|
||||||
def tg_compile(flags, model_name):
|
def tg_compile(flags, model_name):
|
||||||
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
||||||
@@ -71,7 +47,7 @@ def tg_compile(flags, model_name):
|
|||||||
chunk_targets = get_chunk_paths(pkl, estimate_pickle_max_size(os.path.getsize(onnx_path)))
|
chunk_targets = get_chunk_paths(pkl, estimate_pickle_max_size(os.path.getsize(onnx_path)))
|
||||||
compile_node = lenv.Command(
|
compile_node = lenv.Command(
|
||||||
pkl,
|
pkl,
|
||||||
[onnx_path] + tinygrad_files + [chunker_file, compiled_flags_node],
|
[onnx_path] + tinygrad_files + [chunker_file],
|
||||||
f'{pythonpath_string} {flags} {image_flag} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {pkl}',
|
f'{pythonpath_string} {flags} {image_flag} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {pkl}',
|
||||||
)
|
)
|
||||||
def do_chunk(target, source, env):
|
def do_chunk(target, source, env):
|
||||||
@@ -86,3 +62,16 @@ def tg_compile(flags, model_name):
|
|||||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||||
tg_compile(tg_flags, model_name)
|
tg_compile(tg_flags, model_name)
|
||||||
|
|
||||||
|
# Compile BIG model if USB GPU is available
|
||||||
|
if "USBGPU" in os.environ:
|
||||||
|
import subprocess
|
||||||
|
# because tg doesn't support multi-process
|
||||||
|
devs = subprocess.check_output('python3 -c "from tinygrad import Device; print(list(Device.get_available_devices()))"', shell=True, cwd=env.Dir('#').abspath)
|
||||||
|
if b"AMD" in devs:
|
||||||
|
print("USB GPU detected... building")
|
||||||
|
flags = "DEV=AMD AMD_IFACE=USB AMD_LLVM=1 NOLOCALS=0 IMAGE=0"
|
||||||
|
bp = tg_compile(flags, "big_driving_policy")
|
||||||
|
bv = tg_compile(flags, "big_driving_vision")
|
||||||
|
lenv.SideEffect('lock', [bp, bv]) # tg doesn't support multi-process so build serially
|
||||||
|
else:
|
||||||
|
print("USB GPU not detected... skipping")
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ def make_update_img_input(frame_prepare, model_w, model_h):
|
|||||||
def update_img_input_tinygrad(tensor, frame, M_inv):
|
def update_img_input_tinygrad(tensor, frame, M_inv):
|
||||||
M_inv = M_inv.to(Device.DEFAULT)
|
M_inv = M_inv.to(Device.DEFAULT)
|
||||||
new_img = frame_prepare(frame, M_inv)
|
new_img = frame_prepare(frame, M_inv)
|
||||||
tensor.assign(tensor[6:].cat(new_img, dim=0).contiguous())
|
full_buffer = tensor[6:].cat(new_img, dim=0).contiguous()
|
||||||
return Tensor.cat(tensor[:6], tensor[-6:], dim=0).contiguous().reshape(1, 12, model_h//2, model_w//2)
|
return full_buffer, Tensor.cat(full_buffer[:6], full_buffer[-6:], dim=0).contiguous().reshape(1, 12, model_h//2, model_w//2)
|
||||||
return update_img_input_tinygrad
|
return update_img_input_tinygrad
|
||||||
|
|
||||||
|
|
||||||
@@ -107,9 +107,9 @@ def make_update_both_imgs(frame_prepare, model_w, model_h):
|
|||||||
|
|
||||||
def update_both_imgs_tinygrad(calib_img_buffer, new_img, M_inv,
|
def update_both_imgs_tinygrad(calib_img_buffer, new_img, M_inv,
|
||||||
calib_big_img_buffer, new_big_img, M_inv_big):
|
calib_big_img_buffer, new_big_img, M_inv_big):
|
||||||
calib_img_pair = update_img(calib_img_buffer, new_img, M_inv)
|
calib_img_buffer, calib_img_pair = update_img(calib_img_buffer, new_img, M_inv)
|
||||||
calib_big_img_pair = update_img(calib_big_img_buffer, new_big_img, M_inv_big)
|
calib_big_img_buffer, calib_big_img_pair = update_img(calib_big_img_buffer, new_big_img, M_inv_big)
|
||||||
return calib_img_pair, calib_big_img_pair
|
return calib_img_buffer, calib_img_pair, calib_big_img_buffer, calib_big_img_pair
|
||||||
return update_both_imgs_tinygrad
|
return update_both_imgs_tinygrad
|
||||||
|
|
||||||
|
|
||||||
@@ -136,20 +136,29 @@ def compile_modeld_warp(cam_w, cam_h):
|
|||||||
|
|
||||||
full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
||||||
big_full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
big_full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
||||||
new_frame_np = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
|
full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
||||||
new_big_frame_np = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
|
big_full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
|
new_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
||||||
img_inputs = [full_buffer,
|
img_inputs = [full_buffer,
|
||||||
Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||||
|
new_big_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
||||||
big_img_inputs = [big_full_buffer,
|
big_img_inputs = [big_full_buffer,
|
||||||
Tensor.from_blob(new_big_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
Tensor.from_blob(new_big_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||||
inputs = img_inputs + big_img_inputs
|
inputs = img_inputs + big_img_inputs
|
||||||
Device.default.synchronize()
|
Device.default.synchronize()
|
||||||
|
|
||||||
|
inputs_np = [x.numpy() for x in inputs]
|
||||||
|
inputs_np[0] = full_buffer_np
|
||||||
|
inputs_np[3] = big_full_buffer_np
|
||||||
|
|
||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
_ = update_img_jit(*inputs)
|
out = update_img_jit(*inputs)
|
||||||
|
full_buffer = out[0].contiguous().realize().clone()
|
||||||
|
big_full_buffer = out[2].contiguous().realize().clone()
|
||||||
mt = time.perf_counter()
|
mt = time.perf_counter()
|
||||||
Device.default.synchronize()
|
Device.default.synchronize()
|
||||||
et = time.perf_counter()
|
et = time.perf_counter()
|
||||||
@@ -173,9 +182,8 @@ def compile_dm_warp(cam_w, cam_h):
|
|||||||
warp_dm = make_warp_dm(cam_w, cam_h, dm_w, dm_h)
|
warp_dm = make_warp_dm(cam_w, cam_h, dm_w, dm_h)
|
||||||
warp_dm_jit = TinyJit(warp_dm, prune=True)
|
warp_dm_jit = TinyJit(warp_dm, prune=True)
|
||||||
|
|
||||||
new_frame_np = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
inputs = [Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
inputs = [Tensor.from_blob((32 * Tensor.randn(yuv_size,) + 128).cast(dtype='uint8').realize().numpy().ctypes.data, (yuv_size,), dtype='uint8'),
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||||
Device.default.synchronize()
|
Device.default.synchronize()
|
||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from openpilot.selfdrive.modeld.tinygrad_helpers import MODELS_DIR, set_tinygrad_backend_from_compiled_flags
|
|
||||||
set_tinygrad_backend_from_compiled_flags()
|
|
||||||
|
|
||||||
# FIXME-SP: remove once we bump tg
|
|
||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||||
|
|
||||||
from tinygrad.tensor import Tensor
|
from tinygrad.tensor import Tensor
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from cereal import messaging
|
from cereal import messaging
|
||||||
from cereal.messaging import PubMaster, SubMaster
|
from cereal.messaging import PubMaster, SubMaster
|
||||||
@@ -25,8 +21,9 @@ from openpilot.selfdrive.modeld.parse_model_outputs import sigmoid, safe_exp
|
|||||||
|
|
||||||
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
||||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||||
MODEL_PKL_PATH = MODELS_DIR / 'dmonitoring_model_tinygrad.pkl'
|
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
||||||
METADATA_PATH = MODELS_DIR / 'dmonitoring_model_metadata.pkl'
|
METADATA_PATH = Path(__file__).parent / 'models/dmonitoring_model_metadata.pkl'
|
||||||
|
MODELS_DIR = Path(__file__).parent / 'models'
|
||||||
|
|
||||||
class ModelState:
|
class ModelState:
|
||||||
inputs: dict[str, np.ndarray]
|
inputs: dict[str, np.ndarray]
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from openpilot.selfdrive.modeld.tinygrad_helpers import MODELS_DIR, set_tinygrad_backend_from_compiled_flags
|
|
||||||
set_tinygrad_backend_from_compiled_flags()
|
|
||||||
|
|
||||||
# FIXME-SP: remove once we bump tg
|
|
||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||||
|
|
||||||
USBGPU = "USBGPU" in os.environ
|
USBGPU = "USBGPU" in os.environ
|
||||||
if USBGPU:
|
if USBGPU:
|
||||||
os.environ['DEV'] = 'AMD'
|
os.environ['DEV'] = 'AMD'
|
||||||
@@ -17,6 +12,7 @@ import pickle
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
|
from pathlib import Path
|
||||||
from cereal.messaging import PubMaster, SubMaster
|
from cereal.messaging import PubMaster, SubMaster
|
||||||
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
|
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
|
||||||
from opendbc.car.car_helpers import get_demo_car_params
|
from opendbc.car.car_helpers import get_demo_car_params
|
||||||
@@ -41,10 +37,11 @@ from openpilot.sunnypilot.modeld_v2.modeld_base import ModelStateBase
|
|||||||
PROCESS_NAME = "selfdrive.modeld.modeld"
|
PROCESS_NAME = "selfdrive.modeld.modeld"
|
||||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||||
|
|
||||||
VISION_PKL_PATH = MODELS_DIR / 'driving_vision_tinygrad.pkl'
|
VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
|
||||||
VISION_METADATA_PATH = MODELS_DIR / 'driving_vision_metadata.pkl'
|
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
||||||
POLICY_PKL_PATH = MODELS_DIR / 'driving_policy_tinygrad.pkl'
|
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
||||||
POLICY_METADATA_PATH = MODELS_DIR / 'driving_policy_metadata.pkl'
|
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
||||||
|
MODELS_DIR = Path(__file__).parent / 'models'
|
||||||
|
|
||||||
LAT_SMOOTH_SECONDS = 0.0
|
LAT_SMOOTH_SECONDS = 0.0
|
||||||
LONG_SMOOTH_SECONDS = 0.3
|
LONG_SMOOTH_SECONDS = 0.3
|
||||||
@@ -220,7 +217,8 @@ class ModelState(ModelStateBase):
|
|||||||
|
|
||||||
out = self.update_imgs(self.img_queues['img'], self.full_frames['img'], self.transforms['img'],
|
out = self.update_imgs(self.img_queues['img'], self.full_frames['img'], self.transforms['img'],
|
||||||
self.img_queues['big_img'], self.full_frames['big_img'], self.transforms['big_img'])
|
self.img_queues['big_img'], self.full_frames['big_img'], self.transforms['big_img'])
|
||||||
vision_inputs = {'img': out[0], 'big_img': out[1]}
|
self.img_queues['img'], self.img_queues['big_img'] = out[0].realize(), out[2].realize()
|
||||||
|
vision_inputs = {'img': out[1], 'big_img': out[3]}
|
||||||
|
|
||||||
if prepare_only:
|
if prepare_only:
|
||||||
return None
|
return None
|
||||||
@@ -398,9 +396,7 @@ def main(demo=False):
|
|||||||
posenet_send = messaging.new_message('cameraOdometry')
|
posenet_send = messaging.new_message('cameraOdometry')
|
||||||
mdv2sp_send = messaging.new_message('modelDataV2SP')
|
mdv2sp_send = messaging.new_message('modelDataV2SP')
|
||||||
|
|
||||||
frame_delay = DT_MDL # compensate for time passed since the frame was captured: current_time - timestamp_eof is 50ms on average
|
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego)
|
||||||
action_delay = DT_MDL / 2 # middle of the interval between model output (current state) and next frame (expected state)
|
|
||||||
action = get_action_from_model(model_output, prev_action, lat_delay + frame_delay + action_delay, long_delay + frame_delay + action_delay, v_ego)
|
|
||||||
prev_action = action
|
prev_action = action
|
||||||
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
|
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
|
||||||
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
|
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:853c6634746ff439a848349d00e4d5581cd941f13f7c1862c31b72a31cc24858
|
oid sha256:78477124cbf3ffe30fa951ebada8410b43c4242c6054584d656f1d329b067e15
|
||||||
size 14061595
|
size 14060847
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:940e9006a25f27f0b6e85da798e6a8fd1f6dd492dd7d0b9ff1a9436460f46129
|
oid sha256:ee29ee5bce84d1ce23e9ff381280de9b4e4d96d2934cd751740354884e112c66
|
||||||
size 46887794
|
size 46877473
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class Parser:
|
|||||||
return outs
|
return outs
|
||||||
|
|
||||||
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||||
plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH)
|
plan_mhp = self.is_mhp(outs, 'plan', ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH)
|
||||||
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
|
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
|
||||||
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||||
if 'planplus' in outs:
|
if 'planplus' in outs:
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import json
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
MODELS_DIR = Path(__file__).parent / 'models'
|
|
||||||
COMPILED_FLAGS_PATH = MODELS_DIR / 'tg_compiled_flags.json'
|
|
||||||
|
|
||||||
|
|
||||||
def set_tinygrad_backend_from_compiled_flags() -> None:
|
|
||||||
if os.path.isfile(COMPILED_FLAGS_PATH):
|
|
||||||
with open(COMPILED_FLAGS_PATH) as f:
|
|
||||||
os.environ['DEV'] = str(json.load(f)['DEV'])
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from math import atan2, radians
|
from math import atan2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
@@ -43,9 +43,6 @@ class DRIVER_MONITOR_SETTINGS:
|
|||||||
self._POSE_YAW_THRESHOLD = 0.4020
|
self._POSE_YAW_THRESHOLD = 0.4020
|
||||||
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
|
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
|
||||||
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
|
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
|
||||||
self._POSE_YAW_MIN_STEER_DEG = 30
|
|
||||||
self._POSE_YAW_STEER_FACTOR = 0.15
|
|
||||||
self._POSE_YAW_STEER_MAX_OFFSET = 0.3927
|
|
||||||
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
|
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
|
||||||
self._PITCH_NATURAL_THRESHOLD = 0.449
|
self._PITCH_NATURAL_THRESHOLD = 0.449
|
||||||
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
|
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
|
||||||
@@ -62,6 +59,7 @@ class DRIVER_MONITOR_SETTINGS:
|
|||||||
self._POSESTD_THRESHOLD = 0.3
|
self._POSESTD_THRESHOLD = 0.3
|
||||||
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
|
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
|
||||||
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
|
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
|
||||||
|
self._ALWAYS_ON_ALERT_MIN_SPEED = 11
|
||||||
|
|
||||||
self._POSE_CALIB_MIN_SPEED = 13 # 30 mph
|
self._POSE_CALIB_MIN_SPEED = 13 # 30 mph
|
||||||
self._POSE_OFFSET_MIN_COUNT = int(60 / self._DT_DMON) # valid data counts before calibration completes, 1min cumulative
|
self._POSE_OFFSET_MIN_COUNT = int(60 / self._DT_DMON) # valid data counts before calibration completes, 1min cumulative
|
||||||
@@ -103,7 +101,6 @@ class DriverPose:
|
|||||||
self.low_std = True
|
self.low_std = True
|
||||||
self.cfactor_pitch = 1.
|
self.cfactor_pitch = 1.
|
||||||
self.cfactor_yaw = 1.
|
self.cfactor_yaw = 1.
|
||||||
self.steer_yaw_offset = 0.
|
|
||||||
|
|
||||||
class DriverProb:
|
class DriverProb:
|
||||||
def __init__(self, raw_priors, max_trackable):
|
def __init__(self, raw_priors, max_trackable):
|
||||||
@@ -241,11 +238,7 @@ class DriverMonitoring:
|
|||||||
yaw_error = self.pose.yaw - min(max(self.pose.yaw_offseter.filtered_stat.mean(),
|
yaw_error = self.pose.yaw - min(max(self.pose.yaw_offseter.filtered_stat.mean(),
|
||||||
self.settings._YAW_MIN_OFFSET), self.settings._YAW_MAX_OFFSET)
|
self.settings._YAW_MIN_OFFSET), self.settings._YAW_MAX_OFFSET)
|
||||||
pitch_error = 0 if pitch_error > 0 else abs(pitch_error) # no positive pitch limit
|
pitch_error = 0 if pitch_error > 0 else abs(pitch_error) # no positive pitch limit
|
||||||
|
yaw_error = abs(yaw_error)
|
||||||
if yaw_error * self.pose.steer_yaw_offset > 0: # unidirectional
|
|
||||||
yaw_error = max(abs(yaw_error) - min(abs(self.pose.steer_yaw_offset), self.settings._POSE_YAW_STEER_MAX_OFFSET), 0.)
|
|
||||||
else:
|
|
||||||
yaw_error = abs(yaw_error)
|
|
||||||
|
|
||||||
pitch_threshold = self.settings._POSE_PITCH_THRESHOLD * self.pose.cfactor_pitch if self.pose.calibrated else self.settings._PITCH_NATURAL_THRESHOLD
|
pitch_threshold = self.settings._POSE_PITCH_THRESHOLD * self.pose.cfactor_pitch if self.pose.calibrated else self.settings._PITCH_NATURAL_THRESHOLD
|
||||||
yaw_threshold = self.settings._POSE_YAW_THRESHOLD * self.pose.cfactor_yaw
|
yaw_threshold = self.settings._POSE_YAW_THRESHOLD * self.pose.cfactor_yaw
|
||||||
@@ -261,7 +254,7 @@ class DriverMonitoring:
|
|||||||
|
|
||||||
return distracted_types
|
return distracted_types
|
||||||
|
|
||||||
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill, demo_mode=False, steering_angle_deg=0.):
|
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill, demo_mode=False):
|
||||||
rhd_pred = driver_state.wheelOnRightProb
|
rhd_pred = driver_state.wheelOnRightProb
|
||||||
# calibrates only when there's movement and either face detected
|
# calibrates only when there's movement and either face detected
|
||||||
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
|
||||||
@@ -284,11 +277,8 @@ class DriverMonitoring:
|
|||||||
|
|
||||||
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
|
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
|
||||||
self.pose.roll, self.pose.pitch, self.pose.yaw = face_orientation_from_net(driver_data.faceOrientation, driver_data.facePosition, cal_rpy)
|
self.pose.roll, self.pose.pitch, self.pose.yaw = face_orientation_from_net(driver_data.faceOrientation, driver_data.facePosition, cal_rpy)
|
||||||
steer_d = max(abs(steering_angle_deg) - self.settings._POSE_YAW_MIN_STEER_DEG, 0.)
|
|
||||||
self.pose.steer_yaw_offset = radians(steer_d) * -np.sign(steering_angle_deg) * self.settings._POSE_YAW_STEER_FACTOR
|
|
||||||
if self.wheel_on_right:
|
if self.wheel_on_right:
|
||||||
self.pose.yaw *= -1
|
self.pose.yaw *= -1
|
||||||
self.pose.steer_yaw_offset *= -1
|
|
||||||
self.wheel_on_right_last = self.wheel_on_right
|
self.wheel_on_right_last = self.wheel_on_right
|
||||||
self.pose.pitch_std = driver_data.faceOrientationStd[0]
|
self.pose.pitch_std = driver_data.faceOrientationStd[0]
|
||||||
self.pose.yaw_std = driver_data.faceOrientationStd[1]
|
self.pose.yaw_std = driver_data.faceOrientationStd[1]
|
||||||
@@ -354,14 +344,10 @@ class DriverMonitoring:
|
|||||||
self._reset_awareness()
|
self._reset_awareness()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
driver_attentive = self.driver_distraction_filter.x < 0.37
|
||||||
awareness_prev = self.awareness
|
awareness_prev = self.awareness
|
||||||
_reaching_pre = self.awareness - self.step_change <= self.threshold_pre
|
|
||||||
_reaching_terminal = self.awareness - self.step_change <= 0
|
|
||||||
standstill_orange_exemption = standstill and _reaching_pre
|
|
||||||
always_on_red_exemption = always_on_valid and not op_engaged and _reaching_terminal
|
|
||||||
|
|
||||||
if self.awareness > 0 and \
|
if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0):
|
||||||
((self.driver_distraction_filter.x < 0.37 and self.face_detected and self.pose.low_std) or standstill_orange_exemption):
|
|
||||||
if driver_engaged:
|
if driver_engaged:
|
||||||
self._reset_awareness()
|
self._reset_awareness()
|
||||||
return
|
return
|
||||||
@@ -374,28 +360,34 @@ class DriverMonitoring:
|
|||||||
if self.awareness > self.threshold_prompt:
|
if self.awareness > self.threshold_prompt:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_reaching_audible = self.awareness - self.step_change <= self.threshold_prompt
|
||||||
|
_reaching_terminal = self.awareness - self.step_change <= 0
|
||||||
|
standstill_orange_exemption = standstill and _reaching_audible
|
||||||
|
always_on_red_exemption = always_on_valid and not op_engaged and _reaching_terminal
|
||||||
|
always_on_lowspeed_exemption = always_on_valid and not op_engaged and car_speed < self.settings._ALWAYS_ON_ALERT_MIN_SPEED
|
||||||
|
|
||||||
certainly_distracted = self.driver_distraction_filter.x > 0.63 and self.driver_distracted and self.face_detected
|
certainly_distracted = self.driver_distraction_filter.x > 0.63 and self.driver_distracted and self.face_detected
|
||||||
maybe_distracted = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME or not self.face_detected
|
maybe_distracted = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME or not self.face_detected
|
||||||
|
|
||||||
if certainly_distracted or maybe_distracted:
|
if certainly_distracted or maybe_distracted:
|
||||||
# should always be counting if distracted unless at standstill and reaching green
|
# should always be counting if distracted unless at standstill (lowspeed for always-on) and reaching orange
|
||||||
# also will not be reaching 0 if DM is active when not engaged
|
# also will not be reaching 0 if DM is active when not engaged
|
||||||
if not (standstill_orange_exemption or always_on_red_exemption):
|
if not (standstill_orange_exemption or always_on_red_exemption or (always_on_lowspeed_exemption and _reaching_audible)):
|
||||||
self.awareness = max(self.awareness - self.step_change, -0.1)
|
self.awareness = max(self.awareness - self.step_change, -0.1)
|
||||||
|
|
||||||
alert = None
|
alert = None
|
||||||
if self.awareness <= 0.:
|
if self.awareness <= 0.:
|
||||||
# terminal red alert: disengagement required
|
# terminal red alert: disengagement required
|
||||||
alert = EventName.driverDistracted3 if self.active_monitoring_mode else EventName.driverUnresponsive3
|
alert = EventName.driverDistracted if self.active_monitoring_mode else EventName.driverUnresponsive
|
||||||
self.terminal_time += 1
|
self.terminal_time += 1
|
||||||
if awareness_prev > 0.:
|
if awareness_prev > 0.:
|
||||||
self.terminal_alert_cnt += 1
|
self.terminal_alert_cnt += 1
|
||||||
elif self.awareness <= self.threshold_prompt:
|
elif self.awareness <= self.threshold_prompt:
|
||||||
# prompt orange alert
|
# prompt orange alert
|
||||||
alert = EventName.driverDistracted2 if self.active_monitoring_mode else EventName.driverUnresponsive2
|
alert = EventName.promptDriverDistracted if self.active_monitoring_mode else EventName.promptDriverUnresponsive
|
||||||
elif self.awareness <= self.threshold_pre:
|
elif self.awareness <= self.threshold_pre and not always_on_lowspeed_exemption:
|
||||||
# pre green alert
|
# pre green alert
|
||||||
alert = EventName.driverDistracted1 if self.active_monitoring_mode else EventName.driverUnresponsive1
|
alert = EventName.preDriverDistracted if self.active_monitoring_mode else EventName.preDriverUnresponsive
|
||||||
|
|
||||||
if alert is not None:
|
if alert is not None:
|
||||||
self.current_events.add(alert)
|
self.current_events.add(alert)
|
||||||
@@ -443,7 +435,7 @@ class DriverMonitoring:
|
|||||||
enabled = sm['selfdriveState'].enabled or sm['carControl'].latActive
|
enabled = sm['selfdriveState'].enabled or sm['carControl'].latActive
|
||||||
wrong_gear = sm['carState'].gearShifter not in (car.CarState.GearShifter.drive, car.CarState.GearShifter.low)
|
wrong_gear = sm['carState'].gearShifter not in (car.CarState.GearShifter.drive, car.CarState.GearShifter.low)
|
||||||
standstill = sm['carState'].standstill
|
standstill = sm['carState'].standstill
|
||||||
driver_engaged = sm['carState'].steeringPressed or (sm['selfdriveState'].enabled and sm['carState'].gasPressed)
|
driver_engaged = sm['carState'].steeringPressed or sm['carState'].gasPressed
|
||||||
brake_disengage_prob = sm['modelV2'].meta.disengagePredictions.brakeDisengageProbs[0] # brake disengage prob in next 2s
|
brake_disengage_prob = sm['modelV2'].meta.disengagePredictions.brakeDisengageProbs[0] # brake disengage prob in next 2s
|
||||||
rpyCalib = sm['liveCalibration'].rpyCalib
|
rpyCalib = sm['liveCalibration'].rpyCalib
|
||||||
self._set_policy(
|
self._set_policy(
|
||||||
@@ -459,7 +451,6 @@ class DriverMonitoring:
|
|||||||
op_engaged=enabled,
|
op_engaged=enabled,
|
||||||
standstill=standstill,
|
standstill=standstill,
|
||||||
demo_mode=demo,
|
demo_mode=demo,
|
||||||
steering_angle_deg=sm['carState'].steeringAngleDeg,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update distraction events
|
# Update distraction events
|
||||||
|
|||||||
@@ -78,11 +78,11 @@ class TestMonitoring:
|
|||||||
assert len(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]) == 0
|
assert len(events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]) == 0
|
||||||
assert events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL + \
|
assert events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL + \
|
||||||
((d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == \
|
((d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == \
|
||||||
EventName.driverDistracted1
|
EventName.preDriverDistracted
|
||||||
assert events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL + \
|
assert events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL + \
|
||||||
((d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == EventName.driverDistracted2
|
((d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
assert events[int((d_status.settings._DISTRACTED_TIME + \
|
assert events[int((d_status.settings._DISTRACTED_TIME + \
|
||||||
((TEST_TIMESPAN-10-d_status.settings._DISTRACTED_TIME)/2))/DT_DMON)].names[0] == EventName.driverDistracted3
|
((TEST_TIMESPAN-10-d_status.settings._DISTRACTED_TIME)/2))/DT_DMON)].names[0] == EventName.driverDistracted
|
||||||
assert isinstance(d_status.awareness, float)
|
assert isinstance(d_status.awareness, float)
|
||||||
|
|
||||||
# engaged, no face detected the whole time, no action
|
# engaged, no face detected the whole time, no action
|
||||||
@@ -91,11 +91,11 @@ class TestMonitoring:
|
|||||||
assert len(events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]) == 0
|
assert len(events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL)/2/DT_DMON)]) == 0
|
||||||
assert events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL + \
|
assert events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL + \
|
||||||
((d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == \
|
((d_status.settings._AWARENESS_PRE_TIME_TILL_TERMINAL-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == \
|
||||||
EventName.driverUnresponsive1
|
EventName.preDriverUnresponsive
|
||||||
assert events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL + \
|
assert events[int((d_status.settings._AWARENESS_TIME-d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL + \
|
||||||
((d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == EventName.driverUnresponsive2
|
((d_status.settings._AWARENESS_PROMPT_TIME_TILL_TERMINAL)/2))/DT_DMON)].names[0] == EventName.promptDriverUnresponsive
|
||||||
assert events[int((d_status.settings._AWARENESS_TIME + \
|
assert events[int((d_status.settings._AWARENESS_TIME + \
|
||||||
((TEST_TIMESPAN-10-d_status.settings._AWARENESS_TIME)/2))/DT_DMON)].names[0] == EventName.driverUnresponsive3
|
((TEST_TIMESPAN-10-d_status.settings._AWARENESS_TIME)/2))/DT_DMON)].names[0] == EventName.driverUnresponsive
|
||||||
|
|
||||||
# engaged, down to orange, driver pays attention, back to normal; then down to orange, driver touches wheel
|
# engaged, down to orange, driver pays attention, back to normal; then down to orange, driver touches wheel
|
||||||
# - should have short orange recovery time and no green afterwards; wheel touch only recovers when paying attention
|
# - should have short orange recovery time and no green afterwards; wheel touch only recovers when paying attention
|
||||||
@@ -108,10 +108,10 @@ class TestMonitoring:
|
|||||||
[car_interaction_DETECTED] * (int(TEST_TIMESPAN/DT_DMON)-int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON))
|
[car_interaction_DETECTED] * (int(TEST_TIMESPAN/DT_DMON)-int(DISTRACTED_SECONDS_TO_ORANGE*3/DT_DMON))
|
||||||
events, _ = self._run_seq(ds_vector, interaction_vector, always_true, always_false)
|
events, _ = self._run_seq(ds_vector, interaction_vector, always_true, always_false)
|
||||||
assert len(events[int(DISTRACTED_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
assert len(events[int(DISTRACTED_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.driverDistracted2
|
assert events[int((DISTRACTED_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
assert len(events[int(DISTRACTED_SECONDS_TO_ORANGE*1.5/DT_DMON)]) == 0
|
assert len(events[int(DISTRACTED_SECONDS_TO_ORANGE*1.5/DT_DMON)]) == 0
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)].names[0] == EventName.driverDistracted2
|
assert events[int((DISTRACTED_SECONDS_TO_ORANGE*3-0.1)/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_ORANGE*3+0.1)/DT_DMON)].names[0] == EventName.driverDistracted2
|
assert events[int((DISTRACTED_SECONDS_TO_ORANGE*3+0.1)/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
assert len(events[int((DISTRACTED_SECONDS_TO_ORANGE*3+2.5)/DT_DMON)]) == 0
|
assert len(events[int((DISTRACTED_SECONDS_TO_ORANGE*3+2.5)/DT_DMON)]) == 0
|
||||||
|
|
||||||
# engaged, down to orange, driver dodges camera, then comes back still distracted, down to red, \
|
# engaged, down to orange, driver dodges camera, then comes back still distracted, down to red, \
|
||||||
@@ -131,9 +131,9 @@ class TestMonitoring:
|
|||||||
op_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+2.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3)/DT_DMON)] \
|
op_vector[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+2.5)/DT_DMON):int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3)/DT_DMON)] \
|
||||||
= [False] * int(0.5/DT_DMON)
|
= [False] * int(0.5/DT_DMON)
|
||||||
events, _ = self._run_seq(ds_vector, interaction_vector, op_vector, always_false)
|
events, _ = self._run_seq(ds_vector, interaction_vector, op_vector, always_false)
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_ORANGE+0.5*_invisible_time)/DT_DMON)].names[0] == EventName.driverDistracted2
|
assert events[int((DISTRACTED_SECONDS_TO_ORANGE+0.5*_invisible_time)/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_RED+1.5*_invisible_time)/DT_DMON)].names[0] == EventName.driverDistracted3
|
assert events[int((DISTRACTED_SECONDS_TO_RED+1.5*_invisible_time)/DT_DMON)].names[0] == EventName.driverDistracted
|
||||||
assert events[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)].names[0] == EventName.driverDistracted3
|
assert events[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+1.5)/DT_DMON)].names[0] == EventName.driverDistracted
|
||||||
assert len(events[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3.5)/DT_DMON)]) == 0
|
assert len(events[int((DISTRACTED_SECONDS_TO_RED+2*_invisible_time+3.5)/DT_DMON)]) == 0
|
||||||
|
|
||||||
# engaged, invisible driver, down to orange, driver touches wheel; then down to orange again, driver appears
|
# engaged, invisible driver, down to orange, driver touches wheel; then down to orange again, driver appears
|
||||||
@@ -147,13 +147,13 @@ class TestMonitoring:
|
|||||||
interaction_vector[int((INVISIBLE_SECONDS_TO_ORANGE)/DT_DMON):int((INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON)] = [True] * int(1/DT_DMON)
|
interaction_vector[int((INVISIBLE_SECONDS_TO_ORANGE)/DT_DMON):int((INVISIBLE_SECONDS_TO_ORANGE+1)/DT_DMON)] = [True] * int(1/DT_DMON)
|
||||||
events, _ = self._run_seq(ds_vector, interaction_vector, 2*always_true, 2*always_false)
|
events, _ = self._run_seq(ds_vector, interaction_vector, 2*always_true, 2*always_false)
|
||||||
assert len(events[int(INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
assert len(events[int(INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive2
|
assert events[int((INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.promptDriverUnresponsive
|
||||||
assert len(events[int((INVISIBLE_SECONDS_TO_ORANGE+0.1)/DT_DMON)]) == 0
|
assert len(events[int((INVISIBLE_SECONDS_TO_ORANGE+0.1)/DT_DMON)]) == 0
|
||||||
if _visible_time == 0.5:
|
if _visible_time == 0.5:
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive2
|
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0] == EventName.promptDriverUnresponsive
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)].names[0] == EventName.driverUnresponsive1
|
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)].names[0] == EventName.preDriverUnresponsive
|
||||||
elif _visible_time == 10:
|
elif _visible_time == 10:
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive2
|
assert events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1-0.1)/DT_DMON)].names[0] == EventName.promptDriverUnresponsive
|
||||||
assert len(events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)]) == 0
|
assert len(events[int((INVISIBLE_SECONDS_TO_ORANGE*2+1+0.1+_visible_time)/DT_DMON)]) == 0
|
||||||
|
|
||||||
# engaged, invisible driver, down to red, driver appears and then touches wheel, then disengages/reengages
|
# engaged, invisible driver, down to red, driver appears and then touches wheel, then disengages/reengages
|
||||||
@@ -168,10 +168,10 @@ class TestMonitoring:
|
|||||||
op_vector[int((INVISIBLE_SECONDS_TO_RED+_visible_time+1)/DT_DMON):int((INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)] = [False] * int(0.5/DT_DMON)
|
op_vector[int((INVISIBLE_SECONDS_TO_RED+_visible_time+1)/DT_DMON):int((INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)] = [False] * int(0.5/DT_DMON)
|
||||||
events, _ = self._run_seq(ds_vector, interaction_vector, op_vector, always_false)
|
events, _ = self._run_seq(ds_vector, interaction_vector, op_vector, always_false)
|
||||||
assert len(events[int(INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
assert len(events[int(INVISIBLE_SECONDS_TO_ORANGE*0.5/DT_DMON)]) == 0
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive2
|
assert events[int((INVISIBLE_SECONDS_TO_ORANGE-0.1)/DT_DMON)].names[0] == EventName.promptDriverUnresponsive
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_RED-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive3
|
assert events[int((INVISIBLE_SECONDS_TO_RED-0.1)/DT_DMON)].names[0] == EventName.driverUnresponsive
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_RED+0.5*_visible_time)/DT_DMON)].names[0] == EventName.driverUnresponsive3
|
assert events[int((INVISIBLE_SECONDS_TO_RED+0.5*_visible_time)/DT_DMON)].names[0] == EventName.driverUnresponsive
|
||||||
assert events[int((INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)].names[0] == EventName.driverUnresponsive3
|
assert events[int((INVISIBLE_SECONDS_TO_RED+_visible_time+0.5)/DT_DMON)].names[0] == EventName.driverUnresponsive
|
||||||
assert len(events[int((INVISIBLE_SECONDS_TO_RED+_visible_time+1+0.1)/DT_DMON)]) == 0
|
assert len(events[int((INVISIBLE_SECONDS_TO_RED+_visible_time+1+0.1)/DT_DMON)]) == 0
|
||||||
|
|
||||||
# disengaged, always distracted driver
|
# disengaged, always distracted driver
|
||||||
@@ -187,21 +187,10 @@ class TestMonitoring:
|
|||||||
standstill_vector = always_true[:]
|
standstill_vector = always_true[:]
|
||||||
standstill_vector[int(_redlight_time/DT_DMON):] = [False] * int((TEST_TIMESPAN-_redlight_time)/DT_DMON)
|
standstill_vector[int(_redlight_time/DT_DMON):] = [False] * int((TEST_TIMESPAN-_redlight_time)/DT_DMON)
|
||||||
events, d_status = self._run_seq(always_distracted, always_false, always_true, standstill_vector)
|
events, d_status = self._run_seq(always_distracted, always_false, always_true, standstill_vector)
|
||||||
assert len(events[int((_redlight_time-0.1)/DT_DMON)]) == 0
|
assert events[int((d_status.settings._DISTRACTED_TIME-d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL+1)/DT_DMON)].names[0] == \
|
||||||
_pre_to_prompt = d_status.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL - d_status.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL
|
EventName.preDriverDistracted
|
||||||
assert events[int((_redlight_time+0.5)/DT_DMON)].names[0] == EventName.driverDistracted1
|
assert events[int((_redlight_time-0.1)/DT_DMON)].names[0] == EventName.preDriverDistracted
|
||||||
assert events[int((_redlight_time+_pre_to_prompt+0.5)/DT_DMON)].names[0] == EventName.driverDistracted2
|
assert events[int((_redlight_time+0.5)/DT_DMON)].names[0] == EventName.promptDriverDistracted
|
||||||
|
|
||||||
# engaged, distracted while moving, then car stops after reaching orange
|
|
||||||
# - should reset timer to pre green at standstill
|
|
||||||
def test_distracted_then_stops(self):
|
|
||||||
_stop_time = DISTRACTED_SECONDS_TO_ORANGE + 1 # stop 1 second after reaching orange
|
|
||||||
standstill_vector = always_false[:]
|
|
||||||
standstill_vector[int(_stop_time/DT_DMON):] = [True] * int((TEST_TIMESPAN-_stop_time)/DT_DMON)
|
|
||||||
events, _ = self._run_seq(always_distracted, always_false, always_true, standstill_vector)
|
|
||||||
# just before and briefly after stopping: orange alert; goes away quickly after stopped
|
|
||||||
assert events[int((_stop_time+0.1)/DT_DMON)].names[0] == EventName.driverDistracted2
|
|
||||||
assert len(events[int((_stop_time+0.5)/DT_DMON)]) == 0
|
|
||||||
|
|
||||||
# engaged, model is somehow uncertain and driver is distracted
|
# engaged, model is somehow uncertain and driver is distracted
|
||||||
# - should fall back to wheel touch after uncertain alert
|
# - should fall back to wheel touch after uncertain alert
|
||||||
@@ -209,56 +198,73 @@ class TestMonitoring:
|
|||||||
ds_vector = [msg_DISTRACTED_BUT_SOMEHOW_UNCERTAIN] * int(TEST_TIMESPAN/DT_DMON)
|
ds_vector = [msg_DISTRACTED_BUT_SOMEHOW_UNCERTAIN] * int(TEST_TIMESPAN/DT_DMON)
|
||||||
interaction_vector = always_false[:]
|
interaction_vector = always_false[:]
|
||||||
events, d_status = self._run_seq(ds_vector, interaction_vector, always_true, always_false)
|
events, d_status = self._run_seq(ds_vector, interaction_vector, always_true, always_false)
|
||||||
assert EventName.driverUnresponsive1 in \
|
assert EventName.preDriverUnresponsive in \
|
||||||
events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME-0.1)/DT_DMON)].names
|
events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME-0.1)/DT_DMON)].names
|
||||||
assert EventName.driverUnresponsive2 in \
|
assert EventName.promptDriverUnresponsive in \
|
||||||
events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names
|
events[int((INVISIBLE_SECONDS_TO_ORANGE-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names
|
||||||
assert EventName.driverUnresponsive3 in \
|
assert EventName.driverUnresponsive in \
|
||||||
events[int((INVISIBLE_SECONDS_TO_RED-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names
|
events[int((INVISIBLE_SECONDS_TO_RED-1+DT_DMON*d_status.settings._HI_STD_FALLBACK_TIME+0.1)/DT_DMON)].names
|
||||||
|
|
||||||
|
|
||||||
def _build_sm(selfdrive_enabled, lat_active, steering_pressed, gas_pressed):
|
@pytest.mark.parametrize("enabled_state, lat_active_state, expected", [
|
||||||
|
(False, False, False), # Both Disabled
|
||||||
|
(True, False, True), # OP Enabled, Lat Inactive
|
||||||
|
(False, True, True), # OP Disabled, Lat Active (e.g. MADS)
|
||||||
|
(True, True, True) # Both Active
|
||||||
|
])
|
||||||
|
def test_enabled_states(enabled_state, lat_active_state, expected):
|
||||||
|
"""
|
||||||
|
Test DriverMonitoring.run_step with all 4 combinations of:
|
||||||
|
- selfdriveState.enabled (True/False)
|
||||||
|
- carControl.latActive (True/False)
|
||||||
|
"""
|
||||||
cs = car.CarState.new_message()
|
cs = car.CarState.new_message()
|
||||||
cs.vEgo = 30.0
|
cs.vEgo = 30.0
|
||||||
cs.gearShifter = car.CarState.GearShifter.drive
|
cs.gearShifter = car.CarState.GearShifter.drive
|
||||||
cs.steeringPressed = steering_pressed
|
cs.standstill = False
|
||||||
cs.gasPressed = gas_pressed
|
cs.steeringPressed = False
|
||||||
|
cs.gasPressed = False
|
||||||
|
|
||||||
ss = log.SelfdriveState.new_message()
|
ss = log.SelfdriveState.new_message()
|
||||||
ss.enabled = selfdrive_enabled
|
ss.enabled = enabled_state
|
||||||
|
|
||||||
cc = car.CarControl.new_message()
|
cc = car.CarControl.new_message()
|
||||||
cc.latActive = lat_active
|
cc.latActive = lat_active_state
|
||||||
|
|
||||||
mv2 = log.ModelDataV2.new_message()
|
mv2 = log.ModelDataV2.new_message()
|
||||||
mv2.meta.disengagePredictions.brakeDisengageProbs = [0.0]
|
mv2.meta.disengagePredictions.brakeDisengageProbs = [0.0]
|
||||||
|
|
||||||
lc = log.LiveCalibrationData.new_message()
|
lc = log.LiveCalibrationData.new_message()
|
||||||
lc.rpyCalib = [0.0, 0.0, 0.0]
|
lc.rpyCalib = [0.0, 0.0, 0.0]
|
||||||
return {
|
|
||||||
'carState': cs, 'selfdriveState': ss, 'carControl': cc,
|
ds = make_msg(False)
|
||||||
'modelV2': mv2, 'liveCalibration': lc, 'driverStateV2': make_msg(False),
|
|
||||||
|
sm = {
|
||||||
|
'carState': cs,
|
||||||
|
'selfdriveState': ss,
|
||||||
|
'carControl': cc,
|
||||||
|
'modelV2': mv2,
|
||||||
|
'liveCalibration': lc,
|
||||||
|
'driverStateV2': ds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
driver_monitoring = DriverMonitoring()
|
||||||
|
|
||||||
@pytest.mark.parametrize("selfdrive_enabled, lat_active, steering, gas, expected_op_engaged, expected_driver_engaged", [
|
# run_test doesn't assign enabled to a variable, so we need to spy on _update_events to see its value
|
||||||
(False, False, False, False, False, False), # disabled
|
captured_args = []
|
||||||
(True, False, False, False, True, False), # OP enabled
|
original_update_events = driver_monitoring._update_events
|
||||||
(False, True, False, False, True, False), # MADS lat-only
|
|
||||||
(True, True, False, False, True, False), # both active
|
|
||||||
(False, True, False, True, True, False), # MADS lat-only + gas
|
|
||||||
(True, True, False, True, True, True), # full op + gas: override
|
|
||||||
(False, True, True, False, True, True), # MADS lat-only + wheel touch: override
|
|
||||||
])
|
|
||||||
def test_run_step_engagement(selfdrive_enabled, lat_active, steering, gas,
|
|
||||||
expected_op_engaged, expected_driver_engaged):
|
|
||||||
sm = _build_sm(selfdrive_enabled, lat_active, steering, gas)
|
|
||||||
dm = DriverMonitoring()
|
|
||||||
captured = {}
|
|
||||||
orig = dm._update_events
|
|
||||||
|
|
||||||
def spy(driver_engaged, op_engaged, standstill, wrong_gear, car_speed):
|
def spy_update_events(driver_engaged, op_engaged, standstill, wrong_gear, car_speed):
|
||||||
captured['driver_engaged'] = driver_engaged
|
captured_args.append(op_engaged)
|
||||||
captured['op_engaged'] = op_engaged
|
return original_update_events(driver_engaged, op_engaged, standstill, wrong_gear, car_speed)
|
||||||
return orig(driver_engaged, op_engaged, standstill, wrong_gear, car_speed)
|
|
||||||
|
driver_monitoring._update_events = spy_update_events
|
||||||
|
|
||||||
|
driver_monitoring.run_step(sm, demo=False)
|
||||||
|
|
||||||
|
# Assertion
|
||||||
|
assert len(captured_args) == 1, "Expected _update_events to be called exactly once"
|
||||||
|
actual_enabled = captured_args[0]
|
||||||
|
|
||||||
|
assert actual_enabled == expected, f"Expected op_engaged={expected}, but got {actual_enabled}"
|
||||||
|
|
||||||
dm._update_events = spy
|
|
||||||
dm.run_step(sm, demo=False)
|
|
||||||
assert captured['op_engaged'] == expected_op_engaged
|
|
||||||
assert captured['driver_engaged'] == expected_driver_engaged
|
|
||||||
|
|||||||
+13
-23
@@ -21,6 +21,8 @@
|
|||||||
#define CUTOFF_IL 400
|
#define CUTOFF_IL 400
|
||||||
#define SATURATE_IL 1000
|
#define SATURATE_IL 1000
|
||||||
|
|
||||||
|
#define ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE 2048
|
||||||
|
|
||||||
ExitHandler do_exit;
|
ExitHandler do_exit;
|
||||||
|
|
||||||
bool check_connected(Panda *panda) {
|
bool check_connected(Panda *panda) {
|
||||||
@@ -32,8 +34,15 @@ bool check_connected(Panda *panda) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool process_mads_heartbeat(SubMaster *sm) {
|
bool process_mads_heartbeat(SubMaster *sm) {
|
||||||
|
const int &alt_exp = (*sm)["carParams"].getCarParams().getAlternativeExperience();
|
||||||
|
const bool disengage_lateral_on_brake = (alt_exp & ALT_EXP_MADS_DISENGAGE_LATERAL_ON_BRAKE) != 0;
|
||||||
|
|
||||||
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
|
const auto &mads = (*sm)["selfdriveStateSP"].getSelfdriveStateSP().getMads();
|
||||||
return sm->allAliveAndValid({"selfdriveStateSP"}) && mads.getEnabled();
|
const bool heartbeat_type = disengage_lateral_on_brake ? mads.getActive() : mads.getEnabled();
|
||||||
|
|
||||||
|
const bool engaged = sm->allAliveAndValid({"selfdriveStateSP"}) && heartbeat_type;
|
||||||
|
|
||||||
|
return engaged;
|
||||||
}
|
}
|
||||||
|
|
||||||
Panda *connect(std::string serial) {
|
Panda *connect(std::string serial) {
|
||||||
@@ -143,8 +152,6 @@ void fill_panda_state(cereal::PandaState::Builder &ps, cereal::PandaState::Panda
|
|||||||
ps.setSbu1Voltage(health.sbu1_voltage_mV / 1000.0f);
|
ps.setSbu1Voltage(health.sbu1_voltage_mV / 1000.0f);
|
||||||
ps.setSbu2Voltage(health.sbu2_voltage_mV / 1000.0f);
|
ps.setSbu2Voltage(health.sbu2_voltage_mV / 1000.0f);
|
||||||
ps.setSoundOutputLevel(health.sound_output_level_pkt);
|
ps.setSoundOutputLevel(health.sound_output_level_pkt);
|
||||||
ps.setControlsAllowedLateral(health.controls_allowed_lateral_pkt);
|
|
||||||
ps.setControlsAllowedLongitudinal(health.controls_allowed_longitudinal_pkt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_panda_can_state(cereal::PandaState::PandaCanState::Builder &cs, const can_health_t &can_health) {
|
void fill_panda_can_state(cereal::PandaState::PandaCanState::Builder &cs, const can_health_t &can_health) {
|
||||||
@@ -299,7 +306,7 @@ void process_panda_state(Panda *panda, PubMaster *pm, bool engaged, bool engaged
|
|||||||
panda->send_heartbeat(engaged, engaged_mads);
|
panda->send_heartbeat(engaged, engaged_mads);
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control, bool is_onroad) {
|
void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control) {
|
||||||
static Params params;
|
static Params params;
|
||||||
static SubMaster sm({"deviceState", "driverCameraState"});
|
static SubMaster sm({"deviceState", "driverCameraState"});
|
||||||
|
|
||||||
@@ -309,8 +316,6 @@ void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control,
|
|||||||
static int prev_ir_pwr = 999;
|
static int prev_ir_pwr = 999;
|
||||||
static uint32_t prev_frame_id = UINT32_MAX;
|
static uint32_t prev_frame_id = UINT32_MAX;
|
||||||
static bool driver_view = false;
|
static bool driver_view = false;
|
||||||
static bool not_car = false;
|
|
||||||
static bool not_car_checked = false;
|
|
||||||
|
|
||||||
// TODO: can we merge these?
|
// TODO: can we merge these?
|
||||||
static FirstOrderFilter integ_lines_filter(0, 30.0, 0.05);
|
static FirstOrderFilter integ_lines_filter(0, 30.0, 0.05);
|
||||||
@@ -356,21 +361,6 @@ void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control,
|
|||||||
ir_pwr = 0;
|
ir_pwr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off IR leds if body
|
|
||||||
if (!not_car_checked && is_onroad) {
|
|
||||||
std::string cp_bytes = params.get("CarParams");
|
|
||||||
if (cp_bytes.size() > 0) {
|
|
||||||
AlignedBuffer aligned_buf;
|
|
||||||
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
|
|
||||||
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
|
|
||||||
not_car = CP.getNotCar();
|
|
||||||
not_car_checked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (not_car) {
|
|
||||||
ir_pwr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir_pwr != prev_ir_pwr || sm.frame % 100 == 0) {
|
if (ir_pwr != prev_ir_pwr || sm.frame % 100 == 0) {
|
||||||
int16_t ir_panda = util::map_val(ir_pwr, 0, 100, 0, MAX_IR_PANDA_VAL);
|
int16_t ir_panda = util::map_val(ir_pwr, 0, 100, 0, MAX_IR_PANDA_VAL);
|
||||||
panda->set_ir_pwr(ir_panda);
|
panda->set_ir_pwr(ir_panda);
|
||||||
@@ -390,7 +380,7 @@ void pandad_run(Panda *panda) {
|
|||||||
|
|
||||||
Params params;
|
Params params;
|
||||||
RateKeeper rk("pandad", 100);
|
RateKeeper rk("pandad", 100);
|
||||||
SubMaster sm({"selfdriveState", "selfdriveStateSP"});
|
SubMaster sm({"selfdriveState", "selfdriveStateSP", "carParams"});
|
||||||
PubMaster pm({"can", "pandaStates", "peripheralState"});
|
PubMaster pm({"can", "pandaStates", "peripheralState"});
|
||||||
PandaSafety panda_safety(panda);
|
PandaSafety panda_safety(panda);
|
||||||
bool engaged = false;
|
bool engaged = false;
|
||||||
@@ -404,7 +394,7 @@ void pandad_run(Panda *panda) {
|
|||||||
|
|
||||||
// Process peripheral state at 20 Hz
|
// Process peripheral state at 20 Hz
|
||||||
if (rk.frame() % 5 == 0) {
|
if (rk.frame() % 5 == 0) {
|
||||||
process_peripheral_state(panda, &pm, no_fan_control, is_onroad);
|
process_peripheral_state(panda, &pm, no_fan_control);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process panda state at 10 Hz
|
// Process panda state at 10 Hz
|
||||||
|
|||||||
@@ -78,6 +78,22 @@ class TestPandad:
|
|||||||
|
|
||||||
assert any(Panda(s).is_internal() for s in Panda.list())
|
assert any(Panda(s).is_internal() for s in Panda.list())
|
||||||
|
|
||||||
|
def test_best_case_startup_time(self):
|
||||||
|
# run once so we're up to date
|
||||||
|
self._run_test(60)
|
||||||
|
|
||||||
|
ts = []
|
||||||
|
for _ in range(10):
|
||||||
|
# should be nearly instant this time
|
||||||
|
dt = self._run_test(5)
|
||||||
|
ts.append(dt)
|
||||||
|
|
||||||
|
# 5s for USB (due to enumeration)
|
||||||
|
# - 0.2s pandad -> pandad
|
||||||
|
# - plus some buffer
|
||||||
|
print("startup times", ts, sum(ts) / len(ts))
|
||||||
|
assert 0.1 < (sum(ts)/len(ts)) < 0.7
|
||||||
|
|
||||||
def test_old_spi_protocol(self):
|
def test_old_spi_protocol(self):
|
||||||
# flash firmware with old SPI protocol
|
# flash firmware with old SPI protocol
|
||||||
self._flash_bootstub(os.path.join(HERE, "bootstub.panda_h7_spiv0.bin"))
|
self._flash_bootstub(os.path.join(HERE, "bootstub.panda_h7_spiv0.bin"))
|
||||||
|
|||||||
@@ -235,11 +235,6 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
"Ensure road ahead is clear"),
|
"Ensure road ahead is clear"),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.lateralManeuver: {
|
|
||||||
ET.WARNING: longitudinal_maneuver_alert,
|
|
||||||
ET.PERMANENT: NormalPermanentAlert("Lateral Maneuver Mode"),
|
|
||||||
},
|
|
||||||
|
|
||||||
EventName.selfdriveInitializing: {
|
EventName.selfdriveInitializing: {
|
||||||
ET.NO_ENTRY: NoEntryAlert("System Initializing"),
|
ET.NO_ENTRY: NoEntryAlert("System Initializing"),
|
||||||
},
|
},
|
||||||
@@ -338,7 +333,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8),
|
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverDistracted1: {
|
EventName.preDriverDistracted: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Pay Attention",
|
"Pay Attention",
|
||||||
"",
|
"",
|
||||||
@@ -346,7 +341,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, .1),
|
Priority.LOW, VisualAlert.none, AudibleAlert.none, .1),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverDistracted2: {
|
EventName.promptDriverDistracted: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Pay Attention",
|
"Pay Attention",
|
||||||
"Driver Distracted",
|
"Driver Distracted",
|
||||||
@@ -354,7 +349,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
|
Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverDistracted3: {
|
EventName.driverDistracted: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"DISENGAGE IMMEDIATELY",
|
"DISENGAGE IMMEDIATELY",
|
||||||
"Driver Distracted",
|
"Driver Distracted",
|
||||||
@@ -362,7 +357,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1),
|
Priority.HIGH, VisualAlert.steerRequired, AudibleAlert.warningImmediate, .1),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverUnresponsive1: {
|
EventName.preDriverUnresponsive: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Touch Steering Wheel: No Face Detected",
|
"Touch Steering Wheel: No Face Detected",
|
||||||
"",
|
"",
|
||||||
@@ -370,7 +365,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .1),
|
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.none, .1),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverUnresponsive2: {
|
EventName.promptDriverUnresponsive: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Touch Steering Wheel",
|
"Touch Steering Wheel",
|
||||||
"Driver Unresponsive",
|
"Driver Unresponsive",
|
||||||
@@ -378,7 +373,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
|
Priority.MID, VisualAlert.steerRequired, AudibleAlert.promptDistracted, .1),
|
||||||
},
|
},
|
||||||
|
|
||||||
EventName.driverUnresponsive3: {
|
EventName.driverUnresponsive: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"DISENGAGE IMMEDIATELY",
|
"DISENGAGE IMMEDIATELY",
|
||||||
"Driver Unresponsive",
|
"Driver Unresponsive",
|
||||||
@@ -858,14 +853,14 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
|||||||
|
|
||||||
if HARDWARE.get_device_type() == 'mici':
|
if HARDWARE.get_device_type() == 'mici':
|
||||||
EVENTS.update({
|
EVENTS.update({
|
||||||
EventName.driverDistracted1: {
|
EventName.preDriverDistracted: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Pay Attention",
|
"Pay Attention",
|
||||||
"",
|
"",
|
||||||
AlertStatus.normal, AlertSize.small,
|
AlertStatus.normal, AlertSize.small,
|
||||||
Priority.LOW, VisualAlert.none, AudibleAlert.none, 2),
|
Priority.LOW, VisualAlert.none, AudibleAlert.none, 2),
|
||||||
},
|
},
|
||||||
EventName.driverDistracted2: {
|
EventName.promptDriverDistracted: {
|
||||||
ET.PERMANENT: Alert(
|
ET.PERMANENT: Alert(
|
||||||
"Pay Attention",
|
"Pay Attention",
|
||||||
"Driver Distracted",
|
"Driver Distracted",
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ class SelfdriveD(CruiseHelper):
|
|||||||
# TODO: de-couple selfdrived with card/conflate on carState without introducing controls mismatches
|
# TODO: de-couple selfdrived with card/conflate on carState without introducing controls mismatches
|
||||||
self.car_state_sock = messaging.sub_sock('carState', timeout=20)
|
self.car_state_sock = messaging.sub_sock('carState', timeout=20)
|
||||||
|
|
||||||
ignore = self.sensor_packets + self.gps_packets + ['alertDebug', 'lateralManeuverPlan'] + ['modelDataV2SP']
|
ignore = self.sensor_packets + self.gps_packets + ['alertDebug'] + ['modelDataV2SP']
|
||||||
if SIMULATION:
|
if SIMULATION:
|
||||||
ignore += ['driverCameraState', 'managerState']
|
ignore += ['driverCameraState', 'managerState']
|
||||||
if REPLAY:
|
if REPLAY:
|
||||||
@@ -99,7 +99,7 @@ class SelfdriveD(CruiseHelper):
|
|||||||
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay',
|
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay',
|
||||||
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
|
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
|
||||||
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback',
|
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback',
|
||||||
'lateralManeuverPlan', 'modelDataV2SP', 'longitudinalPlanSP'] + \
|
'modelDataV2SP', 'longitudinalPlanSP'] + \
|
||||||
self.camera_packets + self.sensor_packets + self.gps_packets,
|
self.camera_packets + self.sensor_packets + self.gps_packets,
|
||||||
ignore_alive=ignore, ignore_avg_freq=ignore,
|
ignore_alive=ignore, ignore_avg_freq=ignore,
|
||||||
ignore_valid=ignore, frequency=int(1/DT_CTRL))
|
ignore_valid=ignore, frequency=int(1/DT_CTRL))
|
||||||
@@ -183,10 +183,7 @@ class SelfdriveD(CruiseHelper):
|
|||||||
self.events.add(EventName.joystickDebug)
|
self.events.add(EventName.joystickDebug)
|
||||||
self.startup_event = None
|
self.startup_event = None
|
||||||
|
|
||||||
if self.sm.recv_frame['lateralManeuverPlan'] > 0:
|
if self.sm.recv_frame['alertDebug'] > 0:
|
||||||
self.events.add(EventName.lateralManeuver)
|
|
||||||
self.startup_event = None
|
|
||||||
elif self.sm.recv_frame['alertDebug'] > 0:
|
|
||||||
self.events.add(EventName.longitudinalManeuver)
|
self.events.add(EventName.longitudinalManeuver)
|
||||||
self.startup_event = None
|
self.startup_event = None
|
||||||
|
|
||||||
@@ -230,7 +227,7 @@ class SelfdriveD(CruiseHelper):
|
|||||||
|
|
||||||
if self.CP.notCar:
|
if self.CP.notCar:
|
||||||
# wait for everything to init first
|
# wait for everything to init first
|
||||||
if self.sm.frame > int(2. / DT_CTRL) and self.initialized:
|
if self.sm.frame > int(5. / DT_CTRL) and self.initialized:
|
||||||
# body always wants to enable
|
# body always wants to enable
|
||||||
self.events.add(EventName.pcmEnable)
|
self.events.add(EventName.pcmEnable)
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class FuzzyGenerator:
|
|||||||
|
|
||||||
def generate_struct(self, schema: capnp.lib.capnp._StructSchema, event: str | None = None) -> st.SearchStrategy[dict[str, Any]]:
|
def generate_struct(self, schema: capnp.lib.capnp._StructSchema, event: str | None = None) -> st.SearchStrategy[dict[str, Any]]:
|
||||||
single_fill: tuple[str, ...] = (event,) if event else (self.draw(st.sampled_from(schema.union_fields)),) if schema.union_fields else ()
|
single_fill: tuple[str, ...] = (event,) if event else (self.draw(st.sampled_from(schema.union_fields)),) if schema.union_fields else ()
|
||||||
fields_to_generate = [f for f in schema.non_union_fields + single_fill if not f.endswith('DEPRECATED') and f != 'deprecated']
|
fields_to_generate = schema.non_union_fields + single_fill
|
||||||
return st.fixed_dictionaries({field: self.generate_field(schema.fields[field]) for field in fields_to_generate})
|
return st.fixed_dictionaries({field: self.generate_field(schema.fields[field]) for field in fields_to_generate if not field.endswith('DEPRECATED')})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@cache
|
@cache
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ def _diff_capnp_values(v1, v2, path, tolerance):
|
|||||||
for i in range(n):
|
for i in range(n):
|
||||||
yield from _diff_capnp_values(v1[i], v2[i], path + (str(i),), tolerance)
|
yield from _diff_capnp_values(v1[i], v2[i], path + (str(i),), tolerance)
|
||||||
if n2 > n:
|
if n2 > n:
|
||||||
yield 'add', dot, [(i, v2[i]) for i in range(n, n2)]
|
yield 'add', dot, list(enumerate(v2[n:], n))
|
||||||
if n1 > n:
|
if n1 > n:
|
||||||
yield 'remove', dot, list(reversed([(i, v1[i]) for i in range(n, n1)]))
|
yield 'remove', dot, list(reversed([(i, v1[i]) for i in range(n, n1)]))
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import os
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
from opendbc.car.tests.car_diff import format_diff, format_numeric_diffs
|
|
||||||
from openpilot.selfdrive.test.process_replay.compare_logs import compare_logs
|
|
||||||
from openpilot.selfdrive.test.process_replay.process_replay import PROC_REPLAY_DIR
|
|
||||||
|
|
||||||
|
|
||||||
class MsgWrap:
|
|
||||||
"""Adapter so to_dict() includes defaults"""
|
|
||||||
def __init__(self, msg):
|
|
||||||
self._msg = msg
|
|
||||||
def to_dict(self) -> dict:
|
|
||||||
return self._msg.to_dict(verbose=True)
|
|
||||||
|
|
||||||
|
|
||||||
def diff_process(cfg, ref_msgs, new_msgs) -> tuple | None:
|
|
||||||
ref = defaultdict(list)
|
|
||||||
new = defaultdict(list)
|
|
||||||
for m in ref_msgs:
|
|
||||||
if m.which() in cfg.subs:
|
|
||||||
ref[m.which()].append(m)
|
|
||||||
for m in new_msgs:
|
|
||||||
if m.which() in cfg.subs:
|
|
||||||
new[m.which()].append(m)
|
|
||||||
|
|
||||||
diffs = []
|
|
||||||
for sub in cfg.subs:
|
|
||||||
if len(ref[sub]) != len(new[sub]):
|
|
||||||
diffs.append((f"{sub} (message count)", 0, (len(ref[sub]), len(new[sub])), 0))
|
|
||||||
for i, (r, n) in enumerate(zip(ref[sub], new[sub], strict=False)):
|
|
||||||
for d in compare_logs([r], [n], cfg.ignore, tolerance=cfg.tolerance):
|
|
||||||
if d[0] == "change":
|
|
||||||
a, b = d[2]
|
|
||||||
if a != a and b != b:
|
|
||||||
continue
|
|
||||||
diffs.append((d[1], i, d[2], r.logMonoTime))
|
|
||||||
elif d[0] in ("add", "remove"):
|
|
||||||
for item in d[2]:
|
|
||||||
if item[1] != item[1]:
|
|
||||||
continue
|
|
||||||
diffs.append((f"{d[1]}.{item[0]}", i, (d[0], item[1]), r.logMonoTime))
|
|
||||||
return (diffs, ref, new) if diffs else None
|
|
||||||
|
|
||||||
|
|
||||||
def diff_format(diffs, ref, new, field) -> list[str]:
|
|
||||||
if any(part.isdigit() for part in field.split(".")):
|
|
||||||
return format_numeric_diffs(diffs)
|
|
||||||
msg_type = field.split(".")[0]
|
|
||||||
ref_ts = [(m.logMonoTime, MsgWrap(m)) for m in ref.get(msg_type, [])]
|
|
||||||
new_wrapped = [MsgWrap(m) for m in new.get(msg_type, [])]
|
|
||||||
if not ref_ts or not new_wrapped:
|
|
||||||
return format_numeric_diffs(diffs)
|
|
||||||
return format_diff(diffs, ref_ts, new_wrapped, field)
|
|
||||||
|
|
||||||
|
|
||||||
def diff_report(replay_diffs, segments) -> None:
|
|
||||||
seg_to_plat = {seg: plat for plat, seg in segments}
|
|
||||||
|
|
||||||
with_diffs, errors, n_passed = [], [], 0
|
|
||||||
for seg, proc, data in replay_diffs:
|
|
||||||
plat = seg_to_plat.get(seg, "UNKNOWN")
|
|
||||||
if data is None:
|
|
||||||
n_passed += 1
|
|
||||||
elif isinstance(data, str):
|
|
||||||
errors.append((plat, seg, proc, data))
|
|
||||||
else:
|
|
||||||
with_diffs.append((plat, seg, proc, data))
|
|
||||||
|
|
||||||
icon = "⚠️" if with_diffs else "✅"
|
|
||||||
lines = [
|
|
||||||
"## Process replay diff report",
|
|
||||||
"Replays driving segments through this PR and compares the behavior to master.",
|
|
||||||
"Please review any changes carefully to ensure they are expected.\n",
|
|
||||||
f"{icon} {len(with_diffs)} changed, {n_passed} passed, {len(errors)} errors",
|
|
||||||
]
|
|
||||||
|
|
||||||
for plat, seg, proc, err in errors:
|
|
||||||
lines.append(f"\nERROR {plat} - {seg} [{proc}]: {err}")
|
|
||||||
|
|
||||||
if with_diffs:
|
|
||||||
lines.append("<details><summary><b>Show changes</b></summary>\n\n```")
|
|
||||||
for plat, seg, proc, (diffs, ref, new) in with_diffs:
|
|
||||||
lines.append(f"\n{plat} - {seg} [{proc}]")
|
|
||||||
by_field = defaultdict(list)
|
|
||||||
for d in diffs:
|
|
||||||
by_field[d[0]].append(d)
|
|
||||||
for field, fd in sorted(by_field.items()):
|
|
||||||
lines.append(f"\n {field} ({len(fd)} diffs)")
|
|
||||||
lines.extend(diff_format(fd, ref, new, field))
|
|
||||||
lines.append("```\n</details>")
|
|
||||||
|
|
||||||
with open(os.path.join(PROC_REPLAY_DIR, "diff_report.txt"), "w") as f:
|
|
||||||
f.write("\n".join(lines))
|
|
||||||
@@ -39,7 +39,6 @@ def migrate_all(lr: LogIterable, manager_states: bool = False, panda_states: boo
|
|||||||
migrate_carOutput,
|
migrate_carOutput,
|
||||||
migrate_controlsState,
|
migrate_controlsState,
|
||||||
migrate_carState,
|
migrate_carState,
|
||||||
migrate_livePose,
|
|
||||||
migrate_liveTracks,
|
migrate_liveTracks,
|
||||||
migrate_driverAssistance,
|
migrate_driverAssistance,
|
||||||
migrate_drivingModelData,
|
migrate_drivingModelData,
|
||||||
@@ -100,17 +99,6 @@ def migration(inputs: list[str], product: str|None=None):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def migrate_onroad_event(event: capnp.lib.capnp._DynamicStructReader):
|
|
||||||
event_dict = event.to_dict()
|
|
||||||
try:
|
|
||||||
return log.OnroadEvent(**event_dict)
|
|
||||||
except capnp.lib.capnp.KjException as e:
|
|
||||||
# Ignore legacy events the current schema no longer defines.
|
|
||||||
if "enum has no such enumerant" in str(e):
|
|
||||||
return None
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@migration(inputs=["longitudinalPlan", "carParams"])
|
@migration(inputs=["longitudinalPlan", "carParams"])
|
||||||
def migrate_longitudinalPlan(msgs):
|
def migrate_longitudinalPlan(msgs):
|
||||||
ops = []
|
ops = []
|
||||||
@@ -189,7 +177,6 @@ def migrate_liveLocationKalman(msgs):
|
|||||||
m = messaging.new_message('livePose')
|
m = messaging.new_message('livePose')
|
||||||
m.valid = msg.valid
|
m.valid = msg.valid
|
||||||
m.logMonoTime = msg.logMonoTime
|
m.logMonoTime = msg.logMonoTime
|
||||||
m.livePose.timestamp = msg.logMonoTime
|
|
||||||
for field in ["orientationNED", "velocityDevice", "accelerationDevice", "angularVelocityDevice"]:
|
for field in ["orientationNED", "velocityDevice", "accelerationDevice", "angularVelocityDevice"]:
|
||||||
lp_field, llk_field = getattr(m.livePose, field), getattr(msg.liveLocationKalmanDEPRECATED, field)
|
lp_field, llk_field = getattr(m.livePose, field), getattr(msg.liveLocationKalmanDEPRECATED, field)
|
||||||
lp_field.x, lp_field.y, lp_field.z = llk_field.value or nans
|
lp_field.x, lp_field.y, lp_field.z = llk_field.value or nans
|
||||||
@@ -201,21 +188,6 @@ def migrate_liveLocationKalman(msgs):
|
|||||||
return ops, [], []
|
return ops, [], []
|
||||||
|
|
||||||
|
|
||||||
@migration(inputs=["livePose"])
|
|
||||||
def migrate_livePose(msgs):
|
|
||||||
ops = []
|
|
||||||
needs_migration = all(msg.livePose.timestamp == 0 for _, msg in msgs if msg.which() == 'livePose')
|
|
||||||
if not needs_migration:
|
|
||||||
return [], [], []
|
|
||||||
|
|
||||||
for index, msg in msgs:
|
|
||||||
if msg.which() == "livePose":
|
|
||||||
new_msg = msg.as_builder()
|
|
||||||
new_msg.livePose.timestamp = msg.logMonoTime
|
|
||||||
ops.append((index, new_msg.as_reader()))
|
|
||||||
return ops, [], []
|
|
||||||
|
|
||||||
|
|
||||||
@migration(inputs=["controlsState"], product="selfdriveState")
|
@migration(inputs=["controlsState"], product="selfdriveState")
|
||||||
def migrate_controlsState(msgs):
|
def migrate_controlsState(msgs):
|
||||||
add_ops = []
|
add_ops = []
|
||||||
@@ -227,7 +199,7 @@ def migrate_controlsState(msgs):
|
|||||||
for field in ("enabled", "active", "state", "engageable", "alertText1", "alertText2",
|
for field in ("enabled", "active", "state", "engageable", "alertText1", "alertText2",
|
||||||
"alertStatus", "alertSize", "alertType", "experimentalMode",
|
"alertStatus", "alertSize", "alertType", "experimentalMode",
|
||||||
"personality"):
|
"personality"):
|
||||||
setattr(ss, field, getattr(msg.controlsState.deprecated, field))
|
setattr(ss, field, getattr(msg.controlsState, field+"DEPRECATED"))
|
||||||
add_ops.append(m.as_reader())
|
add_ops.append(m.as_reader())
|
||||||
return [], add_ops, []
|
return [], add_ops, []
|
||||||
|
|
||||||
@@ -240,10 +212,10 @@ def migrate_carState(msgs):
|
|||||||
if msg.which() == 'controlsState':
|
if msg.which() == 'controlsState':
|
||||||
last_cs = msg
|
last_cs = msg
|
||||||
elif msg.which() == 'carState' and last_cs is not None:
|
elif msg.which() == 'carState' and last_cs is not None:
|
||||||
if last_cs.controlsState.deprecated.vCruise - msg.carState.vCruise > 0.1:
|
if last_cs.controlsState.vCruiseDEPRECATED - msg.carState.vCruise > 0.1:
|
||||||
msg = msg.as_builder()
|
msg = msg.as_builder()
|
||||||
msg.carState.vCruise = last_cs.controlsState.deprecated.vCruise
|
msg.carState.vCruise = last_cs.controlsState.vCruiseDEPRECATED
|
||||||
msg.carState.vCruiseCluster = last_cs.controlsState.deprecated.vCruiseCluster
|
msg.carState.vCruiseCluster = last_cs.controlsState.vCruiseClusterDEPRECATED
|
||||||
ops.append((index, msg.as_reader()))
|
ops.append((index, msg.as_reader()))
|
||||||
return ops, [], []
|
return ops, [], []
|
||||||
|
|
||||||
@@ -305,7 +277,7 @@ def migrate_pandaStates(msgs):
|
|||||||
safety_param_migration = {
|
safety_param_migration = {
|
||||||
"TOYOTA_PRIUS": EPS_SCALE["TOYOTA_PRIUS"] | ToyotaSafetyFlags.STOCK_LONGITUDINAL,
|
"TOYOTA_PRIUS": EPS_SCALE["TOYOTA_PRIUS"] | ToyotaSafetyFlags.STOCK_LONGITUDINAL,
|
||||||
"TOYOTA_RAV4": EPS_SCALE["TOYOTA_RAV4"] | ToyotaSafetyFlags.ALT_BRAKE,
|
"TOYOTA_RAV4": EPS_SCALE["TOYOTA_RAV4"] | ToyotaSafetyFlags.ALT_BRAKE,
|
||||||
"KIA_EV6": HyundaiSafetyFlags.EV_GAS | HyundaiSafetyFlags.CANFD_LKA_STEER_MSG,
|
"KIA_EV6": HyundaiSafetyFlags.EV_GAS | HyundaiSafetyFlags.CANFD_LKA_STEERING,
|
||||||
"CHEVROLET_VOLT": GMSafetyFlags.EV,
|
"CHEVROLET_VOLT": GMSafetyFlags.EV,
|
||||||
"CHEVROLET_BOLT_EUV": GMSafetyFlags.EV | GMSafetyFlags.HW_CAM,
|
"CHEVROLET_BOLT_EUV": GMSafetyFlags.EV | GMSafetyFlags.HW_CAM,
|
||||||
}
|
}
|
||||||
@@ -469,13 +441,12 @@ def migrate_onroadEvents(msgs):
|
|||||||
for event in msg.onroadEventsDEPRECATED:
|
for event in msg.onroadEventsDEPRECATED:
|
||||||
try:
|
try:
|
||||||
if not str(event.name).endswith('DEPRECATED'):
|
if not str(event.name).endswith('DEPRECATED'):
|
||||||
migrated_event = migrate_onroad_event(event)
|
# dict converts name enum into string representation
|
||||||
if migrated_event is not None:
|
onroadEvents.append(log.OnroadEvent(**event.to_dict()))
|
||||||
onroadEvents.append(migrated_event)
|
|
||||||
except RuntimeError: # Member was null
|
except RuntimeError: # Member was null
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
new_msg = messaging.new_message('onroadEvents', len(onroadEvents))
|
new_msg = messaging.new_message('onroadEvents', len(msg.onroadEventsDEPRECATED))
|
||||||
new_msg.valid = msg.valid
|
new_msg.valid = msg.valid
|
||||||
new_msg.logMonoTime = msg.logMonoTime
|
new_msg.logMonoTime = msg.logMonoTime
|
||||||
new_msg.onroadEvents = onroadEvents
|
new_msg.onroadEvents = onroadEvents
|
||||||
@@ -490,12 +461,11 @@ def migrate_driverMonitoringState(msgs):
|
|||||||
for index, msg in msgs:
|
for index, msg in msgs:
|
||||||
msg = msg.as_builder()
|
msg = msg.as_builder()
|
||||||
events = []
|
events = []
|
||||||
for event in msg.driverMonitoringState.deprecated.events:
|
for event in msg.driverMonitoringState.eventsDEPRECATED:
|
||||||
try:
|
try:
|
||||||
if not str(event.name).endswith('DEPRECATED'):
|
if not str(event.name).endswith('DEPRECATED'):
|
||||||
migrated_event = migrate_onroad_event(event)
|
# dict converts name enum into string representation
|
||||||
if migrated_event is not None:
|
events.append(log.OnroadEvent(**event.to_dict()))
|
||||||
events.append(migrated_event)
|
|
||||||
except RuntimeError: # Member was null
|
except RuntimeError: # Member was null
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,6 @@ class ProcessContainer:
|
|||||||
self.cfg = copy.deepcopy(cfg)
|
self.cfg = copy.deepcopy(cfg)
|
||||||
self.process = copy.deepcopy(managed_processes[cfg.proc_name])
|
self.process = copy.deepcopy(managed_processes[cfg.proc_name])
|
||||||
self.msg_queue: list[capnp._DynamicStructReader] = []
|
self.msg_queue: list[capnp._DynamicStructReader] = []
|
||||||
self.last_input_log_mono_time: int = -1
|
|
||||||
self.cnt = 0
|
self.cnt = 0
|
||||||
self.pm: messaging.PubMaster | None = None
|
self.pm: messaging.PubMaster | None = None
|
||||||
self.sockets: list[messaging.SubSocket] | None = None
|
self.sockets: list[messaging.SubSocket] | None = None
|
||||||
@@ -269,7 +268,6 @@ class ProcessContainer:
|
|||||||
ms = messaging.drain_sock(socket)
|
ms = messaging.drain_sock(socket)
|
||||||
for m in ms:
|
for m in ms:
|
||||||
m = m.as_builder()
|
m = m.as_builder()
|
||||||
assert start_time > 0, "start_time must be positive"
|
|
||||||
m.logMonoTime = start_time + int(self.cfg.processing_time * 1e9)
|
m.logMonoTime = start_time + int(self.cfg.processing_time * 1e9)
|
||||||
output_msgs.append(m.as_reader())
|
output_msgs.append(m.as_reader())
|
||||||
return output_msgs
|
return output_msgs
|
||||||
@@ -296,11 +294,10 @@ class ProcessContainer:
|
|||||||
trigger_empty_recv = any(m.which() == self.cfg.main_pub for m in self.msg_queue)
|
trigger_empty_recv = any(m.which() == self.cfg.main_pub for m in self.msg_queue)
|
||||||
|
|
||||||
# get output msgs from previous inputs
|
# get output msgs from previous inputs
|
||||||
output_msgs = self.get_output_msgs(self.last_input_log_mono_time)
|
output_msgs = self.get_output_msgs(msg.logMonoTime)
|
||||||
|
|
||||||
for m in self.msg_queue:
|
for m in self.msg_queue:
|
||||||
self.pm.send(m.which(), m.as_builder())
|
self.pm.send(m.which(), m.as_builder())
|
||||||
self.last_input_log_mono_time = max(self.last_input_log_mono_time, m.logMonoTime)
|
|
||||||
# send frames if needed
|
# send frames if needed
|
||||||
if self.vipc_server is not None and m.which() in self.cfg.vision_pubs:
|
if self.vipc_server is not None and m.which() in self.cfg.vision_pubs:
|
||||||
camera_state = getattr(m, m.which())
|
camera_state = getattr(m, m.which())
|
||||||
@@ -516,7 +513,6 @@ CONFIGS = [
|
|||||||
ignore=["logMonoTime"],
|
ignore=["logMonoTime"],
|
||||||
should_recv_callback=MessageBasedRcvCallback("cameraOdometry"),
|
should_recv_callback=MessageBasedRcvCallback("cameraOdometry"),
|
||||||
tolerance=NUMPY_TOLERANCE,
|
tolerance=NUMPY_TOLERANCE,
|
||||||
processing_time=0.01,
|
|
||||||
),
|
),
|
||||||
ProcessConfig(
|
ProcessConfig(
|
||||||
proc_name="paramsd",
|
proc_name="paramsd",
|
||||||
@@ -720,7 +716,7 @@ def _replay_multi_process(
|
|||||||
|
|
||||||
# flush last set of messages from each process
|
# flush last set of messages from each process
|
||||||
for container in containers:
|
for container in containers:
|
||||||
last_time = container.last_input_log_mono_time if container.last_input_log_mono_time > 0 else int(time.monotonic() * 1e9)
|
last_time = log_msgs[-1].logMonoTime if len(log_msgs) > 0 else int(time.monotonic() * 1e9)
|
||||||
log_msgs.extend(container.get_output_msgs(last_time))
|
log_msgs.extend(container.get_output_msgs(last_time))
|
||||||
finally:
|
finally:
|
||||||
for container in containers:
|
for container in containers:
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import argparse
|
|||||||
import concurrent.futures
|
import concurrent.futures
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -12,7 +11,6 @@ from opendbc.car.car_helpers import interface_names
|
|||||||
from openpilot.common.git import get_commit
|
from openpilot.common.git import get_commit
|
||||||
from openpilot.tools.lib.openpilotci import get_url
|
from openpilot.tools.lib.openpilotci import get_url
|
||||||
from openpilot.selfdrive.test.process_replay.compare_logs import compare_logs, format_diff
|
from openpilot.selfdrive.test.process_replay.compare_logs import compare_logs, format_diff
|
||||||
from openpilot.selfdrive.test.process_replay.diff_report import diff_process, diff_report
|
|
||||||
from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, PROC_REPLAY_DIR, FAKEDATA, replay_process, \
|
from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, PROC_REPLAY_DIR, FAKEDATA, replay_process, \
|
||||||
check_most_messages_valid
|
check_most_messages_valid
|
||||||
from openpilot.tools.lib.filereader import FileReader
|
from openpilot.tools.lib.filereader import FileReader
|
||||||
@@ -74,16 +72,11 @@ EXCLUDED_PROCS = {"modeld", "dmonitoringmodeld"}
|
|||||||
|
|
||||||
def run_test_process(data):
|
def run_test_process(data):
|
||||||
segment, cfg, args, cur_log_fn, ref_log_path, lr_dat = data
|
segment, cfg, args, cur_log_fn, ref_log_path, lr_dat = data
|
||||||
ref_log_msgs = list(LogReader(ref_log_path))
|
|
||||||
lr = LogReader.from_bytes(lr_dat)
|
lr = LogReader.from_bytes(lr_dat)
|
||||||
res, log_msgs = test_process(cfg, lr, segment, ref_log_msgs, cur_log_fn, args.ignore_fields, args.ignore_msgs)
|
res, log_msgs = test_process(cfg, lr, segment, ref_log_path, cur_log_fn, args.ignore_fields, args.ignore_msgs)
|
||||||
# save logs so we can update refs
|
# save logs so we can update refs
|
||||||
save_log(cur_log_fn, log_msgs)
|
save_log(cur_log_fn, log_msgs)
|
||||||
try:
|
return (segment, cfg.proc_name, res)
|
||||||
diff_data = diff_process(cfg, ref_log_msgs, log_msgs)
|
|
||||||
except Exception:
|
|
||||||
diff_data = traceback.format_exc()
|
|
||||||
return (segment, cfg.proc_name, res, diff_data)
|
|
||||||
|
|
||||||
|
|
||||||
def get_log_data(segment):
|
def get_log_data(segment):
|
||||||
@@ -92,12 +85,14 @@ def get_log_data(segment):
|
|||||||
return (segment, f.read())
|
return (segment, f.read())
|
||||||
|
|
||||||
|
|
||||||
def test_process(cfg, lr, segment, ref_log_msgs, new_log_path, ignore_fields=None, ignore_msgs=None):
|
def test_process(cfg, lr, segment, ref_log_path, new_log_path, ignore_fields=None, ignore_msgs=None):
|
||||||
if ignore_fields is None:
|
if ignore_fields is None:
|
||||||
ignore_fields = []
|
ignore_fields = []
|
||||||
if ignore_msgs is None:
|
if ignore_msgs is None:
|
||||||
ignore_msgs = []
|
ignore_msgs = []
|
||||||
|
|
||||||
|
ref_log_msgs = list(LogReader(ref_log_path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
log_msgs = replay_process(cfg, lr, disable_progress=True)
|
log_msgs = replay_process(cfg, lr, disable_progress=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -206,11 +201,9 @@ if __name__ == "__main__":
|
|||||||
log_paths[segment][cfg.proc_name]['new'] = cur_log_fn
|
log_paths[segment][cfg.proc_name]['new'] = cur_log_fn
|
||||||
|
|
||||||
results: Any = defaultdict(dict)
|
results: Any = defaultdict(dict)
|
||||||
diffs: list = []
|
|
||||||
p2 = pool.map(run_test_process, pool_args)
|
p2 = pool.map(run_test_process, pool_args)
|
||||||
for (segment, proc, result, diff_data) in tqdm(p2, desc="Running Tests", total=len(pool_args)):
|
for (segment, proc, result) in tqdm(p2, desc="Running Tests", total=len(pool_args)):
|
||||||
results[segment][proc] = result
|
results[segment][proc] = result
|
||||||
diffs.append((segment, proc, diff_data))
|
|
||||||
|
|
||||||
diff_short, diff_long, failed = format_diff(results, log_paths, ref_commit)
|
diff_short, diff_long, failed = format_diff(results, log_paths, ref_commit)
|
||||||
if not args.update_refs:
|
if not args.update_refs:
|
||||||
@@ -218,11 +211,6 @@ if __name__ == "__main__":
|
|||||||
f.write(diff_long)
|
f.write(diff_long)
|
||||||
print(diff_short)
|
print(diff_short)
|
||||||
|
|
||||||
try:
|
|
||||||
diff_report(diffs, segments)
|
|
||||||
except Exception:
|
|
||||||
print(f"failed to generate diff report:\n{traceback.format_exc()}")
|
|
||||||
|
|
||||||
if failed:
|
if failed:
|
||||||
print("TEST FAILED")
|
print("TEST FAILED")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Font font_display;
|
|||||||
const bool tici_device = Hardware::get_device_type() == cereal::InitData::DeviceType::TICI ||
|
const bool tici_device = Hardware::get_device_type() == cereal::InitData::DeviceType::TICI ||
|
||||||
Hardware::get_device_type() == cereal::InitData::DeviceType::TIZI;
|
Hardware::get_device_type() == cereal::InitData::DeviceType::TIZI;
|
||||||
|
|
||||||
std::vector<std::string> tici_prebuilt_branches = {"release3", "release-tici", "release3-staging", "nightly", "nightly-dev"};
|
std::vector<std::string> tici_prebuilt_branches = {"release3", "release-tizi", "release3-staging", "nightly", "nightly-dev"};
|
||||||
std::string migrated_branch;
|
std::string migrated_branch;
|
||||||
|
|
||||||
void branchMigration() {
|
void branchMigration() {
|
||||||
@@ -144,7 +144,6 @@ int cachedFetch(const std::string &cache) {
|
|||||||
LOGD("Fetching with cache: %s", cache.c_str());
|
LOGD("Fetching with cache: %s", cache.c_str());
|
||||||
|
|
||||||
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
|
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
|
||||||
run(util::string_format("cd %s && git remote set-url origin %s", TMP_INSTALL_PATH, GIT_URL.c_str()).c_str());
|
|
||||||
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str());
|
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str());
|
||||||
|
|
||||||
renderProgress(10);
|
renderProgress(10);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class DeveloperLayout(Widget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._params = Params()
|
self._params = Params()
|
||||||
self._is_release = False # self._params.get_bool("IsReleaseBranch")
|
self._is_release = self._params.get_bool("IsReleaseBranch")
|
||||||
|
|
||||||
# Build items and keep references for callbacks/state updates
|
# Build items and keep references for callbacks/state updates
|
||||||
self._adb_toggle = toggle_item(
|
self._adb_toggle = toggle_item(
|
||||||
@@ -71,13 +71,6 @@ class DeveloperLayout(Widget):
|
|||||||
callback=self._on_long_maneuver_mode,
|
callback=self._on_long_maneuver_mode,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._lat_maneuver_toggle = toggle_item(
|
|
||||||
lambda: tr("Lateral Maneuver Mode"),
|
|
||||||
description="",
|
|
||||||
initial_state=self._params.get_bool("LateralManeuverMode"),
|
|
||||||
callback=self._on_lat_maneuver_mode,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._alpha_long_toggle = toggle_item(
|
self._alpha_long_toggle = toggle_item(
|
||||||
lambda: tr("sunnypilot Longitudinal Control (Alpha)"),
|
lambda: tr("sunnypilot Longitudinal Control (Alpha)"),
|
||||||
description=lambda: tr(DESCRIPTIONS["alpha_longitudinal"]),
|
description=lambda: tr(DESCRIPTIONS["alpha_longitudinal"]),
|
||||||
@@ -100,7 +93,6 @@ class DeveloperLayout(Widget):
|
|||||||
self._ssh_keys,
|
self._ssh_keys,
|
||||||
self._joystick_toggle,
|
self._joystick_toggle,
|
||||||
self._long_maneuver_toggle,
|
self._long_maneuver_toggle,
|
||||||
self._lat_maneuver_toggle,
|
|
||||||
self._alpha_long_toggle,
|
self._alpha_long_toggle,
|
||||||
self._ui_debug_toggle,
|
self._ui_debug_toggle,
|
||||||
], line_separator=True, spacing=0)
|
], line_separator=True, spacing=0)
|
||||||
@@ -121,7 +113,7 @@ class DeveloperLayout(Widget):
|
|||||||
|
|
||||||
# Hide non-release toggles on release builds
|
# Hide non-release toggles on release builds
|
||||||
# TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault
|
# TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault
|
||||||
for item in (self._joystick_toggle, self._long_maneuver_toggle, self._lat_maneuver_toggle, self._alpha_long_toggle):
|
for item in (self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle):
|
||||||
item.set_visible(not self._is_release)
|
item.set_visible(not self._is_release)
|
||||||
|
|
||||||
# CP gating
|
# CP gating
|
||||||
@@ -138,12 +130,8 @@ class DeveloperLayout(Widget):
|
|||||||
if not long_man_enabled:
|
if not long_man_enabled:
|
||||||
self._long_maneuver_toggle.action_item.set_state(False)
|
self._long_maneuver_toggle.action_item.set_state(False)
|
||||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||||
|
|
||||||
lat_man_enabled = ui_state.is_offroad()
|
|
||||||
self._lat_maneuver_toggle.action_item.set_enabled(lat_man_enabled)
|
|
||||||
else:
|
else:
|
||||||
self._long_maneuver_toggle.action_item.set_enabled(False)
|
self._long_maneuver_toggle.action_item.set_enabled(False)
|
||||||
self._lat_maneuver_toggle.action_item.set_enabled(False)
|
|
||||||
self._alpha_long_toggle.set_visible(False)
|
self._alpha_long_toggle.set_visible(False)
|
||||||
|
|
||||||
# TODO: make a param control list item so we don't need to manage internal state as much here
|
# TODO: make a param control list item so we don't need to manage internal state as much here
|
||||||
@@ -153,7 +141,6 @@ class DeveloperLayout(Widget):
|
|||||||
("SshEnabled", self._ssh_toggle),
|
("SshEnabled", self._ssh_toggle),
|
||||||
("JoystickDebugMode", self._joystick_toggle),
|
("JoystickDebugMode", self._joystick_toggle),
|
||||||
("LongitudinalManeuverMode", self._long_maneuver_toggle),
|
("LongitudinalManeuverMode", self._long_maneuver_toggle),
|
||||||
("LateralManeuverMode", self._lat_maneuver_toggle),
|
|
||||||
("AlphaLongitudinalEnabled", self._alpha_long_toggle),
|
("AlphaLongitudinalEnabled", self._alpha_long_toggle),
|
||||||
("ShowDebugInfo", self._ui_debug_toggle),
|
("ShowDebugInfo", self._ui_debug_toggle),
|
||||||
):
|
):
|
||||||
@@ -175,23 +162,11 @@ class DeveloperLayout(Widget):
|
|||||||
self._params.put_bool("JoystickDebugMode", state)
|
self._params.put_bool("JoystickDebugMode", state)
|
||||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||||
self._long_maneuver_toggle.action_item.set_state(False)
|
self._long_maneuver_toggle.action_item.set_state(False)
|
||||||
self._params.put_bool("LateralManeuverMode", False)
|
|
||||||
self._lat_maneuver_toggle.action_item.set_state(False)
|
|
||||||
|
|
||||||
def _on_long_maneuver_mode(self, state: bool):
|
def _on_long_maneuver_mode(self, state: bool):
|
||||||
self._params.put_bool("LongitudinalManeuverMode", state)
|
self._params.put_bool("LongitudinalManeuverMode", state)
|
||||||
self._params.put_bool("JoystickDebugMode", False)
|
self._params.put_bool("JoystickDebugMode", False)
|
||||||
self._joystick_toggle.action_item.set_state(False)
|
self._joystick_toggle.action_item.set_state(False)
|
||||||
self._params.put_bool("LateralManeuverMode", False)
|
|
||||||
self._lat_maneuver_toggle.action_item.set_state(False)
|
|
||||||
|
|
||||||
def _on_lat_maneuver_mode(self, state: bool):
|
|
||||||
self._params.put_bool("LateralManeuverMode", state)
|
|
||||||
self._params.put_bool("ExperimentalMode", False)
|
|
||||||
self._params.put_bool("JoystickDebugMode", False)
|
|
||||||
self._joystick_toggle.action_item.set_state(False)
|
|
||||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
|
||||||
self._long_maneuver_toggle.action_item.set_state(False)
|
|
||||||
|
|
||||||
def _on_alpha_long_enabled(self, state: bool):
|
def _on_alpha_long_enabled(self, state: bool):
|
||||||
if state:
|
if state:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class TogglesLayout(Widget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._params = Params()
|
self._params = Params()
|
||||||
self._is_release = False # self._params.get_bool("IsReleaseBranch")
|
self._is_release = self._params.get_bool("IsReleaseBranch")
|
||||||
|
|
||||||
# param, title, desc, icon, needs_restart
|
# param, title, desc, icon, needs_restart
|
||||||
self._toggle_defs = {
|
self._toggle_defs = {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user