Merge branch 'master' into dev-priv/master
@@ -21,6 +21,7 @@ jobs:
|
||||
IMAGE_NAME: openpilot-prebuilt
|
||||
steps:
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: lewagon/wait-on-check-action@e2558238c09778af25867eb5de5a3ce4bbae3dcd
|
||||
with:
|
||||
ref: master
|
||||
@@ -38,3 +39,5 @@ jobs:
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
docker push $DOCKER_REGISTRY/$IMAGE_NAME:latest
|
||||
docker tag $DOCKER_REGISTRY/$IMAGE_NAME:latest $DOCKER_REGISTRY/$IMAGE_NAME:$GITHUB_SHA
|
||||
docker push $DOCKER_REGISTRY/$IMAGE_NAME:$GITHUB_SHA
|
||||
|
||||
@@ -7,10 +7,20 @@ on:
|
||||
jobs:
|
||||
build_masterci:
|
||||
name: build master-ci
|
||||
env:
|
||||
TARGET_DIR: /tmp/openpilot
|
||||
ImageOS: ubuntu20
|
||||
container:
|
||||
image: ghcr.io/commaai/openpilot-base:latest
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- name: Install wait-on-check-action dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libyaml-dev
|
||||
- name: Wait for green check mark
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
uses: lewagon/wait-on-check-action@e2558238c09778af25867eb5de5a3ce4bbae3dcd
|
||||
with:
|
||||
ref: master
|
||||
@@ -22,7 +32,19 @@ jobs:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Pull LFS
|
||||
run: git lfs pull
|
||||
run: |
|
||||
git config --global --add safe.directory '*'
|
||||
git lfs pull
|
||||
- name: Build master-ci
|
||||
run: |
|
||||
release/build_devel.sh
|
||||
- name: Run tests
|
||||
run: |
|
||||
export PYTHONPATH=$TARGET_DIR
|
||||
cd $TARGET_DIR
|
||||
scons -j$(nproc)
|
||||
selfdrive/car/tests/test_car_interfaces.py
|
||||
- name: Push master-ci
|
||||
run: |
|
||||
unset TARGET_DIR
|
||||
BRANCH=master-ci release/build_devel.sh
|
||||
|
||||
@@ -23,7 +23,7 @@ env:
|
||||
RUN: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/sh -c
|
||||
|
||||
BUILD_CL: |
|
||||
DOCKER_BUILDKIT=1 docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
|
||||
DOCKER_BUILDKIT=1 docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest -t $CL_BASE_IMAGE:latest -f Dockerfile.openpilot_base_cl .
|
||||
RUN_CL: docker run --shm-size 1G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v /tmp/scons_cache:/tmp/scons_cache -v /tmp/comma_download_cache:/tmp/comma_download_cache -v /tmp/openpilot_cache:/tmp/openpilot_cache $CL_BASE_IMAGE /bin/sh -c
|
||||
|
||||
UNIT_TEST: coverage run --append -m unittest discover
|
||||
@@ -81,67 +81,97 @@ jobs:
|
||||
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
|
||||
scons -j$(nproc) --cache-populate"
|
||||
|
||||
#build_mac:
|
||||
# name: build macos
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# with:
|
||||
# submodules: true
|
||||
# - name: Determine pre-existing Homebrew packages
|
||||
# if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
# run: |
|
||||
# echo 'EXISTING_CELLAR<<EOF' >> $GITHUB_ENV
|
||||
# ls -1 /usr/local/Cellar >> $GITHUB_ENV
|
||||
# echo 'EOF' >> $GITHUB_ENV
|
||||
# - name: Cache dependencies
|
||||
# id: dependency-cache
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.pyenv
|
||||
# ~/.local/share/virtualenvs/
|
||||
# /usr/local/Cellar
|
||||
# ~/github_brew_cache_entries.txt
|
||||
# /tmp/scons_cache
|
||||
# key: macos-${{ hashFiles('tools/mac_setup.sh', 'update_requirements.sh', 'poetry.lock') }}
|
||||
# restore-keys: macos-
|
||||
# - name: Brew link restored dependencies
|
||||
# run: |
|
||||
# if [ -f ~/github_brew_cache_entries.txt ]; then
|
||||
# while read pkg; do
|
||||
# brew link --force "$pkg" # `--force` for keg-only packages
|
||||
# done < ~/github_brew_cache_entries.txt
|
||||
# else
|
||||
# echo "Cache entries not found"
|
||||
# fi
|
||||
# - name: Install dependencies
|
||||
# run: ./tools/mac_setup.sh
|
||||
# - name: Build openpilot
|
||||
# run: |
|
||||
# source tools/openpilot_env.sh
|
||||
# poetry run selfdrive/manager/build.py
|
||||
#
|
||||
# # cleanup scons cache
|
||||
# rm -rf /tmp/scons_cache/
|
||||
# poetry run scons -j$(nproc) --cache-populate
|
||||
# - name: Remove pre-existing Homebrew packages for caching
|
||||
# if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
# run: |
|
||||
# cd /usr/local/Cellar
|
||||
# new_cellar=$(ls -1)
|
||||
# comm -12 <(echo "$EXISTING_CELLAR") <(echo "$new_cellar") | while read pkg; do
|
||||
# if [[ $pkg != "zstd" ]]; then # caching step needs zstd
|
||||
# rm -rf "$pkg"
|
||||
# fi
|
||||
# done
|
||||
# comm -13 <(echo "$EXISTING_CELLAR") <(echo "$new_cellar") | tee ~/github_brew_cache_entries.txt
|
||||
build_mac:
|
||||
name: build macos
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- name: Determine pre-existing Homebrew packages
|
||||
if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
echo 'EXISTING_CELLAR<<EOF' >> $GITHUB_ENV
|
||||
brew list --formula -1 >> $GITHUB_ENV
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
- name: Restore scons cache
|
||||
id: scons-restore-cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: /tmp/scons_cache
|
||||
key: macos_scons-${{ github.sha }}
|
||||
restore-keys: macos_scons-
|
||||
- name: Cache dependencies
|
||||
id: dependency-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/github_brew_cache_entries.txt
|
||||
~/.pyenv
|
||||
~/Library/Caches/pypoetry
|
||||
/usr/local/Cellar
|
||||
/usr/local/opt
|
||||
/usr/local/Caskroom/gcc-arm-*
|
||||
/opt/homebrew/Cellar
|
||||
/opt/homebrew/opt
|
||||
/opt/homebrew/Caskroom/gcc-arm-*
|
||||
/Applications/ArmGNUToolchain/*/*/*
|
||||
key: macos_deps-${{ hashFiles('tools/mac_setup.sh', 'update_requirements.sh', 'poetry.lock') }}
|
||||
restore-keys: macos_deps-
|
||||
- name: Brew link restored dependencies
|
||||
run: |
|
||||
if [ -f ~/github_brew_cache_entries.txt ]; then
|
||||
brew link --force --overwrite $(cat ~/github_brew_cache_entries.txt) # `--force` for keg-only packages
|
||||
if [ -d /Applications/ArmGNUToolchain ]; then # link gcc-arm-embedded manually
|
||||
GCC_TOOLCHAIN="$(echo /Applications/ArmGNUToolchain/**/**/bin)"
|
||||
echo "$GCC_TOOLCHAIN" >> $GITHUB_PATH
|
||||
fi
|
||||
else
|
||||
echo "Cache entries not found"
|
||||
fi
|
||||
- name: Install dependencies
|
||||
if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
run: ./tools/mac_setup.sh
|
||||
- name: Build openpilot
|
||||
run: |
|
||||
source tools/openpilot_env.sh
|
||||
poetry run scons -j$(nproc)
|
||||
- name: Run tests
|
||||
run: |
|
||||
source tools/openpilot_env.sh
|
||||
export PYTHONPATH=$PWD
|
||||
poetry run tools/plotjuggler/test_plotjuggler.py
|
||||
- name: Pre Cache - Cleanup scons cache
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
source tools/openpilot_env.sh
|
||||
rm -rf /tmp/scons_cache/*
|
||||
poetry run scons -j$(nproc) --cache-populate
|
||||
- name: Save scons cache
|
||||
id: scons-save-cache
|
||||
uses: actions/cache/save@v3
|
||||
if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
path: /tmp/scons_cache
|
||||
key: macos_scons-${{ github.sha }}
|
||||
- name: Pre Cache - Remove pre-existing Homebrew packages
|
||||
if: steps.dependency-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
new_cellar=$(brew list --formula -1)
|
||||
comm -12 <(echo "$EXISTING_CELLAR") <(echo "$new_cellar") | while read pkg; do
|
||||
if [[ $pkg != "zstd" ]]; then # caching step needs zstd
|
||||
rm -rf "$(brew --cellar)/$pkg"
|
||||
fi
|
||||
done
|
||||
comm -13 <(echo "$EXISTING_CELLAR") <(echo "$new_cellar") | tee ~/github_brew_cache_entries.txt
|
||||
# .fseventsd directory causes permission errors in dep caching step
|
||||
# its used by the system to observe changes within the directory - toolchain works without it, just remove it
|
||||
sudo rm -rf /Applications/ArmGNUToolchain/*/*/.fseventsd
|
||||
|
||||
docker_push:
|
||||
name: docker push
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
|
||||
needs: static_analysis # hack to ensure slow tests run first since this and static_analysis are fast
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -152,12 +182,16 @@ jobs:
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
docker push $DOCKER_REGISTRY/$BASE_IMAGE:latest
|
||||
docker tag $DOCKER_REGISTRY/$BASE_IMAGE:latest $DOCKER_REGISTRY/$BASE_IMAGE:$GITHUB_SHA
|
||||
docker push $DOCKER_REGISTRY/$BASE_IMAGE:$GITHUB_SHA
|
||||
- name: Build CL Docker image
|
||||
run: eval "$BUILD_CL"
|
||||
- name: Push to container registry
|
||||
run: |
|
||||
$DOCKER_LOGIN
|
||||
docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest
|
||||
docker tag $DOCKER_REGISTRY/$CL_BASE_IMAGE:latest $DOCKER_REGISTRY/$CL_BASE_IMAGE:$GITHUB_SHA
|
||||
docker push $DOCKER_REGISTRY/$CL_BASE_IMAGE:$GITHUB_SHA
|
||||
|
||||
static_analysis:
|
||||
name: static analysis
|
||||
@@ -229,9 +263,10 @@ jobs:
|
||||
./tools/replay/tests/test_replay && \
|
||||
./tools/cabana/tests/test_cabana && \
|
||||
./system/camerad/test/ae_gray_test && \
|
||||
./selfdrive/test/process_replay/test_fuzzy.py && \
|
||||
coverage xml"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
process_replay:
|
||||
name: process replay
|
||||
@@ -269,7 +304,7 @@ jobs:
|
||||
run: |
|
||||
${{ env.RUN }} "CI=1 AZURE_TOKEN='$AZURE_TOKEN' python selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
test_modeld:
|
||||
name: model tests
|
||||
@@ -279,6 +314,8 @@ jobs:
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/workflows/setup
|
||||
- name: Build base Docker image
|
||||
run: eval "$BUILD"
|
||||
- name: Build Docker image
|
||||
# Sim docker is needed to get the OpenCL drivers
|
||||
run: eval "$BUILD_CL"
|
||||
@@ -294,7 +331,7 @@ jobs:
|
||||
${{ env.RUN_CL }} "$UNIT_TEST selfdrive/modeld && \
|
||||
coverage xml"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
test_longitudinal:
|
||||
name: longitudinal
|
||||
@@ -314,7 +351,7 @@ jobs:
|
||||
coverage run ./test_longitudinal.py && \
|
||||
coverage xml"
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
@@ -351,7 +388,7 @@ jobs:
|
||||
NUM_JOBS: 5
|
||||
JOB_ID: ${{ matrix.job }}
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
car_docs_diff:
|
||||
name: PR comments
|
||||
|
||||
@@ -45,7 +45,9 @@ selfdrive/logcatd/logcatd
|
||||
selfdrive/mapd/default_speeds_by_region.json
|
||||
system/proclogd/proclogd
|
||||
selfdrive/ui/_ui
|
||||
selfdrive/ui/translations/alerts_generated.h
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/car/tests/cars_dump
|
||||
system/camerad/camerad
|
||||
system/camerad/test/ae_gray_test
|
||||
selfdrive/modeld/_modeld
|
||||
|
||||
@@ -5,5 +5,5 @@ repos:
|
||||
- id: poetry-check
|
||||
- id: poetry-lock
|
||||
name: validate poetry lock
|
||||
args:
|
||||
args:
|
||||
- --check
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.8.10
|
||||
3.11.4
|
||||
|
||||
@@ -13,7 +13,7 @@ ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
ENV POETRY_VIRTUALENVS_CREATE=false
|
||||
ENV PYENV_VERSION=3.8.10
|
||||
ENV PYENV_VERSION=3.11.4
|
||||
ENV PYENV_ROOT="/root/.pyenv"
|
||||
ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <
|
||||
set -e
|
||||
|
||||
export CI=1
|
||||
export LOGPRINT=debug
|
||||
export TEST_DIR=${env.TEST_DIR}
|
||||
export SOURCE_DIR=${env.SOURCE_DIR}
|
||||
export GIT_BRANCH=${env.GIT_BRANCH}
|
||||
@@ -18,6 +19,20 @@ export GIT_SSH_COMMAND="ssh -i /data/gitkey"
|
||||
source ~/.bash_profile
|
||||
if [ -f /TICI ]; then
|
||||
source /etc/profile
|
||||
|
||||
if ! systemctl is-active --quiet systemd-resolved; then
|
||||
echo "restarting resolved"
|
||||
sudo systemctl start systemd-resolved
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# restart aux USB
|
||||
if [ -e /sys/bus/usb/drivers/hub/3-0:1.0 ]; then
|
||||
echo "restarting aux usb"
|
||||
echo "3-0:1.0" | sudo tee /sys/bus/usb/drivers/hub/unbind
|
||||
sleep 0.5
|
||||
echo "3-0:1.0" | sudo tee /sys/bus/usb/drivers/hub/bind
|
||||
fi
|
||||
fi
|
||||
if [ -f /data/openpilot/launch_env.sh ]; then
|
||||
source /data/openpilot/launch_env.sh
|
||||
@@ -128,17 +143,41 @@ pipeline {
|
||||
}
|
||||
*/
|
||||
|
||||
stage('scons build test') {
|
||||
agent {
|
||||
dockerfile {
|
||||
filename 'Dockerfile.openpilot_base'
|
||||
args '--user=root'
|
||||
}
|
||||
}
|
||||
steps {
|
||||
sh "git config --global --add safe.directory '*'"
|
||||
sh "git submodule update --init --depth=1 --recursive"
|
||||
sh "scons --clean && scons --no-cache -j42"
|
||||
sh "scons --clean && scons --no-cache --random -j42"
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
sh "rm -rf ${WORKSPACE}/* || true"
|
||||
sh "rm -rf .* || true"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('tizi-tests') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tizi", [
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test boardd loopback", "SINGLE_PANDA=1 python selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
["test pandad", "python selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test sensord", "cd system/sensord/tests && python -m unittest test_sensord.py"],
|
||||
["test camerad", "python system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python system/camerad/test/test_exposure.py"],
|
||||
["test amp", "python system/hardware/tici/tests/test_amplifier.py"],
|
||||
["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"],
|
||||
["test hw", "pytest system/hardware/tici/tests/test_hardware.py"],
|
||||
["test rawgpsd", "pytest system/sensord/rawgps/test_rawgps.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -155,7 +194,6 @@ pipeline {
|
||||
["check dirty", "release/check-dirty.sh"],
|
||||
["onroad tests", "cd selfdrive/test/ && ./test_onroad.py"],
|
||||
["time to onroad", "cd selfdrive/test/ && pytest test_time_to_onroad.py"],
|
||||
["test car interfaces", "cd selfdrive/car/tests/ && ./test_car_interfaces.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -165,7 +203,7 @@ pipeline {
|
||||
steps {
|
||||
phone_steps("tici-loopback", [
|
||||
["build openpilot", "cd selfdrive/manager && ./build.py"],
|
||||
["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -175,12 +213,13 @@ pipeline {
|
||||
steps {
|
||||
phone_steps("tici-common", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test pandad", "python selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test power draw", "python system/hardware/tici/tests/test_power_draw.py"],
|
||||
["test loggerd", "python system/loggerd/tests/test_loggerd.py"],
|
||||
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib python system/loggerd/tests/test_encoder.py"],
|
||||
["test pigeond", "python system/sensord/tests/test_pigeond.py"],
|
||||
["test manager", "python selfdrive/manager/test/test_manager.py"],
|
||||
["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"],
|
||||
["test power draw", "pytest system/hardware/tici/tests/test_power_draw.py"],
|
||||
["test loggerd", "pytest system/loggerd/tests/test_loggerd.py"],
|
||||
["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"],
|
||||
["test pigeond", "pytest system/sensord/tests/test_pigeond.py"],
|
||||
["test manager", "pytest selfdrive/manager/test/test_manager.py"],
|
||||
["test nav", "pytest selfdrive/navd/tests/"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -190,13 +229,13 @@ pipeline {
|
||||
steps {
|
||||
phone_steps("tici-ar0231", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "python system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python system/camerad/test/test_exposure.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
phone_steps("tici-ox03c10", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test camerad", "python system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "python system/camerad/test/test_exposure.py"],
|
||||
["test camerad", "pytest system/camerad/test/test_camerad.py"],
|
||||
["test exposure", "pytest system/camerad/test/test_exposure.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -206,11 +245,11 @@ pipeline {
|
||||
steps {
|
||||
phone_steps("tici-lsmc", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && python -m unittest test_sensord.py"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
])
|
||||
phone_steps("tici-bmx-lsm", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["test sensord", "cd system/sensord/tests && python -m unittest test_sensord.py"],
|
||||
["test sensord", "cd system/sensord/tests && pytest test_sensord.py"],
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -218,7 +257,7 @@ pipeline {
|
||||
stage('replay') {
|
||||
agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } }
|
||||
steps {
|
||||
phone_steps("tici-common", [
|
||||
phone_steps("tici-replay", [
|
||||
["build", "cd selfdrive/manager && ./build.py"],
|
||||
["model replay", "cd selfdrive/test/process_replay && ./model_replay.py"],
|
||||
])
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
Version 0.9.3 (2023-06-20)
|
||||
Version 0.9.4 (2023-07-27)
|
||||
========================
|
||||
* Navigate on openpilot in Experimental mode
|
||||
* When navigation has a destination, openpilot will input the map information into the model, which provides useful context to help the model understand the scene
|
||||
* When navigating on openpilot, openpilot will keep left or right appropriately at forks and exits
|
||||
* When navigating on openpilot, lane change behavior is unchanged and still activated by the driver
|
||||
* When navigate on openpilot is active, the path on the map is green
|
||||
* UI updates
|
||||
* Navigation settings moved to home screen and map
|
||||
* Border color always shows engagement status. Blue means disengaged, green means engaged, and grey means engaged with human overriding
|
||||
* Alerts are shown inside the border. Black means info, orange means warning, and red means critical alert
|
||||
* Bookmarked segments are preserved on the device's storage
|
||||
* Ford Focus 2018 support
|
||||
* Kia Carnival 2023 support thanks to sunnyhaibin!
|
||||
|
||||
Version 0.9.3 (2023-06-29)
|
||||
========================
|
||||
* New driving model
|
||||
* Improved height estimation and added height tracking in liveCalibration
|
||||
|
||||
@@ -67,17 +67,17 @@ AddOption('--no-test',
|
||||
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||
if platform.system() == "Darwin":
|
||||
arch = "Darwin"
|
||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||
|
||||
if arch == "aarch64" and AGNOS:
|
||||
arch = "larch64"
|
||||
|
||||
|
||||
lenv = {
|
||||
"PATH": os.environ['PATH'],
|
||||
"LD_LIBRARY_PATH": [Dir(f"#third_party/acados/{arch}/lib").abspath],
|
||||
"PYTHONPATH": Dir("#").abspath,
|
||||
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados/include/acados").abspath,
|
||||
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
|
||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||
}
|
||||
@@ -110,26 +110,21 @@ else:
|
||||
cflags = []
|
||||
cxxflags = []
|
||||
cpppath = []
|
||||
rpath += [
|
||||
Dir("#cereal").abspath,
|
||||
Dir("#common").abspath
|
||||
]
|
||||
|
||||
# MacOS
|
||||
if arch == "Darwin":
|
||||
if real_arch == "x86_64":
|
||||
lenv["TERA_PATH"] = Dir("#").abspath + f"/third_party/acados/Darwin_x86_64/t_renderer"
|
||||
|
||||
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||
yuv_dir = "mac" if real_arch != "arm64" else "mac_arm64"
|
||||
libpath = [
|
||||
f"#third_party/libyuv/{yuv_dir}/lib",
|
||||
f"#third_party/acados/{arch}/lib",
|
||||
f"{brew_prefix}/lib",
|
||||
f"{brew_prefix}/Library",
|
||||
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||
f"{brew_prefix}/Cellar",
|
||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||
]
|
||||
if real_arch == "x86_64":
|
||||
libpath.append(f"#third_party/acados/Darwin_x86_64/lib")
|
||||
else:
|
||||
libpath.append(f"#third_party/acados/{arch}/lib")
|
||||
|
||||
cflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
|
||||
@@ -137,6 +132,7 @@ else:
|
||||
f"{brew_prefix}/include",
|
||||
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||
]
|
||||
lenv["DYLD_LIBRARY_PATH"] = lenv["LD_LIBRARY_PATH"]
|
||||
# Linux 86_64
|
||||
else:
|
||||
libpath = [
|
||||
@@ -149,12 +145,9 @@ else:
|
||||
"/usr/lib",
|
||||
"/usr/local/lib",
|
||||
]
|
||||
|
||||
rpath += [
|
||||
Dir("#third_party/snpe/x86_64-linux-clang").abspath,
|
||||
Dir("#cereal").abspath,
|
||||
Dir("#common").abspath
|
||||
]
|
||||
rpath += [
|
||||
Dir("#third_party/snpe/x86_64-linux-clang").abspath,
|
||||
]
|
||||
|
||||
if GetOption('asan'):
|
||||
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
|
||||
@@ -231,7 +224,9 @@ env = Environment(
|
||||
)
|
||||
|
||||
if arch == "Darwin":
|
||||
env['RPATHPREFIX'] = "-rpath "
|
||||
# RPATH is not supported on macOS, instead use the linker flags
|
||||
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
|
||||
env["LINKFLAGS"] += darwin_rpath_link_flags
|
||||
|
||||
if GetOption('compile_db'):
|
||||
env.CompilationDatabase('compile_commands.json')
|
||||
@@ -268,10 +263,11 @@ py_include = sysconfig.get_paths()['include']
|
||||
envCython = env.Clone()
|
||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||
envCython["CCFLAGS"].remove("-Werror")
|
||||
|
||||
envCython["LIBS"] = []
|
||||
if arch == "Darwin":
|
||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"]
|
||||
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
|
||||
elif arch == "aarch64":
|
||||
envCython["LINKFLAGS"] = ["-shared"]
|
||||
envCython["LIBS"] = [os.path.basename(py_include)]
|
||||
@@ -286,10 +282,7 @@ qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "Multimedia", "
|
||||
|
||||
qt_libs = []
|
||||
if arch == "Darwin":
|
||||
if real_arch == "arm64":
|
||||
qt_env['QTDIR'] = "/opt/homebrew/opt/qt@5"
|
||||
else:
|
||||
qt_env['QTDIR'] = "/usr/local/opt/qt@5"
|
||||
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
|
||||
qt_dirs = [
|
||||
os.path.join(qt_env['QTDIR'], "include"),
|
||||
]
|
||||
|
||||
@@ -53,7 +53,7 @@ int gpiochip_get_ro_value_fd(const char* consumer_label, int gpiochiop_id, int p
|
||||
std::string gpiochip_path = "/dev/gpiochip" + std::to_string(gpiochiop_id);
|
||||
int fd = open(gpiochip_path.c_str(), O_RDONLY);
|
||||
if (fd < 0) {
|
||||
LOGE("Error opening gpiochip0 fd")
|
||||
LOGE("Error opening gpiochip0 fd");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import glob
|
||||
from functools import lru_cache
|
||||
from typing import Optional, List
|
||||
|
||||
def gpio_init(pin: int, output: bool) -> None:
|
||||
@@ -25,12 +25,27 @@ def gpio_read(pin: int) -> Optional[bool]:
|
||||
|
||||
return val
|
||||
|
||||
def get_irq_for_action(action: str) -> List[int]:
|
||||
ret = []
|
||||
for fn in glob.glob('/sys/kernel/irq/*/actions'):
|
||||
with open(fn) as f:
|
||||
def gpio_export(pin: int) -> None:
|
||||
try:
|
||||
with open("/sys/class/gpio/export", 'w') as f:
|
||||
f.write(str(pin))
|
||||
except Exception:
|
||||
print(f"Failed to export gpio {pin}")
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def get_irq_action(irq: int) -> List[str]:
|
||||
try:
|
||||
with open(f"/sys/kernel/irq/{irq}/actions") as f:
|
||||
actions = f.read().strip().split(',')
|
||||
if action in actions:
|
||||
irq = int(fn.split('/')[-2])
|
||||
return actions
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
|
||||
def get_irqs_for_action(action: str) -> List[str]:
|
||||
ret = []
|
||||
with open("/proc/interrupts") as f:
|
||||
for l in f.readlines():
|
||||
irq = l.split(':')[0].strip()
|
||||
if irq.isdigit() and action in get_irq_action(irq):
|
||||
ret.append(irq)
|
||||
return ret
|
||||
|
||||
@@ -10,6 +10,9 @@ const int LON_MPC_N = 32;
|
||||
const float MIN_DRAW_DISTANCE = 10.0;
|
||||
const float MAX_DRAW_DISTANCE = 100.0;
|
||||
|
||||
const float RYG_GREEN = 0.01165;
|
||||
const float RYG_YELLOW = 0.06157;
|
||||
|
||||
template <typename T, size_t size>
|
||||
constexpr std::array<T, size> build_idxs(float max_val) {
|
||||
std::array<T, size> result{};
|
||||
@@ -24,23 +27,12 @@ constexpr auto T_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(10.0);
|
||||
constexpr auto X_IDXS = build_idxs<double, TRAJECTORY_SIZE>(192.0);
|
||||
constexpr auto X_IDXS_FLOAT = build_idxs<float, TRAJECTORY_SIZE>(192.0);
|
||||
|
||||
const mat3 fcam_intrinsic_matrix = (mat3){{2648.0, 0.0, 1928.0 / 2,
|
||||
const mat3 FCAM_INTRINSIC_MATRIX = (mat3){{2648.0, 0.0, 1928.0 / 2,
|
||||
0.0, 2648.0, 1208.0 / 2,
|
||||
0.0, 0.0, 1.0}};
|
||||
|
||||
// tici ecam focal probably wrong? magnification is not consistent across frame
|
||||
// Need to retrain model before this can be changed
|
||||
const mat3 ecam_intrinsic_matrix = (mat3){{567.0, 0.0, 1928.0 / 2,
|
||||
const mat3 ECAM_INTRINSIC_MATRIX = (mat3){{567.0, 0.0, 1928.0 / 2,
|
||||
0.0, 567.0, 1208.0 / 2,
|
||||
0.0, 0.0, 1.0}};
|
||||
|
||||
static inline mat3 get_model_yuv_transform() {
|
||||
float db_s = 1.0;
|
||||
const mat3 transform = (mat3){{
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
}};
|
||||
// Can this be removed since scale is 1?
|
||||
return transform_scale_buffer(transform, db_s);
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@ private:
|
||||
|
||||
std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG},
|
||||
{"ApiCache_Device", PERSISTENT},
|
||||
{"ApiCache_DriveStats", PERSISTENT},
|
||||
{"ApiCache_NavDestinations", PERSISTENT},
|
||||
{"AssistNowToken", PERSISTENT},
|
||||
{"AthenadPid", PERSISTENT},
|
||||
{"AthenadUploadQueue", PERSISTENT},
|
||||
@@ -102,16 +105,16 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DisablePowerDown", PERSISTENT},
|
||||
{"ExperimentalMode", PERSISTENT},
|
||||
{"ExperimentalModeConfirmed", PERSISTENT},
|
||||
{"LongitudinalPersonality", PERSISTENT},
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT},
|
||||
{"DisableUpdates", PERSISTENT},
|
||||
{"DisengageOnAccelerator", PERSISTENT},
|
||||
{"DmModelInitialized", CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"DongleId", PERSISTENT},
|
||||
{"DoReboot", CLEAR_ON_MANAGER_START},
|
||||
{"DoShutdown", CLEAR_ON_MANAGER_START},
|
||||
{"DoUninstall", CLEAR_ON_MANAGER_START},
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT},
|
||||
{"ExperimentalMode", PERSISTENT},
|
||||
{"ExperimentalModeConfirmed", PERSISTENT},
|
||||
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"ForcePowerDown", PERSISTENT},
|
||||
{"GitBranch", PERSISTENT},
|
||||
@@ -134,9 +137,9 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"IsOffroad", CLEAR_ON_MANAGER_START},
|
||||
{"IsOnroad", PERSISTENT},
|
||||
{"IsRhdDetected", PERSISTENT},
|
||||
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"IsTestedBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsReleaseBranch", CLEAR_ON_MANAGER_START},
|
||||
{"IsUpdateAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"JoystickDebugMode", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LaikadEphemerisV3", PERSISTENT | DONT_LOG},
|
||||
@@ -144,6 +147,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LastAthenaPingTime", CLEAR_ON_MANAGER_START},
|
||||
{"LastGPSPosition", PERSISTENT},
|
||||
{"LastManagerExitReason", CLEAR_ON_MANAGER_START},
|
||||
{"LastOffroadStatusPacket", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LastPowerDropDetected", CLEAR_ON_MANAGER_START},
|
||||
{"LastSystemShutdown", CLEAR_ON_MANAGER_START},
|
||||
{"LastUpdateException", CLEAR_ON_MANAGER_START},
|
||||
@@ -151,16 +155,30 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"LiveParameters", PERSISTENT},
|
||||
{"LiveTorqueCarParams", PERSISTENT},
|
||||
{"LiveTorqueParameters", PERSISTENT | DONT_LOG},
|
||||
{"LastOffroadStatusPacket", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"LongitudinalPersonality", PERSISTENT},
|
||||
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"NavSettingTime24h", PERSISTENT},
|
||||
{"NavSettingLeftSide", PERSISTENT},
|
||||
{"NavSettingTime24h", PERSISTENT},
|
||||
{"NavdRender", PERSISTENT},
|
||||
{"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"ObdMultiplexingEnabled", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_BadNvme", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_CarUnrecognized", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_ConnectivityNeeded", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_ConnectivityNeededPrompt", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_InvalidTime", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NeosUpdate", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NoFirmware", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_Recalibration", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_StorageMissing", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
||||
{"OpenpilotEnabledToggle", PERSISTENT},
|
||||
{"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"PandaLogState", PERSISTENT},
|
||||
{"PandaSignatures", CLEAR_ON_MANAGER_START},
|
||||
{"Passive", PERSISTENT},
|
||||
{"PrimeType", PERSISTENT},
|
||||
@@ -177,32 +195,17 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"UbloxAvailable", PERSISTENT},
|
||||
{"UpdateAvailable", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterAvailableBranches", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterState", CLEAR_ON_MANAGER_START},
|
||||
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
|
||||
{"Version", PERSISTENT},
|
||||
{"VisionRadarToggle", PERSISTENT},
|
||||
{"ApiCache_Device", PERSISTENT},
|
||||
{"ApiCache_DriveStats", PERSISTENT},
|
||||
{"ApiCache_NavDestinations", PERSISTENT},
|
||||
{"Offroad_BadNvme", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_CarUnrecognized", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_ConnectivityNeeded", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_ConnectivityNeededPrompt", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_InvalidTime", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_IsTakingSnapshot", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NeosUpdate", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_NoFirmware", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"Offroad_StorageMissing", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START},
|
||||
{"Offroad_Recalibration", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"WheeledBody", PERSISTENT},
|
||||
|
||||
{"AccMadsCombo", PERSISTENT},
|
||||
{"AmapKey1", PERSISTENT},
|
||||
|
||||
@@ -135,4 +135,3 @@ void cloudlog_te(int levelnum, const char* filename, int lineno, const char* fun
|
||||
cloudlog_t_common(levelnum, filename, lineno, func, frame_id, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ void cloudlog_te(int levelnum, const char* filename, int lineno, const char* fun
|
||||
|
||||
#define cloudlog(lvl, fmt, ...) cloudlog_e(lvl, __FILE__, __LINE__, \
|
||||
__func__, \
|
||||
fmt, ## __VA_ARGS__);
|
||||
|
||||
fmt, ## __VA_ARGS__)
|
||||
|
||||
#define cloudlog_t(lvl, ...) cloudlog_te(lvl, __FILE__, __LINE__, \
|
||||
__func__, \
|
||||
__VA_ARGS__);
|
||||
__VA_ARGS__)
|
||||
|
||||
|
||||
#define cloudlog_rl(burst, millis, lvl, fmt, ...) \
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import datetime
|
||||
|
||||
MIN_DATE = datetime.datetime(year=2023, month=6, day=1)
|
||||
|
||||
def system_time_valid():
|
||||
return datetime.datetime.now() > MIN_DATE
|
||||
@@ -1 +1 @@
|
||||
#define COMMA_VERSION "0.9.3.2"
|
||||
#define COMMA_VERSION "0.9.4"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
|
||||
# 264 Supported Cars
|
||||
# 255 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|
|
||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
@@ -17,14 +17,14 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=Q3 2019-23">Buy Here</a></sub></details>||
|
||||
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=RS3 2018">Buy Here</a></sub></details>||
|
||||
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Audi&model=S3 2015-17">Buy Here</a></sub></details>||
|
||||
|Buick|LaCrosse 2017-19[<sup>3</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade 2017[<sup>3</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">Buy Here</a></sub></details>||
|
||||
|Buick|LaCrosse 2017-19[<sup>4</sup>](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Buick&model=LaCrosse 2017-19">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade 2017[<sup>4</sup>](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade 2017">Buy Here</a></sub></details>||
|
||||
|Cadillac|Escalade ESV 2016[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Cadillac&model=Escalade ESV 2016">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>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=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>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Bolt EV 2022-23">Buy Here</a></sub></details>||
|
||||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=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>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Silverado 1500 2020-21">Buy Here</a></sub></details>||
|
||||
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Trailblazer 2021-22">Buy Here</a></sub></details>||
|
||||
|Chevrolet|Volt 2017-18[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Volt 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/QeMCN_4TFfQ" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Chevrolet|Volt 2017-18[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chevrolet&model=Volt 2017-18">Buy Here</a></sub></details>|<a href="https://youtu.be/QeMCN_4TFfQ" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2017-18">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2019-20">Buy Here</a></sub></details>||
|
||||
|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Chrysler&model=Pacifica 2021">Buy Here</a></sub></details>||
|
||||
@@ -34,30 +34,28 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Bronco Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Explorer 2020-22|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Explorer 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|
||||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|
||||
|Ford|Maverick 2022-23|Co-Pilot360 Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 angled mount (8 degrees)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ford&model=Maverick 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G70 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G70 2020">Buy Here</a></sub></details>||
|
||||
|Genesis|G70 Non-SCC 2021|No Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G70 Non-SCC 2021">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2017|All|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai J connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G80 2017">Buy Here</a></sub></details>||
|
||||
|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G80 2018-19">Buy Here</a></sub></details>||
|
||||
|Genesis|G90 2017-18|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=G90 2017-18">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Advanced Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Performance Trim) 2023[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Performance Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim) 2022-23[<sup>7</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV70 (2.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim) 2022-23[<sup>7</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV70 (3.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>7</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|GMC|Acadia 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=GMC&model=Acadia 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0ZN6DdsBUZo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|3 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=GMC&model=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 4-Cylinder 2016-17|Honda Sensing|openpilot|25 mph[<sup>5</sup>](#footnotes)|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord 4-Cylinder 2016-17">Buy Here</a></sub></details>[<sup>6</sup>](#footnotes)||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Advanced Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Performance Trim) 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV60 (Performance Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV70 (2.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim) 2022-23[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV70 (3.5T Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai M connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|GMC|Acadia 2018[<sup>4</sup>](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 OBD-II connector<br>- 1 comma three<br>- 2 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=GMC&model=Acadia 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=0ZN6DdsBUZo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 GM connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=GMC&model=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>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=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 Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Accord Hybrid 2018-22">Buy Here</a></sub></details>||
|
||||
|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=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>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=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 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|2 mph[<sup>5</sup>](#footnotes)|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=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|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic 2022">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-21|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2017-21">Buy Here</a></sub></details>||
|
||||
|Honda|Civic Hatchback 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Civic Hatchback 2022">Buy Here</a></sub></details>|<a href="https://youtu.be/ytiOT5lcp6Q" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Clarity 2018-22|Honda Sensing|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=Clarity 2018-22">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Nidec connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2015-16">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V 2017-22|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V 2017-22">Buy Here</a></sub></details>||
|
||||
|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[<sup>1</sup>](#footnotes)|0 mph|12 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Honda Bosch A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Honda&model=CR-V Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
@@ -76,12 +74,11 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra GT 2017-19">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>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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|Elantra Non-SCC 2022|No Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Elantra Non-SCC 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai J connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Genesis 2015-16">Buy Here</a></sub></details>||
|
||||
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=i30 2017-19">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>7</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>7</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai Q connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq 5 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq 5 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Electric 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Electric 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
@@ -90,42 +87,42 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Ioniq Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona 2020">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Electric 2018-21">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai O connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Electric 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai O connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Electric 2022">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>View</summary><sub>- 1 Hyundai I connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Kona Hybrid 2020">Buy Here</a></sub></details>|<a href="https://youtu.be/0dwpAHiZgFo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Hyundai|Palisade 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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-23[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Cruz 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Cruz 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2019-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe 2019-20">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe 2021-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe 2021-22">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 2021-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Santa Fe Plug-in Hybrid 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=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-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Sonata Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Sonata Hybrid 2020-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2021">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>7</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2022[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2022">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson 2023[<sup>6</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson 2023">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson Diesel 2019">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Hybrid 2022-23[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Tucson Hybrid 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Tucson Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Hyundai&model=Veloster 2019-20">Buy Here</a></sub></details>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Jeep&model=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>View</summary><sub>- 1 FCA connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Jeep&model=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 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Carnival 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Ceed 2019">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (with HDA II) 2022-23[<sup>7</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (without HDA II) 2022-23[<sup>7</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-23[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (Southeast Asia only) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (with HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai P connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (with HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (without HDA II) 2022-23[<sup>6</sup>](#footnotes)|Highway Driving Assist|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai L connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=EV6 (without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Forte 2019-21">Buy Here</a></sub></details>||
|
||||
|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Forte 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Forte Non-SCC 2019">Buy Here</a></sub></details>||
|
||||
|Kia|Forte Non-SCC 2021|No Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai G connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Forte Non-SCC 2021">Buy Here</a></sub></details>||
|
||||
|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=K5 2021-22">Buy Here</a></sub></details>||
|
||||
|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=K5 Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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 2023[<sup>7</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro EV 2023[<sup>6</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro EV 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai F connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2021-22">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2023[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[<sup>1</sup>](#footnotes)|10 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Plug-in Hybrid 2018-19">Buy Here</a></sub></details>||
|
||||
|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai D connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Niro Plug-in Hybrid 2020">Buy Here</a></sub></details>||
|
||||
|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai B connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Optima 2017">Buy Here</a></sub></details>||
|
||||
@@ -133,10 +130,10 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Seltos 2021">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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>View</summary><sub>- 1 Hyundai E connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>7</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2021-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Sportage 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>6</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai N connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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>View</summary><sub>- 1 Hyundai C connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=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|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai K connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Stinger 2022">Buy Here</a></sub></details>||
|
||||
|Kia|Telluride 2020-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Hyundai H connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Kia&model=Telluride 2020-22">Buy Here</a></sub></details>||
|
||||
@@ -156,8 +153,8 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2016">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2017-19">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2020-21">Buy Here</a></sub></details>||
|
||||
|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=UX Hybrid 2019-22">Buy Here</a></sub></details>||
|
||||
|Lexus|RX Hybrid 2020-22|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=RX Hybrid 2020-22">Buy Here</a></sub></details>||
|
||||
|Lexus|UX Hybrid 2019-23|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lexus&model=UX Hybrid 2019-23">Buy Here</a></sub></details>||
|
||||
|Lincoln|Aviator 2020-21|Co-Pilot360 Plus|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Ford Q3 connector<br>- 1 RJ45 cable (7 ft)<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Lincoln&model=Aviator 2020-21">Buy Here</a></sub></details>||
|
||||
|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=MAN&model=eTGE 2020-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=MAN&model=TGE 2017-23">Buy Here</a></sub></details>|<a href="https://youtu.be/4100gLeabmo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
@@ -167,32 +164,26 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=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|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=Rogue 2018-20">Buy Here</a></sub></details>||
|
||||
|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 Nissan A connector<br>- 1 RJ45 cable (7 ft)<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Nissan&model=X-Trail 2017">Buy Here</a></sub></details>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|32 mph|1 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ram&model=1500 2019-23">Buy Here</a></sub></details>||
|
||||
|Ram|2500 2020-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ram&model=2500 2020-22">Buy Here</a></sub></details>||
|
||||
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|Stock|0 mph|36 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ram&model=3500 2019-22">Buy Here</a></sub></details>||
|
||||
|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Ram connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Ram&model=1500 2019-23">Buy Here</a></sub></details>||
|
||||
|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Ateca 2018">Buy Here</a></sub></details>||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=SEAT&model=Leon 2014-20">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2018-19">Buy Here</a></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|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details>||
|
||||
|Subaru|Forester 2017-18|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Forester 2017-18">Buy Here</a></sub></details>||
|
||||
|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Forester 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Legacy 2015-18|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Legacy 2015-18">Buy Here</a></sub></details>||
|
||||
|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Legacy 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Outback 2015-17|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Outback 2015-17">Buy Here</a></sub></details>||
|
||||
|Subaru|Outback 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Outback 2018-19">Buy Here</a></sub></details>||
|
||||
|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Outback 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2018-19">Buy Here</a></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|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2020-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Ascent 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Ascent 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2018-19">Buy Here</a></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>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Crosstrek 2020-23">Buy Here</a></sub></details>||
|
||||
|Subaru|Forester 2019-21|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Forester 2019-21">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2017-19">Buy Here</a></sub></details>||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Impreza 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Legacy 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Legacy 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|Outback 2020-22|All[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru B connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=Outback 2020-22">Buy Here</a></sub></details>||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2018-19">Buy Here</a></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>7</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Subaru A connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Subaru&model=XV 2020-21">Buy Here</a></sub></details>||
|
||||
|Škoda|Fabia 2022-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Fabia 2022-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Kamiq 2021[<sup>9,11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Kamiq 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Karoq 2019-21[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Karoq 2019-21">Buy Here</a></sub></details>||
|
||||
|Škoda|Kodiaq 2017-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Kodiaq 2017-23">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia 2015, 2018-19[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Octavia 2015, 2018-19">Buy Here</a></sub></details>||
|
||||
|Škoda|Octavia RS 2016[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Octavia RS 2016">Buy Here</a></sub></details>||
|
||||
|Škoda|Scala 2020[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Scala 2020">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Scala 2020-23[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Scala 2020-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Škoda|Superb 2015-22[<sup>11</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Škoda&model=Superb 2015-22">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Alphard 2019-20">Buy Here</a></sub></details>||
|
||||
|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=Alphard Hybrid 2021">Buy Here</a></sub></details>||
|
||||
@@ -237,9 +228,9 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=RAV4 Hybrid 2019-21">Buy Here</a></sub></details>||
|
||||
|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=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|Sienna 2018-20|All|openpilot available[<sup>2</sup>](#footnotes)|19 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 RJ45 cable (7 ft)<br>- 1 Toyota connector<br>- 1 comma power v2<br>- 1 comma three<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Toyota&model=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|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon 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|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon eHybrid 2020-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|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Arteon R 2020-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|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=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|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=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|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=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|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Atlas 2018-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Atlas Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|31 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=California 2021">Buy Here</a></sub></details>||
|
||||
@@ -265,7 +256,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Polo GTI 2018-23">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=T-Cross 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=T-Roc 2021">Buy Here</a></sub></details>[<sup>13</sup>](#footnotes)||
|
||||
|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Taos 2022">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Taos 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Taos 2022-23">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont 2018-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont Cross Sport 2021-22">Buy Here</a></sub></details>||
|
||||
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>View</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-three.html?make=Volkswagen&model=Teramont X 2021-22">Buy Here</a></sub></details>||
|
||||
@@ -276,11 +267,11 @@ A supported vehicle is one that just works when you install a comma three. All s
|
||||
### Footnotes
|
||||
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>3</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>5</sup>openpilot operates above 25mph for 9th Generation Accord 4CYL, 6CYL and Hybrid which don't have Low-Speed Follow. <br />
|
||||
<sup>6</sup>9th Generation model years have ALC data over serial lines and require third party hardware to interface with openpilot. For more information, see <a href="https://github.com/mlocoteta/serialSteeringHardware" target="_blank">mlocoteta's GitHub</a>. <br />
|
||||
<sup>7</sup>Requires a <a href="https://comma.ai/shop/panda" target="_blank">red panda</a> for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. All the hardware needed is sold in the <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD kit</a>. <br />
|
||||
<sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
|
||||
<sup>4</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>5</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
<sup>6</sup>Requires a <a href="https://comma.ai/shop/panda" target="_blank">red panda</a> for this <a href="https://en.wikipedia.org/wiki/CAN_FD" target="_blank">CAN FD car</a>. All the hardware needed is sold in the <a href="https://comma.ai/shop/can-fd-panda-kit" target="_blank">CAN FD kit</a>. <br />
|
||||
<sup>7</sup>In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance. <br />
|
||||
<sup>8</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>9</sup>Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform. <br />
|
||||
<sup>10</sup>Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets. <br />
|
||||
|
||||
@@ -25,9 +25,12 @@ ensuring two main safety requirements.
|
||||
by stepping on the brake pedal or by pressing the cancel button.
|
||||
2. The vehicle must not alter its trajectory too quickly for the driver to safely
|
||||
react. This means that while the system is engaged, the actuators are constrained
|
||||
to operate within reasonable limits.
|
||||
to operate within reasonable limits[^1].
|
||||
|
||||
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [panda/board/safety/](https://github.com/commaai/panda/tree/master/board/safety).
|
||||
|
||||
**Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or
|
||||
not fully meeting the above requirements.
|
||||
|
||||
[^1]: For these actuator limits we observe ISO11270 and ISO15622. Lateral limits described there translate to 0.9 seconds of maximum actuation to achieve a 1m lateral deviation.
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ source "$BASEDIR/launch_env.sh"
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
function agnos_init {
|
||||
# wait longer for weston to come up
|
||||
if [ -f "$BASEDIR/prebuilt" ]; then
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# TODO: move this to agnos
|
||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
|
||||
export VECLIB_MAXIMUM_THREADS=1
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="7.1"
|
||||
export AGNOS_VERSION="8.2"
|
||||
fi
|
||||
|
||||
if [ -z "$PASSIVE" ]; then
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[mypy]
|
||||
python_version = 3.8
|
||||
python_version = 3.11
|
||||
plugins = numpy.typing.mypy_plugin
|
||||
files = body, common, docs, scripts, selfdrive, site_scons, system, tools
|
||||
exclude = ^(cereal/)|(opendbc/)|(panda/)|(laika/)|(laika_repo/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(xx/)
|
||||
|
||||
@@ -10,174 +10,177 @@ documentation = "https://docs.comma.ai"
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "~3.8"
|
||||
atomicwrites = "^1.4.0"
|
||||
casadi = { version = "==3.5.5", platform = "linux" }
|
||||
cffi = "^1.15.1"
|
||||
crcmod = "^1.7"
|
||||
cryptography = "^37.0.4"
|
||||
Cython = "^0.29.30"
|
||||
flake8 = "^4.0.1"
|
||||
Flask = "^2.1.2"
|
||||
future-fstrings = "^1.2.0" # for acados
|
||||
gunicorn = "^20.1.0"
|
||||
python = "~3.11"
|
||||
atomicwrites = "*"
|
||||
aiohttp = "*"
|
||||
aiortc = "*"
|
||||
casadi = "==3.6.3"
|
||||
cffi = "*"
|
||||
crcmod = "*"
|
||||
cryptography = "*"
|
||||
Cython = "*"
|
||||
flake8 = "*"
|
||||
Flask = "*"
|
||||
future-fstrings = "*" # for acados
|
||||
gunicorn = "*"
|
||||
hatanaka = "==2.4"
|
||||
hexdump = "^3.3"
|
||||
Jinja2 = "^3.1.2"
|
||||
json-rpc = "^1.13.0"
|
||||
libusb1 = "^3.0.0"
|
||||
nose = "^1.3.7"
|
||||
numpy = "^1.23.0"
|
||||
onnx = "^1.12.0"
|
||||
onnxruntime-gpu = { version = "^1.11.1", platform = "linux", markers = "platform_machine == 'x86_64'" }
|
||||
pillow = "^9.2.0"
|
||||
hexdump = "*"
|
||||
Jinja2 = "*"
|
||||
json-rpc = "*"
|
||||
libusb1 = "*"
|
||||
numpy = "==1.23.0" # locked pending deprecation fixes in xx
|
||||
onnx = ">=1.14.0"
|
||||
onnxruntime-gpu = { version = ">=1.15.1", platform = "linux", markers = "platform_machine == 'x86_64'" }
|
||||
pillow = "*"
|
||||
poetry = "==1.2.2"
|
||||
protobuf = "==3.20.1"
|
||||
psutil = "^5.9.1"
|
||||
pycapnp = "==1.1.0"
|
||||
pycryptodome = "^3.15.0"
|
||||
PyJWT = "^2.5.0"
|
||||
pyopencl = "^2022.2.4"
|
||||
pyserial = "^3.5"
|
||||
python-dateutil = "^2.8.2"
|
||||
PyYAML = "^6.0"
|
||||
pyzmq = "^23.2.0"
|
||||
requests = "^2.28.1"
|
||||
scons = "^4.3.0"
|
||||
sentry-sdk = "^1.6.0"
|
||||
setproctitle = "^1.2.3"
|
||||
six = "^1.16.0"
|
||||
smbus2 = "^0.4.2"
|
||||
sounddevice = "^0.4.5"
|
||||
spidev = { version = "^3.6", platform = "linux" }
|
||||
spidev2 = { version = "^0.9.0", platform = "linux" }
|
||||
sympy = "^1.10.1"
|
||||
timezonefinder = "^6.0.1"
|
||||
tqdm = "^4.64.0"
|
||||
urllib3 = "^1.26.10"
|
||||
utm = "^0.7.0"
|
||||
websocket_client = "^1.3.3"
|
||||
polyline = "^1.4.0"
|
||||
protobuf = "==3.20.3"
|
||||
psutil = "*"
|
||||
pyaudio = "*"
|
||||
pycapnp = "*"
|
||||
pycryptodome = "*"
|
||||
pydub = "*"
|
||||
PyJWT = "*"
|
||||
pyopencl = "*"
|
||||
pyserial = "*"
|
||||
python-dateutil = "*"
|
||||
PyYAML = "*"
|
||||
pyzmq = "*"
|
||||
requests = "*"
|
||||
scons = "*"
|
||||
sentry-sdk = "*"
|
||||
setproctitle = "*"
|
||||
smbus2 = "*"
|
||||
sounddevice = "*"
|
||||
spidev = { version = "*", platform = "linux" }
|
||||
spidev2 = { version = "*", platform = "linux" }
|
||||
sympy = "*"
|
||||
timezonefinder = "*"
|
||||
tqdm = "*"
|
||||
urllib3 = "*"
|
||||
utm = "*"
|
||||
websocket_client = "*"
|
||||
polyline = "*"
|
||||
sconscontrib = {git = "https://github.com/SCons/scons-contrib.git"}
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
av = "^9.2.0"
|
||||
av = "*"
|
||||
azure-storage-blob = "~2.1"
|
||||
breathe = "^4.34.0"
|
||||
carla = { version = "==0.9.13", platform = "linux", markers = "platform_machine == 'x86_64'" }
|
||||
control = "^0.9.2"
|
||||
coverage = "^6.4.1"
|
||||
dictdiffer = "^0.9.0"
|
||||
fastcluster = "^1.2.6"
|
||||
ft4222 = "^1.4.1"
|
||||
hexdump = "^3.3"
|
||||
breathe = "*"
|
||||
carla = { url = "https://github.com/commaai/carla/releases/download/3.11.4/carla-0.9.14-cp311-cp311-linux_x86_64.whl", platform = "linux", markers = "platform_machine == 'x86_64'" }
|
||||
control = "*"
|
||||
coverage = "*"
|
||||
dictdiffer = "*"
|
||||
fastcluster = "*"
|
||||
ft4222 = "*"
|
||||
hexdump = "*"
|
||||
hypothesis = "==6.46.7"
|
||||
inputs = "^0.5"
|
||||
lru-dict = "^1.1.7"
|
||||
lxml = "^4.9.1"
|
||||
markdown-it-py = "^2.1.0"
|
||||
matplotlib = "^3.5.2"
|
||||
mpld3 = "^0.5.8"
|
||||
mypy = "^0.961"
|
||||
myst-parser = "^0.18.0"
|
||||
natsort = "^8.1.0"
|
||||
numpy = "^1.23.0"
|
||||
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118/opencv_python_headless-4.5.5.64-cp38-cp38-manylinux_2_31_x86_64.whl", platform = "linux" }
|
||||
pandas = "^1.4.3"
|
||||
parameterized = "^0.8.1"
|
||||
paramiko = "^2.11.0"
|
||||
pprofile = "^2.1.0"
|
||||
pre-commit = "^2.19.0"
|
||||
pycurl = "^7.45.1"
|
||||
pygame = "^2.1.2"
|
||||
pylint = "^2.17.4"
|
||||
pyprof2calltree = "^1.4.5"
|
||||
pytest = "^7.1.2"
|
||||
pytest-xdist = "^2.5.0"
|
||||
reverse_geocoder = "^1.5.1"
|
||||
scipy = "^1.8.1"
|
||||
sphinx = "^5.0.2"
|
||||
sphinx-rtd-theme = "^1.0.0"
|
||||
sphinx-sitemap = "^2.2.0"
|
||||
subprocess32 = "^3.5.4"
|
||||
tabulate = "^0.8.10"
|
||||
tenacity = "^8.0.1"
|
||||
types-atomicwrites = "^1.4.5"
|
||||
types-certifi = "^2021.10.8"
|
||||
types-pycurl = "^7.45.1"
|
||||
types-python-dateutil = "^2.8.19.13"
|
||||
types-PyYAML = "^6.0"
|
||||
types-requests = "^2.28.11"
|
||||
types-tabulate = "^0.8.10"
|
||||
inputs = "*"
|
||||
lru-dict = "*"
|
||||
lxml = "*"
|
||||
markdown-it-py = "*"
|
||||
matplotlib = "*"
|
||||
mpld3 = "*"
|
||||
mypy = "*"
|
||||
myst-parser = "*"
|
||||
natsort = "*"
|
||||
opencv-python-headless = "*"
|
||||
pandas = "*"
|
||||
parameterized = "^0.8"
|
||||
paramiko = "*"
|
||||
pprofile = "*"
|
||||
pre-commit = "*"
|
||||
pycurl = "*"
|
||||
pygame = "*"
|
||||
pylint = "*"
|
||||
pyprof2calltree = "*"
|
||||
pytest = "*"
|
||||
pytest-xdist = "*"
|
||||
reverse_geocoder = "*"
|
||||
scipy = "==1.9.3" # pinned until xx refs changes can be checked
|
||||
sphinx = "*"
|
||||
sphinx-rtd-theme = "*"
|
||||
sphinx-sitemap = "*"
|
||||
tabulate = "*"
|
||||
tenacity = "*"
|
||||
types-atomicwrites = "*"
|
||||
types-certifi = "*"
|
||||
types-pycurl = "*"
|
||||
types-python-dateutil = "*"
|
||||
types-PyYAML = "*"
|
||||
types-requests = "*"
|
||||
types-tabulate = "*"
|
||||
|
||||
|
||||
[tool.poetry.group.xx]
|
||||
optional = true
|
||||
|
||||
[tool.poetry.group.xx.dependencies]
|
||||
aenum = "^3.1.11"
|
||||
aiohttp = "^3.8.1"
|
||||
albumentations = "^1.2.1"
|
||||
apex = { url = "https://github.com/commaai/apex/releases/download/pytorch2.0.0%2Bcu11.8/apex-0.1-cp38-cp38-linux_x86_64.whl" }
|
||||
azure-cli-core = "^2.38.0"
|
||||
azure-common = "^1.1.28"
|
||||
azure-core = "^1.24.2"
|
||||
aenum = "*"
|
||||
aiohttp = "*"
|
||||
albumentations = "*"
|
||||
azure-cli-core = "*"
|
||||
azure-common = "*"
|
||||
azure-core = "*"
|
||||
azure-nspkg = "~3.0"
|
||||
azure-storage-common = "~2.1"
|
||||
azure-storage-nspkg = "~3.1"
|
||||
blosc = "==1.9.2"
|
||||
cloudpickle = "^2.1.0"
|
||||
configargparse = "^1.5.3"
|
||||
cupy-cuda11x = "^11.6.0"
|
||||
datadog = "^0.44.0"
|
||||
dotmap = "^1.3.30"
|
||||
einops = "^0.5.0"
|
||||
elasticsearch = "^8.3.1"
|
||||
Flask-Cors = "^3.0.10"
|
||||
Flask-SocketIO = "^5.2.0"
|
||||
GeoAlchemy2 = "^0.12.1"
|
||||
imageio = "^2.19.5"
|
||||
influxdb-client = "^1.30.0"
|
||||
ipykernel = "^6.15.1"
|
||||
ipython = "^8.4.0"
|
||||
joblib = "^1.1.0"
|
||||
json-logging-py = "^0.2"
|
||||
jupyter = "^1.0.0"
|
||||
jupyterlab = "^3.4.4"
|
||||
jupyterlab-vim = "^0.15.1"
|
||||
Markdown = "^3.4.1"
|
||||
msgpack-python = "^0.5.6"
|
||||
cloudpickle = "*"
|
||||
configargparse = "*"
|
||||
cupy-cuda11x = "*"
|
||||
datadog = "*"
|
||||
dotmap = "*"
|
||||
einops = "*"
|
||||
elasticsearch = "*"
|
||||
Flask-Cors = "*"
|
||||
Flask-SocketIO = "*"
|
||||
GeoAlchemy2 = "*"
|
||||
imageio = "*"
|
||||
influxdb-client = "*"
|
||||
ipykernel = "*"
|
||||
ipython = "*"
|
||||
joblib = "*"
|
||||
json-logging-py = "*"
|
||||
jupyter = "*"
|
||||
jupyterlab = "*"
|
||||
jupyterlab-vim = "*"
|
||||
Markdown = "*"
|
||||
msgpack-python = "*"
|
||||
networkx = "~2.8"
|
||||
nvidia-ml-py3 = "^7.352.0"
|
||||
onnx2torch = "^1.5.4"
|
||||
onnxoptimizer = "^0.3.1"
|
||||
osmium = "^3.3.0"
|
||||
pillow-avif-plugin = "^1.2.2"
|
||||
nvidia-ml-py3 = "*"
|
||||
onnx2torch = "*"
|
||||
onnxoptimizer = "*"
|
||||
osmium = "*"
|
||||
pillow-avif-plugin = "*"
|
||||
pipenv = "==2022.10.12"
|
||||
plotly = "^5.9.0"
|
||||
pycuda = "^2022.1"
|
||||
Pygments = "^2.12.0"
|
||||
plotly = "*"
|
||||
pycuda = "*"
|
||||
Pygments = "*"
|
||||
PyMySQL = "~0.9"
|
||||
pyproj = "^3.3.1"
|
||||
python-logstash = "^0.4.8"
|
||||
redis = "^4.3.4"
|
||||
s2sphere = "^0.2.5"
|
||||
scikit-image = "^0.19.3"
|
||||
scikit-learn = "^1.1.1"
|
||||
segmentation-models-pytorch = "==0.3.2"
|
||||
simplejson = "^3.17.6"
|
||||
SQLAlchemy = "^1.4.39"
|
||||
torch = { url = "https://download.pytorch.org/whl/cu118/torch-2.0.0%2Bcu118-cp38-cp38-linux_x86_64.whl" }
|
||||
torchsummary = "^1.5.1"
|
||||
torchvision = { url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.1%2Bcu118-cp38-cp38-linux_x86_64.whl" }
|
||||
triton = "^2.0.0"
|
||||
Werkzeug = "^2.1.2"
|
||||
pyproj = "*"
|
||||
python-logstash = "*"
|
||||
redis = "*"
|
||||
s2sphere = "*"
|
||||
scikit-image = "*"
|
||||
scikit-learn = "*"
|
||||
segmentation-models-pytorch = "==0.3.3"
|
||||
simplejson = "*"
|
||||
SQLAlchemy = "*"
|
||||
torch = { url = "https://download.pytorch.org/whl/cu118/torch-2.0.1%2Bcu118-cp311-cp311-linux_x86_64.whl" }
|
||||
torchsummary = "*"
|
||||
torchvision = { url = "https://download.pytorch.org/whl/cu118/torchvision-0.15.2%2Bcu118-cp311-cp311-linux_x86_64.whl" }
|
||||
triton = "*"
|
||||
Werkzeug = "*"
|
||||
zerorpc = { git = "https://github.com/commaai/zerorpc-python.git", branch = "master" }
|
||||
omegaconf = "^2.3.0"
|
||||
omegaconf = "*"
|
||||
osmnx = "==1.2.2"
|
||||
tritonclient = {version = "2.28.0", extras = ["http"]}
|
||||
tensorrt = "^8.6.0"
|
||||
transformers = "*"
|
||||
timm = "==0.9.2"
|
||||
PyNvCodec = { git = "https://github.com/NVIDIA/VideoProcessingFramework.git", rev = "3347e55" }
|
||||
apex = { url = "https://github.com/commaai/apex/releases/download/pytorch2.0.1%2Bcu11.8/apex-0.1-cp311-cp311-linux_x86_64.whl" }
|
||||
opencv-python-headless = { url = "https://github.com/commaai/opencv-python-builder/releases/download/4.5.5.64%2Bcu118-cp311/opencv_python_headless-4.5.5.64-cp311-cp311-manylinux_2_31_x86_64.whl", platform = "linux" }
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -52,10 +52,8 @@ git branch --set-upstream-to=origin/$RELEASE_BRANCH
|
||||
# Build panda firmware
|
||||
pushd panda/
|
||||
scons -u .
|
||||
mv board/obj/panda.bin.signed /tmp/panda.bin.signed
|
||||
mv board/obj/panda_h7.bin.signed /tmp/panda_h7.bin.signed
|
||||
mv board/obj/bootstub.panda.bin /tmp/bootstub.panda.bin
|
||||
mv board/obj/bootstub.panda_h7.bin /tmp/bootstub.panda_h7.bin
|
||||
mkdir /tmp/panda_obj/
|
||||
mv board/obj/panda.bin.signed board/obj/panda_h7.bin.signed board/obj/bootstub.panda.bin board/obj/bootstub.panda_h7.bin /tmp/panda_obj/
|
||||
popd
|
||||
|
||||
# Build
|
||||
@@ -86,10 +84,7 @@ rm selfdrive/modeld/models/supercombo.onnx
|
||||
rm -rf selfdrive/ui/replay/
|
||||
# Move back signed panda fw
|
||||
mkdir -p panda/board/obj
|
||||
mv /tmp/panda.bin.signed panda/board/obj/panda.bin.signed
|
||||
mv /tmp/panda_h7.bin.signed panda/board/obj/panda_h7.bin.signed
|
||||
mv /tmp/bootstub.panda.bin panda/board/obj/bootstub.panda.bin
|
||||
mv /tmp/bootstub.panda_h7.bin panda/board/obj/bootstub.panda_h7.bin
|
||||
mv /tmp/panda_obj/* panda/board/obj/
|
||||
|
||||
# Restore third_party
|
||||
git checkout third_party/
|
||||
|
||||
@@ -208,7 +208,6 @@ selfdrive/controls/lib/lateral_planner.py
|
||||
selfdrive/controls/lib/longcontrol.py
|
||||
selfdrive/controls/lib/longitudinal_planner.py
|
||||
selfdrive/controls/lib/pid.py
|
||||
selfdrive/controls/lib/radar_helpers.py
|
||||
selfdrive/controls/lib/speed_limit_controller.py
|
||||
selfdrive/controls/lib/turn_speed_controller.py
|
||||
selfdrive/controls/lib/vehicle_model.py
|
||||
@@ -467,6 +466,7 @@ third_party/libyuv/larch64/**
|
||||
third_party/snpe/include/**
|
||||
third_party/snpe/dsp**
|
||||
|
||||
third_party/acados/.gitignore
|
||||
third_party/acados/x86_64/**
|
||||
third_party/acados/larch64/**
|
||||
third_party/acados/include/**
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/usr/bin/bash
|
||||
nmcli connection modify --temporary lte ipv4.route-metric 1 ipv6.route-metric 1
|
||||
nmcli con up --wait --ask lte
|
||||
nmcli con up lte
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><g fill="#fff" clip-path="url(#a)" transform="matrix(2.3325 0 0 2.3825 -72.89 -63.797)"><path d="M31.25 53.12A15.62 15.62 0 0 1 46.88 37.5h9.37v-9.16a1.56 1.56 0 0 1 2.56-1.2l14.75 12.28c.75.63.75 1.78 0 2.4l-14.75 12.3a1.56 1.56 0 0 1-2.56-1.2v-9.17h-9.38a9.37 9.37 0 0 0-9.37 9.37v15.63h-6.25z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 466 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" fill="none" viewBox="0 0 100 100"><g fill="#fff" clip-path="url(#a)"><path d="M31.25 53.12A15.62 15.62 0 0 1 46.88 37.5h9.37v-9.16a1.56 1.56 0 0 1 2.56-1.2l14.75 12.28c.75.63.75 1.78 0 2.4l-14.75 12.3a1.56 1.56 0 0 1-2.56-1.2v-9.17h-9.38a9.37 9.37 0 0 0-9.37 9.37v15.63h-6.25V53.12Z"/><path fill-rule="evenodd" d="M43.44 2.72a9.28 9.28 0 0 1 13.12 0l40.72 40.72a9.26 9.26 0 0 1 0 13.12L56.56 97.28a9.26 9.26 0 0 1-13.1 0L2.71 56.56a9.26 9.26 0 0 1 0-13.11L43.44 2.72Zm8.75 4.37a3.1 3.1 0 0 0-4.38 0L7.1 47.81a3.1 3.1 0 0 0 0 4.38L47.8 92.9a3.1 3.1 0 0 0 4.38 0L92.9 52.2a3.1 3.1 0 0 0 0-4.38L52.2 7.1Z" clip-rule="evenodd"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 780 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><g clip-path="url(#a)"><path fill="#fff" d="M49.1 0 36.35 31.82H0l27.27 27.27L13.64 100l35.45-23.64L86.36 100 72.73 59.1 100 31.81H63.64L49.09 0Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 315 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><g clip-path="url(#a)"><path fill="#fff" d="M7.7 98.08c0 1.06.85 1.92 1.92 1.92h28.84V76.92h23.08V100H90.4c1.06 0 1.92-.86 1.92-1.92V53.85H7.69v44.23ZM99.47 42.9l-7.17-6.3V7.7a7.7 7.7 0 0 0-15.39 0v15.4L51.4.68A1.92 1.92 0 0 0 48.68.6l-.08.08L.52 42.72a1.92 1.92 0 0 0 1.38 3.24l13.49.2h82.68a1.92 1.92 0 0 0 1.41-3.25Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 489 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><g fill="#fff" clip-path="url(#a)"><path d="M53.22 6.37a43.75 43.75 0 0 0-3.21-.12V0a50 50 0 0 1 3.68.14l-.47 6.23Zm12.53 2.81c-2-.77-4.06-1.4-6.16-1.87l1.37-6.1c2.4.54 4.75 1.25 7.04 2.14l-2.25 5.83Zm8.56 4.44c-.9-.6-1.8-1.16-2.74-1.69l3.08-5.44a50.15 50.15 0 0 1 6.12 4.09l-3.85 4.93a43.75 43.75 0 0 0-2.61-1.89ZM85.77 24.8a43.7 43.7 0 0 0-4.08-4.97l4.53-4.31a52.14 52.14 0 0 1 4.66 5.68l-5.1 3.6Zm4.65 8.45a36.5 36.5 0 0 0-1.34-2.92l5.59-2.81a48.6 48.6 0 0 1 2.8 6.8l-5.93 1.95a43.9 43.9 0 0 0-1.12-3.02Zm3.31 15.67a43.68 43.68 0 0 0-.62-6.4l6.15-1.07c.42 2.41.67 4.86.73 7.31l-6.25.16Zm-.81 9.61c.2-1.06.37-2.12.5-3.19l6.2.77a49.3 49.3 0 0 1-1.43 7.22l-6.02-1.67c.28-1.03.53-2.07.75-3.13ZM86.97 73.4A44.16 44.16 0 0 0 90 67.72l5.72 2.53c-1 2.25-2.16 4.42-3.47 6.49l-5.28-3.34Zm-6.03 7.53c.76-.77 1.5-1.55 2.19-2.36l4.74 4.08a49.2 49.2 0 0 1-2.51 2.7l-4.42-4.42Z"/><path d="M50 6.25a43.75 43.75 0 1 0 30.94 74.68l4.42 4.42A50 50 0 1 1 50 0v6.25Z"/><path d="M46.88 18.75a3.13 3.13 0 0 1 3.13 3.12v32.56l20.3 11.6a3.12 3.12 0 0 1-3.1 5.42l-21.88-12.5a3.12 3.12 0 0 1-1.57-2.7V21.86a3.12 3.12 0 0 1 3.12-3.12Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" fill="#fff" class="bi bi-gear-fill"><path d="M75.24 8.4c-3.304-11.2-19.176-11.2-22.48 0l-.8 2.72a11.712 11.712 0 0 1-16.84 6.976l-2.48-1.36c-10.264-5.584-21.488 5.64-15.896 15.896l1.352 2.488c3.568 6.56.184 14.728-6.976 16.84l-2.72.8c-11.2 3.304-11.2 19.176 0 22.48l2.72.8a11.712 11.712 0 0 1 6.976 16.84l-1.36 2.48c-5.584 10.264 5.64 21.488 15.896 15.896l2.488-1.352a11.712 11.712 0 0 1 16.84 6.976l.8 2.72c3.304 11.2 19.176 11.2 22.48 0l.8-2.72a11.712 11.712 0 0 1 16.84-6.976l2.48 1.36c10.264 5.584 21.488-5.64 15.896-15.896l-1.352-2.488a11.712 11.712 0 0 1 6.976-16.84l2.72-.8c11.2-3.304 11.2-19.176 0-22.48l-2.72-.8a11.712 11.712 0 0 1-6.976-16.84l1.36-2.48c5.584-10.264-5.64-21.488-15.896-15.896l-2.488 1.352a11.712 11.712 0 0 1-16.84-6.976ZM64 87.44a23.44 23.44 0 1 1 0-46.88 23.432 23.432 0 0 1 0 46.864z" style="stroke-width:8"/></svg>
|
||||
|
After Width: | Height: | Size: 910 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" fill="none"><g clip-path="url(#a)"><path fill="#fff" d="M77.78 22.22V6.42L70.67 0H29.33l-7.1 8.3v13.92H5.55L0 27.78v66.66L5.56 100h88.88l5.56-5.56V27.78l-5.56-5.56H77.78Zm-44.45-11.1h33.34v11.1H33.33v-11.1Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h100v100H0z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 1.4 MiB |
@@ -42,6 +42,10 @@ from selfdrive.statsd import STATS_DIR
|
||||
from system.swaglog import SWAGLOG_DIR, cloudlog
|
||||
from system.version import get_commit, get_origin, get_short_branch, get_version
|
||||
|
||||
|
||||
# missing in pysocket
|
||||
TCP_USER_TIMEOUT = 18
|
||||
|
||||
ATHENA_HOST = os.getenv('ATHENA_HOST', 'wss://athena.comma.ai')
|
||||
HANDLER_THREADS = int(os.getenv('HANDLER_THREADS', "4"))
|
||||
LOCAL_PORT_WHITELIST = {8022}
|
||||
@@ -137,10 +141,11 @@ class UploadQueueCache:
|
||||
cloudlog.exception("athena.UploadQueueCache.cache.exception")
|
||||
|
||||
|
||||
def handle_long_poll(ws: WebSocket) -> None:
|
||||
def handle_long_poll(ws: WebSocket, exit_event: Optional[threading.Event]) -> None:
|
||||
end_event = threading.Event()
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=ws_manage, args=(ws, end_event), name='ws_manage'),
|
||||
threading.Thread(target=ws_recv, args=(ws, end_event), name='ws_recv'),
|
||||
threading.Thread(target=ws_send, args=(ws, end_event), name='ws_send'),
|
||||
threading.Thread(target=upload_handler, args=(end_event,), name='upload_handler'),
|
||||
@@ -154,8 +159,9 @@ def handle_long_poll(ws: WebSocket) -> None:
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
try:
|
||||
while not end_event.is_set():
|
||||
time.sleep(0.1)
|
||||
while not end_event.wait(0.1):
|
||||
if exit_event is not None and exit_event.is_set():
|
||||
end_event.set()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
end_event.set()
|
||||
raise
|
||||
@@ -754,11 +760,30 @@ def ws_send(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
end_event.set()
|
||||
|
||||
|
||||
def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
|
||||
params = Params()
|
||||
onroad_prev = None
|
||||
sock = ws.sock
|
||||
|
||||
while True:
|
||||
onroad = params.get_bool("IsOnroad")
|
||||
if onroad != onroad_prev:
|
||||
onroad_prev = onroad
|
||||
|
||||
sock.setsockopt(socket.IPPROTO_TCP, TCP_USER_TIMEOUT, 16000 if onroad else 0)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
|
||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)
|
||||
|
||||
if end_event.wait(5):
|
||||
break
|
||||
|
||||
|
||||
def backoff(retries: int) -> int:
|
||||
return random.randrange(0, min(128, int(2 ** retries)))
|
||||
|
||||
|
||||
def main():
|
||||
def main(exit_event: Optional[threading.Event] = None):
|
||||
try:
|
||||
set_core_affinity([0, 1, 2, 3])
|
||||
except Exception:
|
||||
@@ -771,26 +796,34 @@ def main():
|
||||
ws_uri = ATHENA_HOST + "/ws/v2/" + dongle_id
|
||||
api = Api(dongle_id)
|
||||
|
||||
conn_start = None
|
||||
conn_retries = 0
|
||||
while 1:
|
||||
while exit_event is None or not exit_event.is_set():
|
||||
try:
|
||||
cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri)
|
||||
if conn_start is None:
|
||||
conn_start = time.monotonic()
|
||||
|
||||
cloudlog.event("athenad.main.connecting_ws", ws_uri=ws_uri, retries=conn_retries)
|
||||
ws = create_connection(ws_uri,
|
||||
cookie="jwt=" + api.get_token(),
|
||||
enable_multithread=True,
|
||||
timeout=30.0)
|
||||
cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri)
|
||||
cloudlog.event("athenad.main.connected_ws", ws_uri=ws_uri, retries=conn_retries,
|
||||
duration=time.monotonic() - conn_start)
|
||||
conn_start = None
|
||||
|
||||
conn_retries = 0
|
||||
cur_upload_items.clear()
|
||||
|
||||
handle_long_poll(ws)
|
||||
handle_long_poll(ws, exit_event)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
break
|
||||
except (ConnectionError, TimeoutError, WebSocketException):
|
||||
conn_retries += 1
|
||||
params.remove("LastAthenaPingTime")
|
||||
except socket.timeout:
|
||||
# TODO: socket.timeout and TimeoutError are now the same exception since python3.10
|
||||
# Remove the socket.timeout case once we have fully moved to python3.11
|
||||
except socket.timeout: # pylint: disable=duplicate-except
|
||||
params.remove("LastAthenaPingTime")
|
||||
except Exception:
|
||||
cloudlog.exception("athenad.main.exception")
|
||||
|
||||
@@ -159,7 +159,7 @@ class TestAthenadMethods(unittest.TestCase):
|
||||
resp = dispatcher["uploadFileToUrl"]("qlog.bz2", f"{host}/qlog.bz2", {})
|
||||
self.assertEqual(resp['enqueued'], 1)
|
||||
self.assertNotIn('failed', resp)
|
||||
self.assertDictContainsSubset({"path": fn, "url": f"{host}/qlog.bz2", "headers": {}}, resp['items'][0])
|
||||
self.assertLessEqual({"path": fn, "url": f"{host}/qlog.bz2", "headers": {}}.items(), resp['items'][0].items())
|
||||
self.assertIsNotNone(resp['items'][0].get('id'))
|
||||
self.assertEqual(athenad.upload_queue.qsize(), 1)
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
from typing import Callable, cast, Optional
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from common.params import Params
|
||||
from common.timeout import Timeout
|
||||
from selfdrive.athena import athenad
|
||||
from system.hardware import TICI
|
||||
|
||||
|
||||
def wifi_radio(on: bool) -> None:
|
||||
if not TICI:
|
||||
return
|
||||
print(f"wifi {'on' if on else 'off'}")
|
||||
subprocess.run(["nmcli", "radio", "wifi", "on" if on else "off"], check=True)
|
||||
|
||||
|
||||
class TestAthenadPing(unittest.TestCase):
|
||||
params: Params
|
||||
dongle_id: str
|
||||
|
||||
athenad: threading.Thread
|
||||
exit_event: threading.Event
|
||||
|
||||
_create_connection: Callable
|
||||
|
||||
def _get_ping_time(self) -> Optional[str]:
|
||||
return cast(Optional[str], self.params.get("LastAthenaPingTime", encoding="utf-8"))
|
||||
|
||||
def _clear_ping_time(self) -> None:
|
||||
self.params.remove("LastAthenaPingTime")
|
||||
|
||||
def _received_ping(self) -> bool:
|
||||
return self._get_ping_time() is not None
|
||||
|
||||
def _set_onroad(self, onroad: bool) -> None:
|
||||
self.params.put_bool("IsOnroad", onroad)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
cls.params = Params()
|
||||
cls.dongle_id = cls.params.get("DongleId", encoding="utf-8")
|
||||
cls._create_connection = athenad.create_connection
|
||||
athenad.create_connection = MagicMock(wraps=cls._create_connection)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
wifi_radio(True)
|
||||
athenad.create_connection = cls._create_connection
|
||||
|
||||
def setUp(self) -> None:
|
||||
wifi_radio(True)
|
||||
self._clear_ping_time()
|
||||
|
||||
self.exit_event = threading.Event()
|
||||
self.athenad = threading.Thread(target=athenad.main, args=(self.exit_event,))
|
||||
|
||||
athenad.create_connection.reset_mock()
|
||||
|
||||
def tearDown(self) -> None:
|
||||
if self.athenad.is_alive():
|
||||
self.exit_event.set()
|
||||
self.athenad.join()
|
||||
|
||||
def assertTimeout(self, reconnect_time: float) -> None:
|
||||
self.athenad.start()
|
||||
|
||||
time.sleep(1)
|
||||
athenad.create_connection.assert_called_once()
|
||||
athenad.create_connection.reset_mock()
|
||||
|
||||
# check normal behaviour
|
||||
with self.subTest("Wi-Fi: receives ping"), Timeout(70, "no ping received"):
|
||||
while not self._received_ping():
|
||||
time.sleep(0.1)
|
||||
print("ping received")
|
||||
|
||||
athenad.create_connection.assert_not_called()
|
||||
|
||||
# websocket should attempt reconnect after short time
|
||||
with self.subTest("LTE: attempt reconnect"):
|
||||
wifi_radio(False)
|
||||
print("waiting for reconnect attempt")
|
||||
start_time = time.monotonic()
|
||||
with Timeout(reconnect_time, "no reconnect attempt"):
|
||||
while not athenad.create_connection.called:
|
||||
time.sleep(0.1)
|
||||
print(f"reconnect attempt after {time.monotonic() - start_time:.2f}s")
|
||||
|
||||
self._clear_ping_time()
|
||||
|
||||
# check ping received after reconnect
|
||||
with self.subTest("LTE: receives ping"), Timeout(70, "no ping received"):
|
||||
while not self._received_ping():
|
||||
time.sleep(0.1)
|
||||
print("ping received")
|
||||
|
||||
@unittest.skipIf(not TICI, "only run on desk")
|
||||
def test_offroad(self) -> None:
|
||||
self._set_onroad(False)
|
||||
self.assertTimeout(100) # expect approx 90s
|
||||
|
||||
@unittest.skipIf(not TICI, "only run on desk")
|
||||
def test_onroad(self) -> None:
|
||||
self._set_onroad(True)
|
||||
self.assertTimeout(30) # expect 20-30s
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -51,7 +51,6 @@
|
||||
#define MIN_IR_POWER 0.0f
|
||||
#define CUTOFF_IL 400
|
||||
#define SATURATE_IL 1000
|
||||
#define NIBBLE_TO_HEX(n) ((n) < 10 ? (n) + '0' : ((n) - 10) + 'a')
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
std::atomic<bool> ignition(false);
|
||||
@@ -200,6 +199,10 @@ Panda *connect(std::string serial="", uint32_t index=0) {
|
||||
}
|
||||
//panda->enable_deepsleep();
|
||||
|
||||
if (!panda->up_to_date() && !getenv("BOARDD_SKIP_FW_CHECK")) {
|
||||
throw std::runtime_error("Panda firmware out of date. Run pandad.py to update.");
|
||||
}
|
||||
|
||||
sync_time(panda.get(), SyncTimeDir::FROM_PANDA);
|
||||
return panda.release();
|
||||
}
|
||||
@@ -226,7 +229,7 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) {
|
||||
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(msg.get()));
|
||||
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>();
|
||||
|
||||
//Dont send if older than 1 second
|
||||
// Don't send if older than 1 second
|
||||
if ((nanos_since_boot() - event.getLogMonoTime() < 1e9) && !fake_send) {
|
||||
for (const auto& panda : pandas) {
|
||||
LOGT("sending sendcan to panda: %s", (panda->hw_serial()).c_str());
|
||||
@@ -242,10 +245,9 @@ void can_send_thread(std::vector<Panda *> pandas, bool fake_send) {
|
||||
void can_recv_thread(std::vector<Panda *> pandas) {
|
||||
util::set_thread_name("boardd_can_recv");
|
||||
|
||||
// can = 8006
|
||||
PubMaster pm({"can"});
|
||||
|
||||
// run at 100hz
|
||||
// run at 100Hz
|
||||
const uint64_t dt = 10000000ULL;
|
||||
uint64_t next_frame_time = nanos_since_boot() + dt;
|
||||
std::vector<can_frame> raw_can_data;
|
||||
@@ -284,20 +286,6 @@ void can_recv_thread(std::vector<Panda *> pandas) {
|
||||
}
|
||||
}
|
||||
|
||||
void send_empty_peripheral_state(PubMaster *pm) {
|
||||
MessageBuilder msg;
|
||||
auto peripheralState = msg.initEvent().initPeripheralState();
|
||||
peripheralState.setPandaType(cereal::PandaState::PandaType::UNKNOWN);
|
||||
pm->send("peripheralState", msg);
|
||||
}
|
||||
|
||||
void send_empty_panda_state(PubMaster *pm) {
|
||||
MessageBuilder msg;
|
||||
auto pandaStates = msg.initEvent().initPandaStates(1);
|
||||
pandaStates[0].setPandaType(cereal::PandaState::PandaType::UNKNOWN);
|
||||
pm->send("pandaStates", msg);
|
||||
}
|
||||
|
||||
std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool spoofing_started) {
|
||||
bool ignition_local = false;
|
||||
const uint32_t pandas_cnt = pandas.size();
|
||||
@@ -313,6 +301,10 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
std::vector<std::array<can_health_t, PANDA_CAN_CNT>> pandaCanStates;
|
||||
pandaCanStates.reserve(pandas_cnt);
|
||||
|
||||
const bool red_panda_comma_three = (pandas.size() == 2) &&
|
||||
(pandas[0]->hw_type == cereal::PandaState::PandaType::DOS) &&
|
||||
(pandas[1]->hw_type == cereal::PandaState::PandaType::RED_PANDA);
|
||||
|
||||
for (const auto& panda : pandas){
|
||||
auto health_opt = panda->get_state();
|
||||
if (!health_opt) {
|
||||
@@ -335,6 +327,13 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
health.ignition_line_pkt = 1;
|
||||
}
|
||||
|
||||
// on comma three setups with a red panda, the dos can
|
||||
// get false positive ignitions due to the harness box
|
||||
// without a harness connector, so ignore it
|
||||
if (red_panda_comma_three && (panda->hw_type == cereal::PandaState::PandaType::DOS)) {
|
||||
health.ignition_line_pkt = 0;
|
||||
}
|
||||
|
||||
ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0));
|
||||
|
||||
pandaStates.push_back(health);
|
||||
@@ -349,7 +348,6 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
|
||||
}
|
||||
|
||||
#ifndef __x86_64__
|
||||
bool power_save_desired = !ignition_local;
|
||||
if (health.power_save_enabled_pkt != power_save_desired) {
|
||||
panda->set_power_saving(power_save_desired);
|
||||
@@ -359,7 +357,6 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
if (!ignition_local && (health.safety_mode_pkt != (uint8_t)(cereal::CarParams::SafetyModel::NO_OUTPUT))) {
|
||||
panda->set_safety_model(cereal::CarParams::SafetyModel::NO_OUTPUT);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!panda->comms_healthy()) {
|
||||
evt.setValid(false);
|
||||
@@ -420,6 +417,10 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
|
||||
cs[j].setCanfdEnabled(can_health.canfd_enabled);
|
||||
cs[j].setBrsEnabled(can_health.brs_enabled);
|
||||
cs[j].setCanfdNonIso(can_health.canfd_non_iso);
|
||||
cs[j].setIrq0CallRate(can_health.irq0_call_rate);
|
||||
cs[j].setIrq1CallRate(can_health.irq1_call_rate);
|
||||
cs[j].setIrq2CallRate(can_health.irq2_call_rate);
|
||||
cs[j].setCanCoreResetCnt(can_health.can_core_reset_cnt);
|
||||
}
|
||||
|
||||
// Convert faults bitset to capnp list
|
||||
@@ -463,11 +464,12 @@ void send_peripheral_state(PubMaster *pm, Panda *panda) {
|
||||
pm->send("peripheralState", msg);
|
||||
}
|
||||
|
||||
void panda_state_thread(PubMaster *pm, std::vector<Panda *> pandas, bool spoofing_started) {
|
||||
void panda_state_thread(std::vector<Panda *> pandas, bool spoofing_started) {
|
||||
util::set_thread_name("boardd_panda_state");
|
||||
|
||||
Params params;
|
||||
SubMaster sm({"controlsState", "carState"});
|
||||
PubMaster pm({"pandaStates", "peripheralState"});
|
||||
|
||||
Panda *peripheral_panda = pandas[0];
|
||||
bool is_onroad = false;
|
||||
@@ -486,8 +488,8 @@ void panda_state_thread(PubMaster *pm, std::vector<Panda *> pandas, bool spoofin
|
||||
uint64_t start_time = nanos_since_boot();
|
||||
|
||||
// send out peripheralState
|
||||
send_peripheral_state(pm, peripheral_panda);
|
||||
auto ignition_opt = send_panda_states(pm, pandas, spoofing_started);
|
||||
send_peripheral_state(&pm, peripheral_panda);
|
||||
auto ignition_opt = send_panda_states(&pm, pandas, spoofing_started);
|
||||
|
||||
if (!ignition_opt) {
|
||||
continue;
|
||||
@@ -554,22 +556,20 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
|
||||
|
||||
SubMaster sm({"deviceState", "driverCameraState"});
|
||||
|
||||
uint64_t last_front_frame_t = 0;
|
||||
uint64_t last_driver_camera_t = 0;
|
||||
uint16_t prev_fan_speed = 999;
|
||||
uint16_t ir_pwr = 0;
|
||||
uint16_t prev_ir_pwr = 999;
|
||||
unsigned int cnt = 0;
|
||||
|
||||
FirstOrderFilter integ_lines_filter(0, 30.0, 0.05);
|
||||
|
||||
while (!do_exit && panda->connected()) {
|
||||
cnt++;
|
||||
sm.update(1000); // TODO: what happens if EINTR is sent while in sm.update?
|
||||
sm.update(1000);
|
||||
|
||||
if (sm.updated("deviceState") && !no_fan_control) {
|
||||
// Fan speed
|
||||
uint16_t fan_speed = sm["deviceState"].getDeviceState().getFanSpeedPercentDesired();
|
||||
if (fan_speed != prev_fan_speed || cnt % 100 == 0) {
|
||||
if (fan_speed != prev_fan_speed || sm.frame % 100 == 0) {
|
||||
panda->set_fan_speed(fan_speed);
|
||||
prev_fan_speed = fan_speed;
|
||||
}
|
||||
@@ -580,7 +580,7 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
|
||||
int cur_integ_lines = event.getDriverCameraState().getIntegLines();
|
||||
|
||||
cur_integ_lines = integ_lines_filter.update(cur_integ_lines);
|
||||
last_front_frame_t = event.getLogMonoTime();
|
||||
last_driver_camera_t = event.getLogMonoTime();
|
||||
|
||||
if (cur_integ_lines <= CUTOFF_IL) {
|
||||
ir_pwr = 100.0 * MIN_IR_POWER;
|
||||
@@ -591,48 +591,48 @@ void peripheral_control_thread(Panda *panda, bool no_fan_control) {
|
||||
}
|
||||
}
|
||||
|
||||
// Disable ir_pwr on front frame timeout
|
||||
uint64_t cur_t = nanos_since_boot();
|
||||
if (cur_t - last_front_frame_t > 1e9) {
|
||||
// Disable IR on input timeout
|
||||
if (nanos_since_boot() - last_driver_camera_t > 1e9) {
|
||||
ir_pwr = 0;
|
||||
}
|
||||
|
||||
if (ir_pwr != prev_ir_pwr || cnt % 100 == 0 || ir_pwr >= 50.0) {
|
||||
if (ir_pwr != prev_ir_pwr || sm.frame % 100 == 0 || ir_pwr >= 50.0) {
|
||||
panda->set_ir_pwr(ir_pwr);
|
||||
prev_ir_pwr = ir_pwr;
|
||||
}
|
||||
|
||||
// Write to rtc once per minute when no ignition present
|
||||
if (!ignition && (cnt % 120 == 1)) {
|
||||
if (!ignition && (sm.frame % 120 == 1)) {
|
||||
sync_time(panda, SyncTimeDir::TO_PANDA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void boardd_main_thread(std::vector<std::string> serials) {
|
||||
PubMaster pm({"pandaStates", "peripheralState"});
|
||||
LOGW("attempting to connect");
|
||||
LOGW("launching boardd");
|
||||
|
||||
if (serials.size() == 0) {
|
||||
// connect to all
|
||||
serials = Panda::list();
|
||||
|
||||
// exit if no pandas are connected
|
||||
if (serials.size() == 0) {
|
||||
LOGW("no pandas found, exiting");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string serials_str;
|
||||
for (int i = 0; i < serials.size(); i++) {
|
||||
serials_str += serials[i];
|
||||
if (i < serials.size() - 1) serials_str += ", ";
|
||||
}
|
||||
LOGW("connecting to pandas: %s", serials_str.c_str());
|
||||
|
||||
// connect to all provided serials
|
||||
std::vector<Panda *> pandas;
|
||||
for (int i = 0; i < serials.size() && !do_exit; /**/) {
|
||||
Panda *p = connect(serials[i], i);
|
||||
if (!p) {
|
||||
// send empty pandaState & peripheralState and try again
|
||||
send_empty_panda_state(&pm);
|
||||
send_empty_peripheral_state(&pm);
|
||||
util::sleep_for(500);
|
||||
util::sleep_for(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -641,12 +641,12 @@ void boardd_main_thread(std::vector<std::string> serials) {
|
||||
}
|
||||
|
||||
if (!do_exit) {
|
||||
LOGW("connected to board");
|
||||
Panda *peripheral_panda = pandas[0];
|
||||
LOGW("connected to all pandas");
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
threads.emplace_back(panda_state_thread, &pm, pandas, getenv("STARTED") != nullptr);
|
||||
threads.emplace_back(peripheral_control_thread, peripheral_panda, getenv("NO_FAN_CONTROL") != nullptr);
|
||||
threads.emplace_back(panda_state_thread, pandas, getenv("STARTED") != nullptr);
|
||||
threads.emplace_back(peripheral_control_thread, pandas[0], getenv("NO_FAN_CONTROL") != nullptr);
|
||||
|
||||
threads.emplace_back(can_send_thread, pandas, getenv("FAKESEND") != nullptr);
|
||||
threads.emplace_back(can_recv_thread, pandas);
|
||||
@@ -654,7 +654,6 @@ void boardd_main_thread(std::vector<std::string> serials) {
|
||||
for (auto &t : threads) t.join();
|
||||
}
|
||||
|
||||
// we have exited, clean up pandas
|
||||
for (Panda *panda : pandas) {
|
||||
delete panda;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ from libcpp.vector cimport vector
|
||||
from libcpp.string cimport string
|
||||
from libcpp cimport bool
|
||||
|
||||
cdef struct can_frame:
|
||||
long address
|
||||
string dat
|
||||
long busTime
|
||||
long src
|
||||
cdef extern from "panda.h":
|
||||
cdef struct can_frame:
|
||||
long address
|
||||
string dat
|
||||
long busTime
|
||||
long src
|
||||
|
||||
cdef extern void can_list_to_can_capnp_cpp(const vector[can_frame] &can_list, string &out, bool sendCan, bool valid)
|
||||
cdef extern from "can_list_to_can_capnp.cc":
|
||||
void can_list_to_can_capnp_cpp(const vector[can_frame] &can_list, string &out, bool sendCan, bool valid)
|
||||
|
||||
def can_list_to_can_capnp(can_msgs, msgtype='can', valid=True):
|
||||
cdef vector[can_frame] can_list
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#include "cereal/messaging/messaging.h"
|
||||
#include "panda.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void can_list_to_can_capnp_cpp(const std::vector<can_frame> &can_list, std::string &out, bool sendCan, bool valid) {
|
||||
MessageBuilder msg;
|
||||
auto event = msg.initEvent(valid);
|
||||
@@ -21,5 +19,3 @@ void can_list_to_can_capnp_cpp(const std::vector<can_frame> &can_list, std::stri
|
||||
kj::ArrayOutputStream output_stream(kj::ArrayPtr<capnp::byte>((unsigned char *)out.data(), msg_size));
|
||||
capnp::writeMessage(output_stream, msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ Panda::Panda(std::string serial, uint32_t bus_offset) : bus_offset(bus_offset) {
|
||||
#ifndef __APPLE__
|
||||
handle = std::make_unique<PandaSpiHandle>(serial);
|
||||
LOGW("connected to %s over SPI", serial.c_str());
|
||||
#else
|
||||
throw e;
|
||||
#endif
|
||||
}
|
||||
|
||||
hw_type = get_hw_type();
|
||||
|
||||
has_rtc = (hw_type == cereal::PandaState::PandaType::UNO) ||
|
||||
(hw_type == cereal::PandaState::PandaType::DOS) ||
|
||||
(hw_type == cereal::PandaState::PandaType::TRES);
|
||||
@@ -153,6 +154,19 @@ std::optional<std::string> Panda::get_serial() {
|
||||
return err >= 0 ? std::make_optional(serial_buf) : std::nullopt;
|
||||
}
|
||||
|
||||
bool Panda::up_to_date() {
|
||||
if (auto fw_sig = get_firmware_version()) {
|
||||
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed" }) {
|
||||
auto content = util::read_file(std::string("../../panda/board/obj/") + fn);
|
||||
if (content.size() >= fw_sig->size() &&
|
||||
memcmp(content.data() + content.size() - fw_sig->size(), fw_sig->data(), fw_sig->size()) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Panda::set_power_saving(bool power_saving) {
|
||||
handle->control_write(0xe7, power_saving, 0);
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
std::optional<can_health_t> get_can_state(uint16_t can_number);
|
||||
void set_loopback(bool loopback);
|
||||
std::optional<std::vector<uint8_t>> get_firmware_version();
|
||||
bool up_to_date();
|
||||
std::optional<std::string> get_serial();
|
||||
void set_power_saving(bool power_saving);
|
||||
void enable_deepsleep();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
|
||||
#define TIMEOUT 0
|
||||
#define SPI_BUF_SIZE 1024
|
||||
#define SPI_BUF_SIZE 2048
|
||||
|
||||
|
||||
// comms base class
|
||||
@@ -74,7 +74,7 @@ private:
|
||||
uint8_t rx_buf[SPI_BUF_SIZE];
|
||||
inline static std::recursive_mutex hw_lock;
|
||||
|
||||
int wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout);
|
||||
int wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout, unsigned int length);
|
||||
int bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len, unsigned int timeout);
|
||||
int spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, unsigned int timeout);
|
||||
int spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, unsigned int timeout);
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
import os
|
||||
import usb1
|
||||
import time
|
||||
import json
|
||||
import subprocess
|
||||
from typing import List, NoReturn
|
||||
from functools import cmp_to_key
|
||||
|
||||
from panda import Panda, PandaDFU, FW_PATH
|
||||
from panda import Panda, PandaDFU, PandaProtocolMismatch, FW_PATH
|
||||
from common.basedir import BASEDIR
|
||||
from common.params import Params
|
||||
from selfdrive.boardd.set_time import set_time
|
||||
@@ -23,9 +24,56 @@ def get_expected_signature(panda: Panda) -> bytes:
|
||||
cloudlog.exception("Error computing expected signature")
|
||||
return b""
|
||||
|
||||
def read_panda_logs(panda: Panda) -> None:
|
||||
"""
|
||||
Forward panda logs to the cloud
|
||||
"""
|
||||
|
||||
params = Params()
|
||||
serial = panda.get_usb_serial()
|
||||
|
||||
log_state = {}
|
||||
try:
|
||||
l = json.loads(params.get("PandaLogState"))
|
||||
for k, v in l.items():
|
||||
if isinstance(k, str) and isinstance(v, int):
|
||||
log_state[k] = v
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
cloudlog.exception("failed to parse PandaLogState")
|
||||
|
||||
try:
|
||||
if serial in log_state:
|
||||
logs = panda.get_logs(last_id=log_state[serial])
|
||||
else:
|
||||
logs = panda.get_logs(get_all=True)
|
||||
|
||||
# truncate logs to 100 entries if needed
|
||||
MAX_LOGS = 100
|
||||
if len(logs) > MAX_LOGS:
|
||||
cloudlog.warning(f"Panda {serial} has {len(logs)} logs, truncating to {MAX_LOGS}")
|
||||
logs = logs[-MAX_LOGS:]
|
||||
|
||||
# update log state
|
||||
if len(logs) > 0:
|
||||
log_state[serial] = logs[-1]["id"]
|
||||
|
||||
for log in logs:
|
||||
if log['timestamp'] is not None:
|
||||
log['timestamp'] = log['timestamp'].isoformat()
|
||||
cloudlog.event("panda_log", **log, serial=serial)
|
||||
|
||||
params.put("PandaLogState", json.dumps(log_state))
|
||||
except Exception:
|
||||
cloudlog.exception(f"Error getting logs for panda {serial}")
|
||||
|
||||
|
||||
def flash_panda(panda_serial: str) -> Panda:
|
||||
panda = Panda(panda_serial)
|
||||
try:
|
||||
panda = Panda(panda_serial)
|
||||
except PandaProtocolMismatch:
|
||||
cloudlog.warning("detected protocol mismatch, reflashing panda")
|
||||
HARDWARE.recover_internal_panda()
|
||||
raise
|
||||
|
||||
fw_signature = get_expected_signature(panda)
|
||||
internal_panda = panda.is_internal()
|
||||
@@ -45,7 +93,7 @@ def flash_panda(panda_serial: str) -> Panda:
|
||||
if internal_panda:
|
||||
HARDWARE.recover_internal_panda()
|
||||
panda.recover(reset=(not internal_panda))
|
||||
cloudlog.info("Done flashing bootloader")
|
||||
cloudlog.info("Done flashing bootstub")
|
||||
|
||||
if panda.bootstub:
|
||||
cloudlog.info("Panda still not booting, exiting")
|
||||
@@ -121,7 +169,7 @@ def main() -> NoReturn:
|
||||
|
||||
# sort pandas to have deterministic order
|
||||
pandas.sort(key=cmp_to_key(panda_sort_cmp))
|
||||
panda_serials = list(map(lambda p: p.get_usb_serial(), pandas)) # type: ignore
|
||||
panda_serials = list(map(lambda p: p.get_usb_serial(), pandas))
|
||||
|
||||
# log panda fw versions
|
||||
params.put("PandaSignatures", b','.join(p.get_signature() for p in pandas))
|
||||
@@ -133,6 +181,8 @@ def main() -> NoReturn:
|
||||
params.put_bool("PandaHeartbeatLost", True)
|
||||
cloudlog.event("heartbeat lost", deviceState=health, serial=panda.get_usb_serial())
|
||||
|
||||
read_panda_logs(panda)
|
||||
|
||||
if first_run:
|
||||
if panda.is_internal():
|
||||
# update time from RTC
|
||||
@@ -152,6 +202,9 @@ def main() -> NoReturn:
|
||||
# a panda was disconnected while setting everything up. let's try again
|
||||
cloudlog.exception("Panda USB exception while setting up")
|
||||
continue
|
||||
except PandaProtocolMismatch:
|
||||
cloudlog.exception("pandad.protocol_mismatch")
|
||||
continue
|
||||
except Exception:
|
||||
cloudlog.exception("pandad.uncaught_exception")
|
||||
continue
|
||||
|
||||
@@ -163,7 +163,7 @@ int PandaSpiHandle::bulk_read(unsigned char endpoint, unsigned char* data, int l
|
||||
}
|
||||
|
||||
int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t rx_len, unsigned int timeout) {
|
||||
const int xfer_size = 0x40 * 15;
|
||||
const int xfer_size = SPI_BUF_SIZE - 0x40;
|
||||
|
||||
int ret = 0;
|
||||
uint16_t length = (tx_data != NULL) ? tx_len : rx_len;
|
||||
@@ -220,27 +220,40 @@ bool check_checksum(uint8_t *data, int data_len) {
|
||||
|
||||
int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, unsigned int timeout) {
|
||||
int ret;
|
||||
int count = 0;
|
||||
int nack_count = 0;
|
||||
int timeout_count = 0;
|
||||
bool timed_out = false;
|
||||
double start_time = millis_since_boot();
|
||||
|
||||
do {
|
||||
ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len, timeout);
|
||||
|
||||
if (ret < 0) {
|
||||
timed_out = (timeout != 0) && (count > 5);
|
||||
count += ret == SpiError::ACK_TIMEOUT;
|
||||
timed_out = (timeout != 0) && (timeout_count > 5);
|
||||
timeout_count += ret == SpiError::ACK_TIMEOUT;
|
||||
|
||||
// give other threads a chance to run
|
||||
std::this_thread::yield();
|
||||
|
||||
if (ret == SpiError::NACK) {
|
||||
// prevent busy waiting while the panda is NACK'ing
|
||||
// due to full TX buffers
|
||||
nack_count += 1;
|
||||
if (nack_count > 3) {
|
||||
usleep(std::clamp(nack_count*10, 200, 2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (ret < 0 && connected && !timed_out);
|
||||
|
||||
if (ret < 0) {
|
||||
LOGE("transfer failed, after %d tries, %.2fms", count, millis_since_boot() - start_time);
|
||||
LOGE("transfer failed, after %d tries, %.2fms", timeout_count, millis_since_boot() - start_time);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout) {
|
||||
int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout, unsigned int length) {
|
||||
double start_millis = millis_since_boot();
|
||||
if (timeout == 0) {
|
||||
timeout = SPI_ACK_TIMEOUT;
|
||||
@@ -250,8 +263,7 @@ int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout)
|
||||
spi_ioc_transfer transfer = {
|
||||
.tx_buf = (uint64_t)tx_buf,
|
||||
.rx_buf = (uint64_t)rx_buf,
|
||||
.delay_usecs = 5,
|
||||
.len = 1
|
||||
.len = length
|
||||
};
|
||||
tx_buf[0] = tx;
|
||||
|
||||
@@ -311,7 +323,7 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx
|
||||
}
|
||||
|
||||
// Wait for (N)ACK
|
||||
ret = wait_for_ack(SPI_HACK, 0x11, timeout);
|
||||
ret = wait_for_ack(SPI_HACK, 0x11, timeout, 1);
|
||||
if (ret < 0) {
|
||||
goto transfer_fail;
|
||||
}
|
||||
@@ -329,26 +341,18 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx
|
||||
}
|
||||
|
||||
// Wait for (N)ACK
|
||||
ret = wait_for_ack(SPI_DACK, 0x13, timeout);
|
||||
ret = wait_for_ack(SPI_DACK, 0x13, timeout, 3);
|
||||
if (ret < 0) {
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Read data len
|
||||
transfer.len = 2;
|
||||
transfer.rx_buf = (uint64_t)(rx_buf + 1);
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
if (ret < 0) {
|
||||
LOGE("SPI: failed to read rx data len");
|
||||
goto transfer_fail;
|
||||
}
|
||||
// Read data
|
||||
rx_data_len = *(uint16_t *)(rx_buf+1);
|
||||
if (rx_data_len >= SPI_BUF_SIZE) {
|
||||
LOGE("SPI: RX data len larger than buf size %d", rx_data_len);
|
||||
goto transfer_fail;
|
||||
}
|
||||
|
||||
// Read data
|
||||
transfer.len = rx_data_len + 1;
|
||||
transfer.rx_buf = (uint64_t)(rx_buf + 2 + 1);
|
||||
ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer);
|
||||
|
||||
@@ -36,7 +36,7 @@ class TestBoardd(unittest.TestCase):
|
||||
params = Params()
|
||||
params.put_bool("IsOnroad", False)
|
||||
|
||||
with Timeout(60, "boardd didn't start"):
|
||||
with Timeout(90, "boardd didn't start"):
|
||||
sm = messaging.SubMaster(['pandaStates'])
|
||||
while sm.rcv_frame['pandaStates'] < 1 or len(sm['pandaStates']) == 0 or \
|
||||
any(ps.pandaType == log.PandaState.PandaType.unknown for ps in sm['pandaStates']):
|
||||
|
||||
@@ -6,7 +6,8 @@ import unittest
|
||||
import cereal.messaging as messaging
|
||||
from cereal import log
|
||||
from common.gpio import gpio_set, gpio_init
|
||||
from panda import Panda, PandaDFU
|
||||
from common.params import Params
|
||||
from panda import Panda, PandaDFU, PandaProtocolMismatch
|
||||
from selfdrive.test.helpers import phone_only
|
||||
from selfdrive.manager.process_config import managed_processes
|
||||
from system.hardware import HARDWARE
|
||||
@@ -17,23 +18,52 @@ HERE = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
class TestPandad(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.params = Params()
|
||||
self.start_log_state = self.params.get("PandaLogState")
|
||||
|
||||
def tearDown(self):
|
||||
managed_processes['pandad'].stop()
|
||||
|
||||
def _wait_for_boardd(self, timeout=30):
|
||||
sm = messaging.SubMaster(['peripheralState'])
|
||||
for _ in range(timeout):
|
||||
sm.update(1000)
|
||||
for _ in range(timeout*10):
|
||||
sm.update(100)
|
||||
if sm['peripheralState'].pandaType != log.PandaState.PandaType.unknown:
|
||||
break
|
||||
|
||||
if sm['peripheralState'].pandaType == log.PandaState.PandaType.unknown:
|
||||
raise Exception("boardd failed to start")
|
||||
|
||||
# simple check that we did something with the panda logs
|
||||
cur_log_state = self.params.get("PandaLogState")
|
||||
assert cur_log_state != self.start_log_state
|
||||
|
||||
def _go_to_dfu(self):
|
||||
HARDWARE.recover_internal_panda()
|
||||
assert Panda.wait_for_dfu(None, 10)
|
||||
|
||||
def _flash_and_test(self, fn, expect_mismatch=False):
|
||||
self._go_to_dfu()
|
||||
pd = PandaDFU(None)
|
||||
if fn is None:
|
||||
fn = os.path.join(HERE, pd.get_mcu_type().config.bootstub_fn)
|
||||
with open(fn, "rb") as f:
|
||||
pd.program_bootstub(f.read())
|
||||
pd.reset()
|
||||
HARDWARE.reset_internal_panda()
|
||||
|
||||
assert Panda.wait_for_panda(None, 10)
|
||||
if expect_mismatch:
|
||||
with self.assertRaises(PandaProtocolMismatch):
|
||||
Panda()
|
||||
else:
|
||||
with Panda() as p:
|
||||
assert p.bootstub
|
||||
|
||||
managed_processes['pandad'].start()
|
||||
self._wait_for_boardd(45)
|
||||
|
||||
@phone_only
|
||||
def test_in_dfu(self):
|
||||
HARDWARE.recover_internal_panda()
|
||||
@@ -71,26 +101,18 @@ class TestPandad(unittest.TestCase):
|
||||
managed_processes['pandad'].start()
|
||||
self._wait_for_boardd(8)
|
||||
|
||||
@phone_only
|
||||
def test_protocol_version_check(self):
|
||||
if HARDWARE.get_device_type() == 'tici':
|
||||
self.skipTest("")
|
||||
|
||||
# flash old fw
|
||||
fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin")
|
||||
self._flash_and_test(fn, expect_mismatch=True)
|
||||
|
||||
@phone_only
|
||||
def test_release_to_devel_bootstub(self):
|
||||
if HARDWARE.get_device_type() != 'tici':
|
||||
self.skipTest("TODO: fix reset timeout")
|
||||
|
||||
# flash release bootstub
|
||||
self._go_to_dfu()
|
||||
pd = PandaDFU(None)
|
||||
fn = os.path.join(HERE, pd.get_mcu_type().config.bootstub_fn)
|
||||
with open(fn, "rb") as f:
|
||||
pd.program_bootstub(f.read())
|
||||
pd.reset()
|
||||
|
||||
assert Panda.wait_for_panda(None, 20)
|
||||
with Panda() as p:
|
||||
assert p.bootstub
|
||||
|
||||
managed_processes['pandad'].start()
|
||||
self._wait_for_boardd(60)
|
||||
|
||||
self._flash_and_test(None)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# functions common among cars
|
||||
import math
|
||||
from collections import namedtuple
|
||||
from typing import Dict, Optional
|
||||
|
||||
@@ -192,7 +191,7 @@ class CanBusBase:
|
||||
def __init__(self, CP, fingerprint: Optional[Dict[int, Dict[int, int]]]) -> None:
|
||||
if CP is None:
|
||||
assert fingerprint is not None
|
||||
num = math.ceil(max([k for k, v in fingerprint.items() if len(v)], default=1) / 4)
|
||||
num = max([k for k, v in fingerprint.items() if len(v)], default=0) // 4 + 1
|
||||
else:
|
||||
num = len(CP.safetyConfigs)
|
||||
self.offset = 4 * (num - 1)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
|
||||
from common.params import Params
|
||||
from common.realtime import DT_CTRL
|
||||
from opendbc.can.packer import CANPacker
|
||||
from selfdrive.car.body import bodycan
|
||||
@@ -23,10 +24,14 @@ class CarController:
|
||||
self.speed_pid = PIDController(0.115, k_i=0.23, rate=1/DT_CTRL)
|
||||
self.balance_pid = PIDController(1300, k_i=0, k_d=280, rate=1/DT_CTRL)
|
||||
self.turn_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
|
||||
self.wheeled_speed_pid = PIDController(110, k_i=11.5, rate=1/DT_CTRL)
|
||||
|
||||
self.torque_r_filtered = 0.
|
||||
self.torque_l_filtered = 0.
|
||||
|
||||
params = Params()
|
||||
self.wheeled_body = params.get("WheeledBody")
|
||||
|
||||
@staticmethod
|
||||
def deadband_filter(torque, deadband):
|
||||
if torque > 0:
|
||||
@@ -45,19 +50,22 @@ class CarController:
|
||||
# Read these from the joystick
|
||||
# TODO: this isn't acceleration, okay?
|
||||
speed_desired = CC.actuators.accel / 5.
|
||||
speed_diff_desired = -CC.actuators.steer
|
||||
speed_diff_desired = -CC.actuators.steer / 2.
|
||||
|
||||
speed_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl + CS.out.wheelSpeeds.fr) / 2.
|
||||
speed_error = speed_desired - speed_measured
|
||||
|
||||
freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or
|
||||
(speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR))
|
||||
angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator)
|
||||
if self.wheeled_body is None:
|
||||
freeze_integrator = ((speed_error < 0 and self.speed_pid.error_integral <= -MAX_POS_INTEGRATOR) or
|
||||
(speed_error > 0 and self.speed_pid.error_integral >= MAX_POS_INTEGRATOR))
|
||||
angle_setpoint = self.speed_pid.update(speed_error, freeze_integrator=freeze_integrator)
|
||||
|
||||
# Clip angle error, this is enough to get up from stands
|
||||
angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR)
|
||||
angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.)
|
||||
torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate)
|
||||
# Clip angle error, this is enough to get up from stands
|
||||
angle_error = np.clip((-CC.orientationNED[1]) - angle_setpoint, -MAX_ANGLE_ERROR, MAX_ANGLE_ERROR)
|
||||
angle_error_rate = np.clip(-CC.angularVelocity[1], -1., 1.)
|
||||
torque = self.balance_pid.update(angle_error, error_rate=angle_error_rate)
|
||||
else:
|
||||
torque = self.wheeled_speed_pid.update(speed_error, freeze_integrator=False)
|
||||
|
||||
speed_diff_measured = SPEED_FROM_RPM * (CS.out.wheelSpeeds.fl - CS.out.wheelSpeeds.fr)
|
||||
turn_error = speed_diff_measured - speed_diff_desired
|
||||
|
||||
@@ -83,6 +83,7 @@ interfaces = load_interfaces(interface_names)
|
||||
def fingerprint(logcan, sendcan, num_pandas):
|
||||
fixed_fingerprint = os.environ.get('FINGERPRINT', "")
|
||||
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
|
||||
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
|
||||
ecu_rx_addrs = set()
|
||||
params = Params()
|
||||
|
||||
@@ -92,11 +93,12 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
|
||||
cached_params = params.get("CarParamsCache")
|
||||
if cached_params is not None:
|
||||
cached_params = car.CarParams.from_bytes(cached_params)
|
||||
if cached_params.carName == "mock":
|
||||
cached_params = None
|
||||
with car.CarParams.from_bytes(cached_params) as cached_params:
|
||||
if cached_params.carName == "mock":
|
||||
cached_params = None
|
||||
|
||||
if cached_params is not None and len(cached_params.carFw) > 0 and cached_params.carVin is not VIN_UNKNOWN:
|
||||
if cached_params is not None and len(cached_params.carFw) > 0 and \
|
||||
cached_params.carVin is not VIN_UNKNOWN and not disable_fw_cache:
|
||||
cloudlog.warning("Using cached CarParams")
|
||||
vin, vin_rx_addr = cached_params.carVin, 0
|
||||
car_fw = list(cached_params.carFw)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from enum import IntFlag
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from cereal import car
|
||||
@@ -72,7 +72,7 @@ RAM_CARS = RAM_DT | RAM_HD
|
||||
@dataclass
|
||||
class ChryslerCarInfo(CarInfo):
|
||||
package: str = "Adaptive Cruise Control (ACC)"
|
||||
car_parts: CarParts = CarParts.common([CarHarness.fca])
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.fca]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = {
|
||||
@@ -179,7 +179,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
Request(
|
||||
[CHRYSLER_VERSION_REQUEST],
|
||||
[CHRYSLER_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.hcp, Ecu.engine, Ecu.transmission],
|
||||
whitelist_ecus=[Ecu.abs, Ecu.hybrid, Ecu.engine, Ecu.transmission],
|
||||
bus=0,
|
||||
),
|
||||
Request(
|
||||
@@ -190,8 +190,8 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
),
|
||||
],
|
||||
extra_ecus=[
|
||||
(Ecu.hcp, 0x7e2, None), # manages transmission on hybrids
|
||||
(Ecu.abs, 0x7e4, None), # alt address for abs on hybrids
|
||||
(Ecu.hybrid, 0x7e2, None), # manages transmission on hybrids
|
||||
(Ecu.abs, 0x7e4, None), # alt address for abs on hybrids
|
||||
],
|
||||
)
|
||||
|
||||
@@ -269,6 +269,7 @@ FW_VERSIONS = {
|
||||
(Ecu.fwdRadar, 0x753, None): [
|
||||
b'04672892AB',
|
||||
b'04672932AB',
|
||||
b'04672932AC',
|
||||
b'22DTRHD_AA',
|
||||
b'68320950AH',
|
||||
b'68320950AI',
|
||||
@@ -295,12 +296,14 @@ FW_VERSIONS = {
|
||||
b'68552788AA',
|
||||
b'68552789AA',
|
||||
b'68552790AA',
|
||||
b'68585106AB',
|
||||
b'68585109AB',
|
||||
b'68585112AB',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'05036065AE ',
|
||||
b'05036066AE ',
|
||||
b'05149592AE ',
|
||||
b'05149591AD ',
|
||||
b'05149846AA ',
|
||||
b'05149848AA ',
|
||||
|
||||
@@ -7,22 +7,22 @@ EXT_DIAG_RESPONSE = b'\x50\x03'
|
||||
COM_CONT_RESPONSE = b''
|
||||
|
||||
|
||||
def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, debug=False):
|
||||
def disable_ecu(logcan, sendcan, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, debug=False):
|
||||
"""Silence an ECU by disabling sending and receiving messages using UDS 0x28.
|
||||
The ECU will stay silent as long as openpilot keeps sending Tester Present.
|
||||
|
||||
This is used to disable the radar in some cars. Openpilot will emulate the radar.
|
||||
WARNING: THIS DISABLES AEB!"""
|
||||
cloudlog.warning(f"ecu disable {hex(addr)} ...")
|
||||
cloudlog.warning(f"ecu disable {hex(addr), sub_addr} ...")
|
||||
|
||||
for i in range(retry):
|
||||
try:
|
||||
query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug)
|
||||
query = IsoTpParallelQuery(sendcan, logcan, bus, [(addr, sub_addr)], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug)
|
||||
|
||||
for _, _ in query.get_data(timeout).items():
|
||||
cloudlog.warning("communication control disable tx/rx ...")
|
||||
|
||||
query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], [com_cont_req], [COM_CONT_RESPONSE], debug=debug)
|
||||
query = IsoTpParallelQuery(sendcan, logcan, bus, [(addr, sub_addr)], [com_cont_req], [COM_CONT_RESPONSE], debug=debug)
|
||||
query.get_data(0)
|
||||
|
||||
cloudlog.warning("ecu disabled")
|
||||
|
||||
@@ -29,7 +29,8 @@ def get_all_car_info() -> List[CarInfo]:
|
||||
all_car_info: List[CarInfo] = []
|
||||
footnotes = get_all_footnotes()
|
||||
for model, car_info in get_interface_attr("CAR_INFO", combine_brands=True).items():
|
||||
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=False, docs=True)
|
||||
# If available, uses experimental longitudinal limits for the docs
|
||||
CP = interfaces[model][0].get_params(model, fingerprint=gen_empty_fingerprint(), car_fw=[car.CarParams.CarFw(ecu="unknown")], experimental_long=True, docs=True)
|
||||
|
||||
if CP.dashcamOnly or car_info is None:
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import re
|
||||
from collections import namedtuple
|
||||
import copy
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
@@ -144,8 +145,11 @@ DEFAULT_CAR_PARTS: List[EnumBase] = [Device.three]
|
||||
class CarParts:
|
||||
parts: List[EnumBase] = field(default_factory=list)
|
||||
|
||||
def __call__(self):
|
||||
return copy.deepcopy(self)
|
||||
|
||||
@classmethod
|
||||
def common(cls, add: List[EnumBase] = None, remove: List[EnumBase] = None):
|
||||
def common(cls, add: Optional[List[EnumBase]] = None, remove: Optional[List[EnumBase]] = None):
|
||||
p = [part for part in (add or []) + DEFAULT_CAR_PARTS if part not in (remove or [])]
|
||||
return cls(p)
|
||||
|
||||
@@ -225,7 +229,7 @@ class CarInfo:
|
||||
auto_resume: Optional[bool] = None
|
||||
|
||||
# all the parts needed for the supported car
|
||||
car_parts: CarParts = CarParts()
|
||||
car_parts: CarParts = field(default_factory=CarParts)
|
||||
|
||||
def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]):
|
||||
self.car_name = CP.carName
|
||||
@@ -234,14 +238,14 @@ class CarInfo:
|
||||
|
||||
# longitudinal column
|
||||
op_long = "Stock"
|
||||
if CP.openpilotLongitudinalControl and not CP.enableDsu:
|
||||
op_long = "openpilot"
|
||||
elif CP.experimentalLongitudinalAvailable or CP.enableDsu:
|
||||
if CP.experimentalLongitudinalAvailable or CP.enableDsu:
|
||||
op_long = "openpilot available"
|
||||
if CP.enableDsu:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_DSU)
|
||||
else:
|
||||
self.footnotes.append(CommonFootnote.EXP_LONG_AVAIL)
|
||||
elif CP.openpilotLongitudinalControl and not CP.enableDsu:
|
||||
op_long = "openpilot"
|
||||
|
||||
# min steer & enable speed columns
|
||||
# TODO: set all the min steer speeds in carParams and remove this
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can.packer import CANPacker
|
||||
from selfdrive.car import apply_std_steer_angle_limits
|
||||
from selfdrive.car.ford.fordcan import CanBus, create_acc_msg, create_acc_ui_msg, create_button_msg, \
|
||||
create_lat_ctl_msg, create_lat_ctl2_msg, create_lka_msg, create_lkas_ui_msg
|
||||
from selfdrive.car.ford.values import CANFD_CARS, CarControllerParams
|
||||
from selfdrive.car.ford.values import CANFD_CAR, CarControllerParams
|
||||
|
||||
LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
@@ -43,6 +43,7 @@ class CarController:
|
||||
|
||||
main_on = CS.out.cruiseState.available
|
||||
steer_alert = hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw)
|
||||
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
|
||||
|
||||
### acc buttons ###
|
||||
if CC.cruiseControl.cancel:
|
||||
@@ -68,7 +69,7 @@ class CarController:
|
||||
|
||||
self.apply_curvature_last = apply_curvature
|
||||
|
||||
if self.CP.carFingerprint in CANFD_CARS:
|
||||
if self.CP.carFingerprint in CANFD_CAR:
|
||||
# TODO: extended mode
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0xF
|
||||
@@ -100,7 +101,7 @@ class CarController:
|
||||
# send acc ui msg at 5Hz or if ui state changes
|
||||
if (self.frame % CarControllerParams.ACC_UI_STEP) == 0 or send_ui:
|
||||
can_sends.append(create_acc_ui_msg(self.packer, self.CAN, self.CP, main_on, CC.latActive,
|
||||
CS.out.cruiseState.standstill, hud_control,
|
||||
fcw_alert, CS.out.cruiseState.standstill, hud_control,
|
||||
CS.acc_tja_status_stock_values))
|
||||
|
||||
self.main_on_last = main_on
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
|
||||
from opendbc.can.parser import CANParser
|
||||
from selfdrive.car.interfaces import CarStateBase
|
||||
from selfdrive.car.ford.fordcan import CanBus
|
||||
from selfdrive.car.ford.values import DBC, CarControllerParams
|
||||
from selfdrive.car.ford.values import CANFD_CAR, CarControllerParams, DBC
|
||||
|
||||
GearShifter = car.CarState.GearShifter
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
@@ -55,6 +55,10 @@ class CarState(CarStateBase):
|
||||
ret.steerFaultPermanent = cp.vl["EPAS_INFO"]["EPAS_Failure"] in (2, 3)
|
||||
# ret.espDisabled = False # TODO: find traction control signal
|
||||
|
||||
if self.CP.carFingerprint in CANFD_CAR:
|
||||
# this signal is always 0 on non-CAN FD cars
|
||||
ret.steerFaultTemporary |= cp.vl["Lane_Assist_Data3_FD1"]["LatCtlSte_D_Stat"] not in (1, 2, 3)
|
||||
|
||||
# cruise state
|
||||
ret.cruiseState.speed = cp.vl["EngBrakeData"]["Veh_V_DsplyCcSet"] * CV.MPH_TO_MS
|
||||
ret.cruiseState.enabled = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (4, 5)
|
||||
@@ -62,6 +66,8 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.nonAdaptive = cp.vl["Cluster_Info1_FD1"]["AccEnbl_B_RqDrv"] == 0
|
||||
ret.cruiseState.standstill = cp.vl["EngBrakeData"]["AccStopMde_D_Rq"] == 3
|
||||
ret.accFaulted = cp.vl["EngBrakeData"]["CcStat_D_Actl"] in (1, 2)
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
ret.accFaulted = ret.accFaulted or cp_cam.vl["ACCDATA"]["CmbbDeny_B_Actl"] == 1
|
||||
|
||||
# gear
|
||||
if self.CP.transmissionType == TransmissionType.automatic:
|
||||
@@ -91,8 +97,9 @@ class CarState(CarStateBase):
|
||||
|
||||
# blindspot sensors
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = cp.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
|
||||
ret.rightBlindspot = cp.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
|
||||
cp_bsm = cp_cam if self.CP.carFingerprint in CANFD_CAR else cp
|
||||
ret.leftBlindspot = cp_bsm.vl["Side_Detect_L_Stat"]["SodDetctLeft_D_Stat"] != 0
|
||||
ret.rightBlindspot = cp_bsm.vl["Side_Detect_R_Stat"]["SodDetctRight_D_Stat"] != 0
|
||||
|
||||
# Stock steering buttons so that we can passthru blinkers etc.
|
||||
self.buttons_stock_values = cp.vl["Steering_Data_FD1"]
|
||||
@@ -179,12 +186,19 @@ class CarState(CarStateBase):
|
||||
("Cluster_Info1_FD1", 10),
|
||||
("SteeringPinion_Data", 100),
|
||||
("EPAS_INFO", 50),
|
||||
("Lane_Assist_Data3_FD1", 33),
|
||||
("Steering_Data_FD1", 10),
|
||||
("BodyInfo_3_FD1", 2),
|
||||
("RCMStatusMessage2_FD1", 10),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in CANFD_CAR:
|
||||
signals += [
|
||||
("LatCtlSte_D_Stat", "Lane_Assist_Data3_FD1"), # PSCM lateral control status
|
||||
]
|
||||
checks += [
|
||||
("Lane_Assist_Data3_FD1", 33),
|
||||
]
|
||||
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
signals += [
|
||||
("TrnRng_D_RqGsm", "Gear_Shift_by_Wire_FD1"), # GWM transmission gear position
|
||||
@@ -202,7 +216,7 @@ class CarState(CarStateBase):
|
||||
("BCM_Lamp_Stat_FD1", 1),
|
||||
]
|
||||
|
||||
if CP.enableBsm:
|
||||
if CP.enableBsm and CP.carFingerprint not in CANFD_CAR:
|
||||
signals += [
|
||||
("SodDetctLeft_D_Stat", "Side_Detect_L_Stat"), # Blindspot sensor, left
|
||||
("SodDetctRight_D_Stat", "Side_Detect_R_Stat"), # Blindspot sensor, right
|
||||
@@ -218,6 +232,8 @@ class CarState(CarStateBase):
|
||||
def get_cam_can_parser(CP):
|
||||
signals = [
|
||||
# sig_name, sig_address
|
||||
("CmbbDeny_B_Actl", "ACCDATA"), # ACC/AEB unavailable/lockout
|
||||
|
||||
("CmbbBrkDecel_B_Rq", "ACCDATA_2"), # AEB actuation request bit
|
||||
|
||||
("HaDsply_No_Cs", "ACCDATA_3"),
|
||||
@@ -264,9 +280,20 @@ class CarState(CarStateBase):
|
||||
|
||||
checks = [
|
||||
# sig_address, frequency
|
||||
("ACCDATA", 50),
|
||||
("ACCDATA_2", 50),
|
||||
("ACCDATA_3", 5),
|
||||
("IPMA_Data", 1),
|
||||
]
|
||||
|
||||
if CP.enableBsm and CP.carFingerprint in CANFD_CAR:
|
||||
signals += [
|
||||
("SodDetctLeft_D_Stat", "Side_Detect_L_Stat"), # Blindspot sensor, left
|
||||
("SodDetctRight_D_Stat", "Side_Detect_R_Stat"), # Blindspot sensor, right
|
||||
]
|
||||
checks += [
|
||||
("Side_Detect_L_Stat", 5),
|
||||
("Side_Detect_R_Stat", 5),
|
||||
]
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus(CP).camera)
|
||||
|
||||
@@ -13,15 +13,15 @@ class CanBus(CanBusBase):
|
||||
return self.offset
|
||||
|
||||
@property
|
||||
def radar(self):
|
||||
def radar(self) -> int:
|
||||
return self.offset + 1
|
||||
|
||||
@property
|
||||
def camera(self):
|
||||
def camera(self) -> int:
|
||||
return self.offset + 2
|
||||
|
||||
|
||||
def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray):
|
||||
def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray) -> int:
|
||||
curvature = (dat[2] << 3) | ((dat[3]) >> 5)
|
||||
curvature_rate = (dat[6] << 3) | ((dat[7]) >> 5)
|
||||
path_angle = ((dat[3] & 0x1F) << 6) | ((dat[4]) >> 2)
|
||||
@@ -142,8 +142,8 @@ def create_acc_msg(packer, CAN: CanBus, long_active: bool, gas: float, accel: fl
|
||||
return packer.make_can_msg("ACCDATA", CAN.main, values)
|
||||
|
||||
|
||||
def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, standstill: bool, hud_control,
|
||||
stock_values: dict):
|
||||
def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, fcw_alert: bool, standstill: bool,
|
||||
hud_control, stock_values: dict):
|
||||
"""
|
||||
Creates a CAN message for the Ford IPC adaptive cruise, forward collision warning and traffic jam
|
||||
assist status.
|
||||
@@ -214,6 +214,10 @@ def create_acc_ui_msg(packer, CAN: CanBus, CP, main_on: bool, enabled: bool, sta
|
||||
"AccTGap_D_Dsply": 4, # Fixed time gap in UI
|
||||
})
|
||||
|
||||
# Forwards FCW alert from IPMA
|
||||
if fcw_alert:
|
||||
values["FcwVisblWarn_B_Rq"] = 1 # FCW visible alert
|
||||
|
||||
return packer.make_can_msg("ACCDATA_3", CAN.main, values)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from panda import Panda
|
||||
from common.conversions import Conversions as CV
|
||||
from selfdrive.car import STD_CARGO_KG, get_safety_config
|
||||
from selfdrive.car.ford.fordcan import CanBus
|
||||
from selfdrive.car.ford.values import CAR, Ecu
|
||||
from selfdrive.car.ford.values import CANFD_CAR, CAR, Ecu
|
||||
from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
@@ -15,11 +15,7 @@ class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
|
||||
ret.carName = "ford"
|
||||
|
||||
# These cars are dashcam only for lack of test coverage.
|
||||
# Once a user confirms each car works and a test route is
|
||||
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
|
||||
ret.dashcamOnly = candidate in {CAR.FOCUS_MK4}
|
||||
ret.dashcamOnly = candidate in {CAR.F_150_MK14}
|
||||
|
||||
ret.radarUnavailable = True
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
@@ -37,6 +33,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_LONG_CONTROL
|
||||
ret.openpilotLongitudinalControl = True
|
||||
|
||||
if candidate in CANFD_CAR:
|
||||
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_FORD_CANFD
|
||||
|
||||
if candidate == CAR.BRONCO_SPORT_MK1:
|
||||
ret.wheelbase = 2.67
|
||||
ret.steerRatio = 17.7
|
||||
@@ -52,6 +51,12 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerRatio = 16.8
|
||||
ret.mass = 2050 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.F_150_MK14:
|
||||
# required trim only on SuperCrew
|
||||
ret.wheelbase = 3.69
|
||||
ret.steerRatio = 17.0
|
||||
ret.mass = 2000 + STD_CARGO_KG
|
||||
|
||||
elif candidate == CAR.FOCUS_MK4:
|
||||
ret.wheelbase = 2.7
|
||||
ret.steerRatio = 15.0
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List, Set, Union
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from cereal import car
|
||||
from selfdrive.car import AngleRateLimit, dbc_dict
|
||||
from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts, Device
|
||||
from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \
|
||||
Device
|
||||
from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -42,11 +44,12 @@ class CAR:
|
||||
BRONCO_SPORT_MK1 = "FORD BRONCO SPORT 1ST GEN"
|
||||
ESCAPE_MK4 = "FORD ESCAPE 4TH GEN"
|
||||
EXPLORER_MK6 = "FORD EXPLORER 6TH GEN"
|
||||
F_150_MK14 = "FORD F-150 14TH GEN"
|
||||
FOCUS_MK4 = "FORD FOCUS 4TH GEN"
|
||||
MAVERICK_MK1 = "FORD MAVERICK 1ST GEN"
|
||||
|
||||
|
||||
CANFD_CARS: Set[str] = set()
|
||||
CANFD_CAR = {CAR.F_150_MK14}
|
||||
|
||||
|
||||
class RADAR:
|
||||
@@ -56,11 +59,22 @@ class RADAR:
|
||||
|
||||
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base_pt", RADAR.DELPHI_MRR))
|
||||
|
||||
# F-150 radar is not yet supported
|
||||
DBC[CAR.F_150_MK14] = dbc_dict("ford_lincoln_base_pt", None)
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
FOCUS = CarFootnote(
|
||||
"Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in " +
|
||||
"North and South America/Southeast Asia.",
|
||||
Column.MODEL,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordCarInfo(CarInfo):
|
||||
package: str = "Co-Pilot360 Assist+"
|
||||
car_parts: CarParts = CarParts.common([CarHarness.ford_q3])
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.ford_q3]))
|
||||
|
||||
def init_make(self, CP: car.CarParams):
|
||||
if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1):
|
||||
@@ -77,22 +91,28 @@ CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = {
|
||||
FordCarInfo("Ford Explorer 2020-22"),
|
||||
FordCarInfo("Lincoln Aviator 2020-21", "Co-Pilot360 Plus"),
|
||||
],
|
||||
CAR.FOCUS_MK4: FordCarInfo("Ford Focus EU 2018", "Adaptive Cruise Control with Lane Centering"),
|
||||
CAR.F_150_MK14: FordCarInfo("Ford F-150 2023", "Co-Pilot360 Active 2.0"),
|
||||
CAR.FOCUS_MK4: FordCarInfo("Ford Focus 2018", "Adaptive Cruise Control with Lane Centering", footnotes=[Footnote.FOCUS]),
|
||||
CAR.MAVERICK_MK1: FordCarInfo("Ford Maverick 2022-23", "Co-Pilot360 Assist"),
|
||||
}
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[
|
||||
# CAN and CAN FD queries are combined.
|
||||
# FIXME: For CAN FD, ECUs respond with frames larger than 8 bytes on the powertrain bus
|
||||
# TODO: properly handle auxiliary requests to separate queries and add back whitelists
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.engine],
|
||||
# whitelist_ecus=[Ecu.engine],
|
||||
auxiliary=True,
|
||||
),
|
||||
Request(
|
||||
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
# whitelist_ecus=[Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.shiftByWire],
|
||||
bus=0,
|
||||
whitelist_ecus=[Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.shiftByWire],
|
||||
auxiliary=True,
|
||||
),
|
||||
],
|
||||
extra_ecus=[
|
||||
@@ -119,6 +139,7 @@ FW_VERSIONS = {
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'M1PA-14C204-GF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'N1PA-14C204-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'N1PA-14C204-AD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.ESCAPE_MK4: {
|
||||
@@ -129,6 +150,7 @@ FW_VERSIONS = {
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'LX6C-2D053-NS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-NT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-NY\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6C-2D053-SA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -142,9 +164,11 @@ FW_VERSIONS = {
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'LX6A-14C204-BJV\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6A-14C204-BJX\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6A-14C204-CNG\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'LX6A-14C204-ESG\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MX6A-14C204-BEF\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MX6A-14C204-BEJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MX6A-14C204-CAB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NX6A-14C204-BLE\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
@@ -154,6 +178,7 @@ FW_VERSIONS = {
|
||||
b'L1MC-14D003-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'L1MC-14D003-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1MC-14D003-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'M1MC-14D003-AC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'L1MC-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -178,9 +203,27 @@ FW_VERSIONS = {
|
||||
b'LB5A-14C204-EAC\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MB5A-14C204-MD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'MB5A-14C204-RC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NB5A-14C204-AZD\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NB5A-14C204-HB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.F_150_MK14: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.engine, 0x7E0, None): [
|
||||
b'PL3A-14C204-BRB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FOCUS_MK4: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'JX6C-14D003-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
|
||||
@@ -85,6 +85,7 @@ class CarController:
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
if not CC.longActive:
|
||||
# ASCM sends max regen when not enabled
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
@@ -92,6 +93,10 @@ class CarController:
|
||||
else:
|
||||
self.apply_gas = int(round(interp(actuators.accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(actuators.accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
# Don't allow any gas above inactive regen while stopping
|
||||
# FIXME: brakes aren't applied immediately when enabling at a stop
|
||||
if stopping:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
|
||||
idx = (self.frame // 4) % 4
|
||||
|
||||
@@ -101,7 +106,7 @@ class CarController:
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and actuators.longControlState == LongCtrlState.stopping
|
||||
at_full_stop = at_full_stop and stopping
|
||||
friction_brake_bus = CanBus.POWERTRAIN
|
||||
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
|
||||
@@ -19,6 +19,12 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
|
||||
CruiseButtons.MAIN: ButtonType.altButton3, CruiseButtons.CANCEL: ButtonType.cancel}
|
||||
|
||||
|
||||
NON_LINEAR_TORQUE_PARAMS = {
|
||||
CAR.BOLT_EUV: [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178],
|
||||
CAR.ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772]
|
||||
}
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
@@ -31,23 +37,14 @@ class CarInterface(CarInterfaceBase):
|
||||
sigmoid = desired_angle / (1 + fabs(desired_angle))
|
||||
return 0.10006696 * sigmoid * (v_ego + 3.12485927)
|
||||
|
||||
@staticmethod
|
||||
def get_steer_feedforward_acadia(desired_angle, v_ego):
|
||||
desired_angle *= 0.09760208
|
||||
sigmoid = desired_angle / (1 + fabs(desired_angle))
|
||||
return 0.04689655 * sigmoid * (v_ego + 10.028217)
|
||||
|
||||
def get_steer_feedforward_function(self):
|
||||
if self.CP.carFingerprint == CAR.VOLT:
|
||||
return self.get_steer_feedforward_volt
|
||||
elif self.CP.carFingerprint == CAR.ACADIA:
|
||||
return self.get_steer_feedforward_acadia
|
||||
else:
|
||||
return CarInterfaceBase.get_steer_feedforward_default
|
||||
|
||||
@staticmethod
|
||||
def torque_from_lateral_accel_bolt(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
|
||||
def torque_from_lateral_accel_siglin(self, lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
|
||||
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
|
||||
|
||||
def sig(val):
|
||||
@@ -57,14 +54,15 @@ class CarInterface(CarInterfaceBase):
|
||||
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
# ToDo: To generalize to other GMs, explore tanh function as the nonlinear
|
||||
a, b, c, _ = [2.6531724862969748, 1.0, 0.1919764879840985, 0.009054123646805178] # weights computed offline
|
||||
|
||||
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
a, b, c, _ = non_linear_torque_params
|
||||
steer_torque = (sig(lateral_accel_value * a) * b) + (lateral_accel_value * c)
|
||||
return float(steer_torque) + friction
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
if self.CP.carFingerprint == CAR.BOLT_EUV:
|
||||
return self.torque_from_lateral_accel_bolt
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
return self.torque_from_lateral_accel_siglin
|
||||
else:
|
||||
return self.torque_from_lateral_accel_linear
|
||||
|
||||
@@ -169,7 +167,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelbase = 2.86
|
||||
ret.steerRatio = 14.4 # end to end is 13.46
|
||||
ret.centerToFront = ret.wheelbase * 0.4
|
||||
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_acadia()
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_LACROSSE:
|
||||
ret.mass = 1712. + STD_CARGO_KG
|
||||
@@ -224,6 +223,11 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerRatio = 16.3
|
||||
ret.centerToFront = ret.wheelbase * 0.5
|
||||
tire_stiffness_factor = 1.0
|
||||
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
|
||||
# with foot on brake to allow engagement, but this platform only has that check in the camera.
|
||||
# TODO: check if this is split by EV/ICE with more platforms in the future
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.EQUINOX:
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
from parameterized import parameterized
|
||||
import unittest
|
||||
|
||||
from selfdrive.car.gm.values import CAMERA_ACC_CAR, CAR, FINGERPRINTS, GM_RX_OFFSET
|
||||
|
||||
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
|
||||
|
||||
|
||||
class TestGMFingerprint(unittest.TestCase):
|
||||
@parameterized.expand(FINGERPRINTS.items())
|
||||
def test_can_fingerprints(self, car_model, fingerprints):
|
||||
self.assertGreater(len(fingerprints), 0)
|
||||
|
||||
# Trailblazer is in dashcam
|
||||
if car_model != CAR.TRAILBLAZER:
|
||||
self.assertTrue(all(len(finger) for finger in fingerprints))
|
||||
|
||||
# The camera can sometimes be communicating on startup
|
||||
if car_model in CAMERA_ACC_CAR - {CAR.TRAILBLAZER}:
|
||||
for finger in fingerprints:
|
||||
for required_addr in (CAMERA_DIAGNOSTIC_ADDRESS, CAMERA_DIAGNOSTIC_ADDRESS + GM_RX_OFFSET):
|
||||
self.assertEqual(finger.get(required_addr), 8, required_addr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -199,24 +199,26 @@ FINGERPRINTS = {
|
||||
}],
|
||||
CAR.BOLT_EUV: [
|
||||
{
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.SILVERADO: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.EQUINOX: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
# Trailblazer also matches as a Silverado, so comment out to avoid conflicts.
|
||||
# TODO: split with FW versions
|
||||
CAR.TRAILBLAZER: [
|
||||
{
|
||||
# 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
# 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1609: 8, 1611: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
}],
|
||||
}
|
||||
|
||||
GM_RX_OFFSET = 0x400
|
||||
|
||||
DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis'))
|
||||
|
||||
EV_CAR = {CAR.VOLT, CAR.BOLT_EUV}
|
||||
|
||||
@@ -229,7 +229,7 @@ class CarController:
|
||||
# cruise cancel
|
||||
if CC.cruiseControl.cancel:
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
|
||||
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CAN, CS.cruise_info))
|
||||
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
|
||||
self.last_button_frame = self.frame
|
||||
else:
|
||||
for _ in range(20):
|
||||
|
||||
@@ -666,7 +666,6 @@ class CarState(CarStateBase):
|
||||
("NEW_SIGNAL_1", "SCC_CONTROL"),
|
||||
("MainMode_ACC", "SCC_CONTROL"),
|
||||
("ACCMode", "SCC_CONTROL"),
|
||||
("CRUISE_INACTIVE", "SCC_CONTROL"),
|
||||
("ZEROS_9", "SCC_CONTROL"),
|
||||
("CRUISE_STANDSTILL", "SCC_CONTROL"),
|
||||
("ZEROS_5", "SCC_CONTROL"),
|
||||
|
||||
@@ -8,7 +8,23 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
left_lane, right_lane,
|
||||
left_lane_depart, right_lane_depart,
|
||||
lateral_paused, blinking_icon):
|
||||
values = lkas11
|
||||
values = {s: lkas11[s] for s in [
|
||||
"CF_Lkas_LdwsActivemode",
|
||||
"CF_Lkas_LdwsSysState",
|
||||
"CF_Lkas_SysWarning",
|
||||
"CF_Lkas_LdwsLHWarning",
|
||||
"CF_Lkas_LdwsRHWarning",
|
||||
"CF_Lkas_HbaLamp",
|
||||
"CF_Lkas_FcwBasReq",
|
||||
"CF_Lkas_HbaSysState",
|
||||
"CF_Lkas_FcwOpt",
|
||||
"CF_Lkas_HbaOpt",
|
||||
"CF_Lkas_FcwSysState",
|
||||
"CF_Lkas_FcwCollisionWarning",
|
||||
"CF_Lkas_FusionState",
|
||||
"CF_Lkas_FcwOpt_USM",
|
||||
"CF_Lkas_LdwsOpt_USM",
|
||||
]}
|
||||
values["CF_Lkas_LdwsSysState"] = sys_state
|
||||
values["CF_Lkas_SysWarning"] = 3 if sys_warning else 0
|
||||
values["CF_Lkas_LdwsLHWarning"] = left_lane_depart
|
||||
@@ -82,7 +98,20 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
|
||||
|
||||
|
||||
def create_clu11(packer, frame, clu11, button, car_fingerprint):
|
||||
values = clu11
|
||||
values = {s: clu11[s] for s in [
|
||||
"CF_Clu_CruiseSwState",
|
||||
"CF_Clu_CruiseSwMain",
|
||||
"CF_Clu_SldMainSW",
|
||||
"CF_Clu_ParityBit1",
|
||||
"CF_Clu_VanzDecimal",
|
||||
"CF_Clu_Vanz",
|
||||
"CF_Clu_SPEED_UNIT",
|
||||
"CF_Clu_DetentOut",
|
||||
"CF_Clu_RheostatLevel",
|
||||
"CF_Clu_CluInfo",
|
||||
"CF_Clu_AmpInfo",
|
||||
"CF_Clu_AliveCnt1",
|
||||
]}
|
||||
values["CF_Clu_CruiseSwState"] = button
|
||||
values["CF_Clu_AliveCnt1"] = frame % 0x10
|
||||
# send buttons to camera on camera-scc based cars
|
||||
|
||||
@@ -60,11 +60,11 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer ,
|
||||
|
||||
return ret
|
||||
|
||||
def create_cam_0x2a4(packer, CAN, camera_values):
|
||||
camera_values.update({
|
||||
"BYTE7": 0,
|
||||
})
|
||||
return packer.make_can_msg("CAM_0x2a4", CAN.ACAN, camera_values)
|
||||
def create_cam_0x2a4(packer, CAN, cam_0x2a4):
|
||||
values = {f"BYTE{i}": cam_0x2a4[f"BYTE{i}"] for i in range(3, 24)}
|
||||
values['COUNTER'] = cam_0x2a4['COUNTER']
|
||||
values["BYTE7"] = 0
|
||||
return packer.make_can_msg("CAM_0x2a4", CAN.ACAN, values)
|
||||
|
||||
def create_buttons(packer, CP, CAN, cnt, btn):
|
||||
values = {
|
||||
@@ -76,10 +76,33 @@ def create_buttons(packer, CP, CAN, cnt, btn):
|
||||
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
|
||||
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
|
||||
|
||||
def create_acc_cancel(packer, CAN, cruise_info_copy):
|
||||
values = cruise_info_copy
|
||||
def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
|
||||
# TODO: why do we copy different values here?
|
||||
if CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value:
|
||||
values = {s: cruise_info_copy[s] for s in [
|
||||
"COUNTER",
|
||||
"CHECKSUM",
|
||||
"NEW_SIGNAL_1",
|
||||
"MainMode_ACC",
|
||||
"ACCMode",
|
||||
"ZEROS_9",
|
||||
"CRUISE_STANDSTILL",
|
||||
"ZEROS_5",
|
||||
"DISTANCE_SETTING",
|
||||
"VSetDis",
|
||||
]}
|
||||
else:
|
||||
values = {s: cruise_info_copy[s] for s in [
|
||||
"COUNTER",
|
||||
"CHECKSUM",
|
||||
"ACCMode",
|
||||
"VSetDis",
|
||||
"CRUISE_STANDSTILL",
|
||||
]}
|
||||
values.update({
|
||||
"ACCMode": 4,
|
||||
"aReqRaw": 0.0,
|
||||
"aReqValue": 0.0,
|
||||
})
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# These cars have been put into dashcam only due to both a lack of users and test coverage.
|
||||
# These cars likely still work fine. Once a user confirms each car works and a test route is
|
||||
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
|
||||
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
|
||||
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, CAR.IONIQ_6}
|
||||
|
||||
hda2 = Ecu.adas in [fw.ecu for fw in car_fw]
|
||||
CAN = CanBus(None, hda2, fingerprint)
|
||||
@@ -192,10 +192,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.wheelbase = 2.9
|
||||
ret.steerRatio = 16.
|
||||
tire_stiffness_factor = 0.65
|
||||
elif candidate == CAR.IONIQ_5:
|
||||
ret.mass = 2012 + STD_CARGO_KG
|
||||
ret.wheelbase = 3.0
|
||||
ret.steerRatio = 16.
|
||||
elif candidate in (CAR.IONIQ_5, CAR.IONIQ_6):
|
||||
ret.mass = 1948 + STD_CARGO_KG
|
||||
ret.wheelbase = 2.97
|
||||
ret.steerRatio = 14.26
|
||||
tire_stiffness_factor = 0.65
|
||||
elif candidate == CAR.KIA_SPORTAGE_HYBRID_5TH_GEN:
|
||||
ret.mass = 1767. + STD_CARGO_KG # SX Prestige trim support only
|
||||
@@ -208,6 +208,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.mass = 3957 * CV.LB_TO_KG + STD_CARGO_KG
|
||||
else:
|
||||
ret.mass = 4537 * CV.LB_TO_KG + STD_CARGO_KG
|
||||
elif candidate == CAR.KIA_CARNIVAL_4TH_GEN:
|
||||
ret.mass = 2087. + STD_CARGO_KG
|
||||
ret.wheelbase = 3.09
|
||||
ret.steerRatio = 14.23
|
||||
|
||||
# Genesis
|
||||
elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN:
|
||||
|
||||
@@ -52,6 +52,20 @@ class TestHyundaiFingerprint(unittest.TestCase):
|
||||
self.assertEqual(len(ecus_not_in_whitelist), 0,
|
||||
f"{car_model}: Car model has ECUs not in auxiliary request whitelists: {ecu_strings}")
|
||||
|
||||
def test_blacklisted_parts(self):
|
||||
# Asserts no ECUs known to be shared across platforms exist in the database.
|
||||
# Tucson having Santa Cruz camera and EPS for example
|
||||
for car_model, ecus in FW_VERSIONS.items():
|
||||
with self.subTest(car_model=car_model):
|
||||
if car_model == CAR.SANTA_CRUZ_1ST_GEN:
|
||||
raise unittest.SkipTest("Skip checking Santa Cruz for its parts")
|
||||
|
||||
for code, _ in get_platform_codes(ecus[(Ecu.fwdCamera, 0x7c4, None)]):
|
||||
if b"-" not in code:
|
||||
continue
|
||||
part = code.split(b"-")[1]
|
||||
self.assertFalse(part.startswith(b'CW'), "Car has bad part number")
|
||||
|
||||
# Tests for platform codes, part numbers, and FW dates which Hyundai will use to fuzzy
|
||||
# fingerprint in the absence of full FW matches:
|
||||
def test_platform_code_ecus_available(self):
|
||||
|
||||
@@ -99,6 +99,7 @@ class CAR:
|
||||
VELOSTER = "HYUNDAI VELOSTER 2019"
|
||||
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
|
||||
IONIQ_5 = "HYUNDAI IONIQ 5 2022"
|
||||
IONIQ_6 = "HYUNDAI IONIQ 6 2023"
|
||||
TUCSON_4TH_GEN = "HYUNDAI TUCSON 4TH GEN"
|
||||
TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN"
|
||||
SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN"
|
||||
@@ -127,6 +128,7 @@ class CAR:
|
||||
KIA_STINGER_2022 = "KIA STINGER 2022"
|
||||
KIA_CEED = "KIA CEED INTRO ED 2019"
|
||||
KIA_EV6 = "KIA EV6 2022"
|
||||
KIA_CARNIVAL_4TH_GEN = "KIA CARNIVAL 4TH GEN"
|
||||
|
||||
# Genesis
|
||||
GENESIS_GV60_EV_1ST_GEN = "GENESIS GV60 ELECTRIC 1ST GEN"
|
||||
@@ -179,7 +181,7 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
|
||||
CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", car_parts=CarParts.common([CarHarness.hyundai_o])),
|
||||
CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages
|
||||
CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
|
||||
CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", video_link="https://youtu.be/VnHzSTygTS4", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
@@ -193,12 +195,16 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
|
||||
HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
|
||||
],
|
||||
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])),
|
||||
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
CAR.IONIQ_5: [
|
||||
HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
],
|
||||
CAR.IONIQ_6: [
|
||||
HyundaiCarInfo("Hyundai Ioniq 6 (without HDA II) 2023", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), # TODO: unknown
|
||||
HyundaiCarInfo("Hyundai Ioniq 6 (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])),
|
||||
],
|
||||
CAR.TUCSON_4TH_GEN: [
|
||||
HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])),
|
||||
HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
|
||||
@@ -251,6 +257,10 @@ CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
|
||||
HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
|
||||
],
|
||||
CAR.KIA_CARNIVAL_4TH_GEN: [
|
||||
HyundaiCarInfo("Kia Carnival 2023", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k]))
|
||||
],
|
||||
|
||||
# Genesis
|
||||
CAR.GENESIS_GV60_EV_1ST_GEN: [
|
||||
@@ -648,6 +658,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00DN8_ SCC F-CUP 1.00 1.02 99110-L1000 ',
|
||||
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.00 99110-L0000 ',
|
||||
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ',
|
||||
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.02 99110-L1000 ',
|
||||
],
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00DN ESC \x07 106 \x07\x01 58910-L0100',
|
||||
@@ -662,6 +673,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x8758910-L0100\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100',
|
||||
b'\xf1\x8758910-L0100\xf1\x00DN ESC \x07 104\x19\x08\x01 58910-L0100',
|
||||
b'\xf1\x00DN ESC \x06 106 \x07\x01 58910-L0100',
|
||||
b'\xf1\x00DN ESC \x06 107 \x07\x03 58910-L1300',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xf1\x81HM6M1_0a0_F00',
|
||||
@@ -676,6 +688,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x87391162M003',
|
||||
b'\xf1\x87391162M013',
|
||||
b'\xf1\x87391162M023',
|
||||
b'\xf1\x87391162M010',
|
||||
b'HM6M1_0a0_F00',
|
||||
b'HM6M1_0a0_G20',
|
||||
b'HM6M2_0a0_BD0',
|
||||
@@ -699,11 +712,13 @@ FW_VERSIONS = {
|
||||
b'\xf1\x8756310L0210\x00\xf1\x00DN8 MDPS C 1.00 1.01 56310L0210\x00 4DNAC101',
|
||||
b'\xf1\x8757700-L0000\xf1\x00DN8 MDPS R 1.00 1.00 57700-L0000 4DNAP100',
|
||||
b'\xf1\x00DN8 MDPS R 1.00 1.00 57700-L0000 4DNAP101',
|
||||
b'\xf1\x00DN8 MDPS R 1.00 1.02 57700-L1000 4DNDP105',
|
||||
b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0210 4DNAC102',
|
||||
b'\xf1\x00DN8 MDPS C 1.00 1.01 56310L0200\x00 4DNAC102',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.02 99211-L1000 190422',
|
||||
b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.04 99211-L1000 191016',
|
||||
b'\xf1\x00DN8 MFC AT RUS LHD 1.00 1.03 99211-L1000 190705',
|
||||
b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.00 99211-L0000 190716',
|
||||
b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.01 99211-L0000 191016',
|
||||
@@ -716,6 +731,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16NB0z{\xd4v',
|
||||
b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB1\xe3\xc10\xa1',
|
||||
b'\xf1\x00bcsh8p54 U913\x00\x00\x00\x00\x00\x00SDN8T16NB2\n\xdd^\xbc',
|
||||
b'\xf1\x00bcsh8p54 U903\x00\x00\x00\x00\x00\x00SDN8T16KB05\x95h%',
|
||||
b'\xf1\x00HT6TA260BLHT6TA800A1TDN8C20KS4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\xf1\x00HT6TA260BLHT6TA810A1TDN8M25GS0\x00\x00\x00\x00\x00\x00\xaa\x8c\xd9p',
|
||||
b'\xf1\x00HT6WA250BLHT6WA910A1SDN8G25NB1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -901,6 +917,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x8758910-S2GA0\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
|
||||
b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
|
||||
b'\xf1\x00TM ESC \x04 101 \x08\x04 58910-S2GA0',
|
||||
b'\xf1\x00TM ESC \x02 103"\x07\x08 58910-S2GA0',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M1_0a0_L50',
|
||||
@@ -913,6 +930,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82TMDWN5TMD3TXXJ1A',
|
||||
b'\xf1\x81HM6M2_0a0_G00',
|
||||
b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81HM6M1_0a0_J10',
|
||||
b'\xf1\x8739101-2STN8\xf1\x81HM6M1_0a0_M00',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00TM MDPS C 1.00 1.02 56370-S2AA0 0B19',
|
||||
@@ -923,6 +941,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00TMA MFC AT USA LHD 1.00 1.00 99211-S2500 200720',
|
||||
b'\xf1\x00TM MFC AT EUR LHD 1.00 1.03 99211-S1500 210224',
|
||||
b'\xf1\x00TMA MFC AT USA LHD 1.00 1.01 99211-S2500 210205',
|
||||
b'\xf1\x00TMA MFC AT USA LHD 1.00 1.03 99211-S2500 220414',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\x00HT6WA280BLHT6WAD00A1STM2G25NH2\x00\x00\x00\x00\x00\x00\xf8\xc0\xc3\xaa',
|
||||
@@ -938,6 +957,7 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc',
|
||||
b'\xf1\x87954A02N250\x00\x00\x00\x00\x00\xf1\x81T02900A1 \xf1\x00T02601BL T02900A1 VTMPT25XXX900NS8\xb7\xaa\xfe\xfc',
|
||||
b'\xf1\x00T02601BL T02800A1 VTMPT25XXX800NS4\xed\xaf\xed\xf5',
|
||||
b'\xf1\x00T02601BL T02900A1 VTMPT25XXW900NS1c\x918\xc5',
|
||||
],
|
||||
},
|
||||
CAR.SANTA_FE_HEV_2022: {
|
||||
@@ -1743,8 +1763,8 @@ FW_VERSIONS = {
|
||||
},
|
||||
CAR.SONATA_HYBRID: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\000DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
|
||||
b'\xf1\x8799110L5000\xf1\000DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
|
||||
b'\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
|
||||
b'\xf1\x8799110L5000\xf1\x00DNhe SCC FHCUP 1.00 1.02 99110-L5000 ',
|
||||
b'\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
|
||||
b'\xf1\x8799110L5000\xf1\000DNhe SCC F-CUP 1.00 1.02 99110-L5000 ',
|
||||
],
|
||||
@@ -1752,23 +1772,27 @@ FW_VERSIONS = {
|
||||
b'\xf1\x8756310-L5500\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5500 4DNHC102',
|
||||
b'\xf1\x8756310-L5450\xf1\x00DN8 MDPS C 1.00 1.02 56310-L5450 4DNHC102',
|
||||
b'\xf1\x8756310-L5450\xf1\000DN8 MDPS C 1.00 1.03 56310-L5450 4DNHC103',
|
||||
b'\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104',
|
||||
b'\xf1\x8756310L5450\x00\xf1\x00DN8 MDPS C 1.00 1.03 56310L5450\x00 4DNHC104',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.04 99211-L1000 191016',
|
||||
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.05 99211-L1000 201109',
|
||||
b'\xf1\000DN8HMFC AT USA LHD 1.00 1.06 99211-L1000 210325',
|
||||
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.07 99211-L1000 211223',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'\xf1\000PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TDN2H20SA6N\xc2\xeeW',
|
||||
b'\xf1\x87959102T250\x00\x00\x00\x00\x00\xf1\x81E09\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2323 E09\x00\x00\x00\x00\x00\x00\x00TDN2H20SA5\x97R\x88\x9e',
|
||||
b'\xf1\000PSBG2323 E09\000\000\000\000\000\000\000TDN2H20SA5\x97R\x88\x9e',
|
||||
b'\xf1\000PSBG2333 E16\000\000\000\000\000\000\000TDN2H20SA7\0323\xf9\xab',
|
||||
b'\xf1\x87PCU\000\000\000\000\000\000\000\000\000\xf1\x81E16\000\000\000\000\000\000\000\xf1\000PSBG2333 E16\000\000\000\000\000\000\000TDN2H20SA7\0323\xf9\xab',
|
||||
b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TDN2H20SA7\x1a3\xf9\xab',
|
||||
b'\xf1\x87PCU\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x81E16\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TDN2H20SA7\x1a3\xf9\xab',
|
||||
b'\xf1\x87959102T250\x00\x00\x00\x00\x00\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TDN2H20SA6N\xc2\xeeW',
|
||||
],
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'\xf1\x87391162J012',
|
||||
b'\xf1\x87391162J013',
|
||||
b'\xf1\x87391162J014',
|
||||
b'\xf1\x87391062J002',
|
||||
],
|
||||
},
|
||||
@@ -1828,11 +1852,18 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401',
|
||||
],
|
||||
},
|
||||
CAR.IONIQ_6: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CE__ RDR ----- 1.00 1.01 99110-KL000 ',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213',
|
||||
],
|
||||
},
|
||||
CAR.TUCSON_4TH_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9210 14G',
|
||||
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T',
|
||||
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-CW010 14X',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
|
||||
@@ -1930,6 +1961,16 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00JX1_ SCC FHCUP 1.00 1.01 99110-T6100 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CARNIVAL_4TH_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.06 99210-R0000 220221',
|
||||
b'\xf1\x00KA4CMFC AT CHN LHD 1.00 1.01 99211-I4000 210525',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00KA4_ SCC FHCUP 1.00 1.03 99110-R0000 ',
|
||||
b'\xf1\x00KA4c SCC FHCUP 1.00 1.01 99110-I4000 ',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
CHECKSUM = {
|
||||
@@ -1944,16 +1985,16 @@ CAN_GEARS = {
|
||||
"use_elect_gears": {CAR.KIA_NIRO_EV, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_OPTIMA_H, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.IONIQ, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.KONA_EV_2022, CAR.KIA_K5_HEV_2020},
|
||||
}
|
||||
|
||||
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN, CAR.GENESIS_GV80}
|
||||
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN, CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN}
|
||||
|
||||
# The radar does SCC on these cars when HDA I, rather than the camera
|
||||
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80}
|
||||
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN}
|
||||
|
||||
# The camera does SCC on these cars, rather than the radar
|
||||
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
|
||||
|
||||
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020, CAR.KIA_NIRO_HEV_2ND_GEN} # these cars use a different gas signal
|
||||
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.GENESIS_GV60_EV_1ST_GEN}
|
||||
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022, CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN}
|
||||
|
||||
# these cars require a special panda safety mode due to missing counters and checksums in the messages
|
||||
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV, CAR.IONIQ, CAR.KONA_EV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.VELOSTER,
|
||||
@@ -2016,6 +2057,7 @@ DBC = {
|
||||
CAR.TUCSON_4TH_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
|
||||
CAR.IONIQ_6: dbc_dict('hyundai_canfd', None),
|
||||
CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: dbc_dict('hyundai_canfd', None),
|
||||
@@ -2026,4 +2068,5 @@ DBC = {
|
||||
CAR.KIA_NIRO_HEV_2ND_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.KIA_NIRO_EV_2ND_GEN: dbc_dict('hyundai_canfd', None),
|
||||
CAR.GENESIS_GV80: dbc_dict('hyundai_canfd', None),
|
||||
CAR.KIA_CARNIVAL_4TH_GEN: dbc_dict('hyundai_canfd', None),
|
||||
}
|
||||
|
||||
@@ -164,8 +164,7 @@ class CarInterfaceBase(ABC):
|
||||
def get_steer_feedforward_function(self):
|
||||
return self.get_steer_feedforward_default
|
||||
|
||||
@staticmethod
|
||||
def torque_from_lateral_accel_linear(lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
|
||||
def torque_from_lateral_accel_linear(self, lateral_accel_value: float, torque_params: car.CarParams.LateralTorqueTuning,
|
||||
lateral_accel_error: float, lateral_accel_deadzone: float, friction_compensation: bool) -> float:
|
||||
# The default is a linear relationship between torque and lateral acceleration (accounting for road roll and steering friction)
|
||||
friction = get_friction(lateral_accel_error, lateral_accel_deadzone, FRICTION_THRESHOLD, torque_params, friction_compensation)
|
||||
|
||||
@@ -115,7 +115,13 @@ class IsoTpParallelQuery:
|
||||
addrs_responded.add(tx_addr)
|
||||
response_timeouts[tx_addr] = time.monotonic() + timeout
|
||||
|
||||
if not dat:
|
||||
if dat is None:
|
||||
continue
|
||||
|
||||
# Log unexpected empty responses
|
||||
if len(dat) == 0:
|
||||
cloudlog.error(f"iso-tp query empty response: {tx_addr}")
|
||||
request_done[tx_addr] = True
|
||||
continue
|
||||
|
||||
counter = request_counter[tx_addr]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from cereal import car
|
||||
@@ -45,7 +45,7 @@ BUTTON_STATES = {
|
||||
@dataclass
|
||||
class MazdaCarInfo(CarInfo):
|
||||
package: str = "All"
|
||||
car_parts: CarParts = CarParts.common([CarHarness.mazda])
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.mazda]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = {
|
||||
@@ -78,6 +78,13 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
),
|
||||
# Log responses on powertrain bus
|
||||
Request(
|
||||
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
|
||||
[StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
|
||||
bus=0,
|
||||
logging=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -89,6 +96,7 @@ FW_VERSIONS = {
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'PX2G-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX2H-188K2-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX85-188K2-E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'SH54-188K2-D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXFG-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -101,10 +109,12 @@ FW_VERSIONS = {
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'GSH7-67XK2-S\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'PYB2-21PS1-H\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'SH51-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXDL-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXFG-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
@@ -186,6 +196,7 @@ FW_VERSIONS = {
|
||||
(Ecu.engine, 0x7e0, None): [
|
||||
b'PX23-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PX24-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXM4-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXN8-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXN8-188K2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PYD7-188K2-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -210,10 +221,12 @@ FW_VERSIONS = {
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'B61L-67XK2-P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'B61L-67XK2-V\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-J\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'GSH7-67XK2-K\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'TK80-67XK2-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.transmission, 0x7e1, None): [
|
||||
b'PXM4-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXM7-21PS1-A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PXM7-21PS1-B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PYFM-21PS1-C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from cereal import car
|
||||
@@ -33,7 +33,7 @@ class CAR:
|
||||
@dataclass
|
||||
class NissanCarInfo(CarInfo):
|
||||
package: str = "ProPILOT Assist"
|
||||
car_parts: CarParts = CarParts.common([CarHarness.nissan_a])
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.nissan_a]))
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = {
|
||||
@@ -79,8 +79,8 @@ FINGERPRINTS = {
|
||||
]
|
||||
}
|
||||
|
||||
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0xc0])
|
||||
NISSAN_DIAGNOSTIC_RESPONSE_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, 0xc0])
|
||||
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0x81])
|
||||
NISSAN_DIAGNOSTIC_RESPONSE_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, 0x81])
|
||||
|
||||
NISSAN_VERSION_REQUEST_KWP = b'\x21\x83'
|
||||
NISSAN_VERSION_RESPONSE_KWP = b'\x61\x83'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from opendbc.can.packer import CANPacker
|
||||
from selfdrive.car import apply_driver_steer_torque_limits
|
||||
from selfdrive.car.subaru import subarucan
|
||||
from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CarControllerParams, SubaruFlags
|
||||
from selfdrive.car.subaru.values import DBC, GLOBAL_GEN2, PREGLOBAL_CARS, CanBus, CarControllerParams, SubaruFlags
|
||||
|
||||
|
||||
class CarController:
|
||||
@@ -44,7 +44,6 @@ class CarController:
|
||||
|
||||
|
||||
# *** alerts and pcm cancel ***
|
||||
|
||||
if self.CP.carFingerprint in PREGLOBAL_CARS:
|
||||
if self.frame % 5 == 0:
|
||||
# 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep
|
||||
@@ -66,7 +65,7 @@ class CarController:
|
||||
|
||||
else:
|
||||
if pcm_cancel_cmd and (self.frame - self.last_cancel_frame) > 0.2:
|
||||
bus = 1 if self.CP.carFingerprint in GLOBAL_GEN2 else 0
|
||||
bus = CanBus.alt if self.CP.carFingerprint in GLOBAL_GEN2 else CanBus.main
|
||||
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg, bus, pcm_cancel_cmd))
|
||||
self.last_cancel_frame = self.frame
|
||||
|
||||
@@ -78,7 +77,7 @@ class CarController:
|
||||
hud_control.leftLaneDepart, hud_control.rightLaneDepart))
|
||||
|
||||
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
can_sends.append(subarucan.create_infotainmentstatus(self.packer, CS.es_infotainmentstatus_msg, hud_control.visualAlert))
|
||||
can_sends.append(subarucan.create_es_infotainment(self.packer, CS.es_infotainment_msg, hud_control.visualAlert))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can.can_define import CANDefine
|
||||
from common.conversions import Conversions as CV
|
||||
from selfdrive.car.interfaces import CarStateBase
|
||||
from opendbc.can.parser import CANParser
|
||||
from selfdrive.car.subaru.values import DBC, CAR, GLOBAL_GEN2, PREGLOBAL_CARS, SubaruFlags
|
||||
from selfdrive.car.subaru.values import DBC, CAR, GLOBAL_GEN2, PREGLOBAL_CARS, CanBus, SubaruFlags
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
@@ -84,6 +84,7 @@ class CarState(CarStateBase):
|
||||
cp.vl["BodyInfo"]["DOOR_OPEN_FL"]])
|
||||
ret.steerFaultPermanent = cp.vl["Steering_Torque"]["Steer_Error_1"] == 1
|
||||
|
||||
cp_es_distance = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
|
||||
if self.car_fingerprint in PREGLOBAL_CARS:
|
||||
self.cruise_button = cp_cam.vl["ES_Distance"]["Cruise_Button"]
|
||||
self.ready = not cp_cam.vl["ES_DashStatus"]["Not_Ready_Startup"]
|
||||
@@ -91,19 +92,22 @@ class CarState(CarStateBase):
|
||||
ret.steerFaultTemporary = cp.vl["Steering_Torque"]["Steer_Warning"] == 1
|
||||
ret.cruiseState.nonAdaptive = cp_cam.vl["ES_DashStatus"]["Conventional_Cruise"] == 1
|
||||
ret.cruiseState.standstill = cp_cam.vl["ES_DashStatus"]["Cruise_State"] == 3
|
||||
ret.stockFcw = cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2
|
||||
ret.stockFcw = (cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 1) or \
|
||||
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
|
||||
# 8 is known AEB, there are a few other values related to AEB we ignore
|
||||
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
|
||||
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
|
||||
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
|
||||
|
||||
cp_es_distance = cp_body if self.car_fingerprint in GLOBAL_GEN2 else cp_cam
|
||||
self.es_distance_msg = copy.copy(cp_es_distance.vl["ES_Distance"])
|
||||
self.es_dashstatus_msg = copy.copy(cp_cam.vl["ES_DashStatus"])
|
||||
if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
self.es_infotainmentstatus_msg = copy.copy(cp_cam.vl["INFOTAINMENT_STATUS"])
|
||||
self.es_infotainment_msg = copy.copy(cp_cam.vl["ES_Infotainment"])
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_common_global_signals():
|
||||
def get_common_global_body_signals():
|
||||
signals = [
|
||||
("Cruise_On", "CruiseControl"),
|
||||
("Cruise_Activated", "CruiseControl"),
|
||||
@@ -122,8 +126,10 @@ class CarState(CarStateBase):
|
||||
return signals, checks
|
||||
|
||||
@staticmethod
|
||||
def get_global_es_distance_signals():
|
||||
def get_common_global_es_signals():
|
||||
signals = [
|
||||
("AEB_Status", "ES_Brake"),
|
||||
("Brake_Pressure", "ES_Brake"),
|
||||
("COUNTER", "ES_Distance"),
|
||||
("CHECKSUM", "ES_Distance"),
|
||||
("Signal1", "ES_Distance"),
|
||||
@@ -145,7 +151,9 @@ class CarState(CarStateBase):
|
||||
("Cruise_Resume", "ES_Distance"),
|
||||
("Signal6", "ES_Distance"),
|
||||
]
|
||||
|
||||
checks = [
|
||||
("ES_Brake", 20),
|
||||
("ES_Distance", 20),
|
||||
]
|
||||
|
||||
@@ -192,8 +200,8 @@ class CarState(CarStateBase):
|
||||
|
||||
if CP.carFingerprint not in PREGLOBAL_CARS:
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
signals += CarState.get_common_global_signals()[0]
|
||||
checks += CarState.get_common_global_signals()[1]
|
||||
signals += CarState.get_common_global_body_signals()[0]
|
||||
checks += CarState.get_common_global_body_signals()[1]
|
||||
|
||||
signals += [
|
||||
("Steer_Warning", "Steering_Torque"),
|
||||
@@ -232,7 +240,7 @@ class CarState(CarStateBase):
|
||||
("CruiseControl", 50),
|
||||
]
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 0)
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.main)
|
||||
|
||||
@staticmethod
|
||||
def get_cam_can_parser(CP):
|
||||
@@ -320,28 +328,28 @@ class CarState(CarStateBase):
|
||||
]
|
||||
|
||||
if CP.carFingerprint not in GLOBAL_GEN2:
|
||||
signals += CarState.get_global_es_distance_signals()[0]
|
||||
checks += CarState.get_global_es_distance_signals()[1]
|
||||
signals += CarState.get_common_global_es_signals()[0]
|
||||
checks += CarState.get_common_global_es_signals()[1]
|
||||
|
||||
if CP.flags & SubaruFlags.SEND_INFOTAINMENT:
|
||||
signals += [
|
||||
("COUNTER", "INFOTAINMENT_STATUS"),
|
||||
("CHECKSUM", "INFOTAINMENT_STATUS"),
|
||||
("LKAS_State_Infotainment", "INFOTAINMENT_STATUS"),
|
||||
("LKAS_Blue_Lines", "INFOTAINMENT_STATUS"),
|
||||
("Signal1", "INFOTAINMENT_STATUS"),
|
||||
("Signal2", "INFOTAINMENT_STATUS"),
|
||||
("COUNTER", "ES_Infotainment"),
|
||||
("CHECKSUM", "ES_Infotainment"),
|
||||
("LKAS_State_Infotainment", "ES_Infotainment"),
|
||||
("LKAS_Blue_Lines", "ES_Infotainment"),
|
||||
("Signal1", "ES_Infotainment"),
|
||||
("Signal2", "ES_Infotainment"),
|
||||
]
|
||||
checks.append(("INFOTAINMENT_STATUS", 10))
|
||||
checks.append(("ES_Infotainment", 10))
|
||||
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 2)
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.camera)
|
||||
|
||||
@staticmethod
|
||||
def get_body_can_parser(CP):
|
||||
if CP.carFingerprint in GLOBAL_GEN2:
|
||||
signals, checks = CarState.get_common_global_signals()
|
||||
signals += CarState.get_global_es_distance_signals()[0]
|
||||
checks += CarState.get_global_es_distance_signals()[1]
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, 1)
|
||||
signals, checks = CarState.get_common_global_body_signals()
|
||||
signals += CarState.get_common_global_es_signals()[0]
|
||||
checks += CarState.get_common_global_es_signals()[1]
|
||||
return CANParser(DBC[CP.carFingerprint]["pt"], signals, checks, CanBus.alt)
|
||||
|
||||
return None
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from cereal import car
|
||||
from selfdrive.car.subaru.values import CanBus
|
||||
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
@@ -105,7 +106,7 @@ def create_es_lkas_state(packer, es_lkas_state_msg, lat_active, mads_enabled, vi
|
||||
values["LKAS_Left_Line_Visible"] = int(left_line)
|
||||
values["LKAS_Right_Line_Visible"] = int(right_line)
|
||||
|
||||
return packer.make_can_msg("ES_LKAS_State", 0, values)
|
||||
return packer.make_can_msg("ES_LKAS_State", CanBus.main, values)
|
||||
|
||||
|
||||
def create_es_dashstatus(packer, dashstatus_msg):
|
||||
@@ -143,12 +144,12 @@ def create_es_dashstatus(packer, dashstatus_msg):
|
||||
if values["LKAS_State_Msg"] in (2, 3):
|
||||
values["LKAS_State_Msg"] = 0
|
||||
|
||||
return packer.make_can_msg("ES_DashStatus", 0, values)
|
||||
return packer.make_can_msg("ES_DashStatus", CanBus.main, values)
|
||||
|
||||
|
||||
def create_infotainmentstatus(packer, infotainmentstatus_msg, visual_alert):
|
||||
def create_es_infotainment(packer, es_infotainment_msg, visual_alert):
|
||||
# Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts
|
||||
values = {s: infotainmentstatus_msg[s] for s in [
|
||||
values = {s: es_infotainment_msg[s] for s in [
|
||||
"CHECKSUM",
|
||||
"COUNTER",
|
||||
"LKAS_State_Infotainment",
|
||||
@@ -167,7 +168,7 @@ def create_infotainmentstatus(packer, infotainmentstatus_msg, visual_alert):
|
||||
if visual_alert == VisualAlert.fcw:
|
||||
values["LKAS_State_Infotainment"] = 2
|
||||
|
||||
return packer.make_can_msg("INFOTAINMENT_STATUS", 0, values)
|
||||
return packer.make_can_msg("ES_Infotainment", CanBus.main, values)
|
||||
|
||||
|
||||
# *** Subaru Pre-global ***
|
||||
@@ -188,7 +189,7 @@ def create_preglobal_steering_control(packer, apply_steer, steer_req, frame, ste
|
||||
}
|
||||
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_LKAS")
|
||||
|
||||
return packer.make_can_msg("ES_LKAS", 0, values)
|
||||
return packer.make_can_msg("ES_LKAS", CanBus.main, values)
|
||||
|
||||
|
||||
def create_preglobal_es_distance(packer, cruise_button, es_distance_msg):
|
||||
@@ -215,4 +216,4 @@ def create_preglobal_es_distance(packer, cruise_button, es_distance_msg):
|
||||
values["Cruise_Button"] = cruise_button
|
||||
values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_Distance")
|
||||
|
||||
return packer.make_can_msg("ES_Distance", 0, values)
|
||||
return packer.make_can_msg("ES_Distance", CanBus.main, values)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import IntFlag
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag
|
||||
from panda import Panda
|
||||
from typing import Dict, List, Union
|
||||
|
||||
from cereal import car
|
||||
from panda.python import uds
|
||||
from selfdrive.car import dbc_dict
|
||||
from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts
|
||||
from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column
|
||||
from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -37,6 +37,12 @@ class SubaruFlags(IntFlag):
|
||||
SEND_INFOTAINMENT = 1
|
||||
|
||||
|
||||
class CanBus:
|
||||
main = 0
|
||||
alt = 1
|
||||
camera = 2
|
||||
|
||||
|
||||
class CAR:
|
||||
# Global platform
|
||||
ASCENT = "SUBARU ASCENT LIMITED 2019"
|
||||
@@ -53,10 +59,17 @@ class CAR:
|
||||
OUTBACK_PREGLOBAL_2018 = "SUBARU OUTBACK 2018 - 2019"
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
GLOBAL = CarFootnote(
|
||||
"In the non-US market, openpilot requires the car to come equipped with EyeSight with Lane Keep Assistance.",
|
||||
Column.PACKAGE)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SubaruCarInfo(CarInfo):
|
||||
package: str = "EyeSight Driver Assistance"
|
||||
car_parts: CarParts = CarParts.common([CarHarness.subaru_a])
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.subaru_a]))
|
||||
footnotes: List[Enum] = field(default_factory=lambda: [Footnote.GLOBAL])
|
||||
|
||||
|
||||
CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = {
|
||||
|
||||
@@ -16,12 +16,13 @@ from selfdrive.car.body.values import CAR as COMMA
|
||||
|
||||
# TODO: add routes for these cars
|
||||
non_tested_cars = [
|
||||
FORD.FOCUS_MK4,
|
||||
FORD.F_150_MK14,
|
||||
GM.CADILLAC_ATS,
|
||||
GM.HOLDEN_ASTRA,
|
||||
GM.MALIBU,
|
||||
GM.EQUINOX,
|
||||
HYUNDAI.GENESIS_G90,
|
||||
HYUNDAI.IONIQ_6,
|
||||
HYUNDAI.KIA_OPTIMA_H,
|
||||
HONDA.ODYSSEY_CHN,
|
||||
VOLKSWAGEN.CRAFTER_MK2, # need a route from an ACC-equipped Crafter
|
||||
@@ -47,6 +48,7 @@ routes = [
|
||||
CarTestRoute("54827bf84c38b14f|2023-01-25--14-14-11", FORD.BRONCO_SPORT_MK1),
|
||||
CarTestRoute("f8eaaccd2a90aef8|2023-05-04--15-10-09", FORD.ESCAPE_MK4),
|
||||
CarTestRoute("62241b0c7fea4589|2022-09-01--15-32-49", FORD.EXPLORER_MK6),
|
||||
CarTestRoute("e886087f430e7fe7|2023-06-16--23-06-36", FORD.FOCUS_MK4),
|
||||
CarTestRoute("bd37e43731e5964b|2023-04-30--10-42-26", FORD.MAVERICK_MK1),
|
||||
#TestRoute("f1b4c567731f4a1b|2018-04-30--10-15-35", FORD.FUSION),
|
||||
|
||||
@@ -98,6 +100,8 @@ routes = [
|
||||
CarTestRoute("bf43d9df2b660eb0|2021-09-23--14-16-37", HYUNDAI.SANTA_FE_2022),
|
||||
CarTestRoute("37398f32561a23ad|2021-11-18--00-11-35", HYUNDAI.SANTA_FE_HEV_2022),
|
||||
CarTestRoute("656ac0d830792fcc|2021-12-28--14-45-56", HYUNDAI.SANTA_FE_PHEV_2022, segment=1),
|
||||
CarTestRoute("de59124955b921d8|2023-06-24--00-12-50", HYUNDAI.KIA_CARNIVAL_4TH_GEN),
|
||||
CarTestRoute("409c9409979a8abc|2023-07-11--09-06-44", HYUNDAI.KIA_CARNIVAL_4TH_GEN), # Chinese model
|
||||
CarTestRoute("e0e98335f3ebc58f|2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
|
||||
CarTestRoute("7653b2bce7bcfdaa|2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
|
||||
CarTestRoute("018654717bc93d7d|2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
|
||||
|
||||
@@ -1,35 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
import unittest
|
||||
import hypothesis.strategies as st
|
||||
from hypothesis import given, settings
|
||||
import importlib
|
||||
from parameterized import parameterized
|
||||
|
||||
from cereal import car
|
||||
from common.realtime import DT_CTRL
|
||||
from selfdrive.car import gen_empty_fingerprint
|
||||
from selfdrive.car.car_helpers import interfaces
|
||||
from selfdrive.car.fingerprints import _FINGERPRINTS as FINGERPRINTS, all_known_cars
|
||||
from selfdrive.test.fuzzy_generation import get_random_msg
|
||||
from selfdrive.car.fingerprints import all_known_cars
|
||||
from selfdrive.test.fuzzy_generation import DrawType, FuzzyGenerator
|
||||
|
||||
|
||||
def get_fuzzy_car_interface_args(draw: DrawType) -> dict:
|
||||
# Fuzzy CAN fingerprints and FW versions to test more states of the CarInterface
|
||||
fingerprint_strategy = st.fixed_dictionaries({key: st.dictionaries(st.integers(min_value=0, max_value=0x800),
|
||||
st.integers(min_value=0, max_value=64)) for key in
|
||||
gen_empty_fingerprint()})
|
||||
|
||||
# just the most important fields
|
||||
car_fw_strategy = st.lists(st.fixed_dictionaries({
|
||||
'ecu': st.sampled_from(list(car.CarParams.Ecu.schema.enumerants.keys())),
|
||||
# TODO: only use reasonable addrs for the paired ecu and brand/platform
|
||||
'address': st.integers(min_value=0, max_value=0x800),
|
||||
}))
|
||||
|
||||
params_strategy = st.fixed_dictionaries({
|
||||
'fingerprints': fingerprint_strategy,
|
||||
'car_fw': car_fw_strategy,
|
||||
'experimental_long': st.booleans(),
|
||||
})
|
||||
|
||||
params: dict = draw(params_strategy)
|
||||
params['car_fw'] = [car.CarParams.CarFw(**fw) for fw in params['car_fw']]
|
||||
return params
|
||||
|
||||
|
||||
class TestCarInterfaces(unittest.TestCase):
|
||||
|
||||
@parameterized.expand([(car,) for car in all_known_cars()])
|
||||
@parameterized.expand([(car,) for car in sorted(all_known_cars())])
|
||||
@settings(max_examples=5)
|
||||
@given(cc_msg=get_random_msg(car.CarControl, real_floats=True))
|
||||
def test_car_interfaces(self, car_name, cc_msg):
|
||||
if car_name in FINGERPRINTS:
|
||||
fingerprint = FINGERPRINTS[car_name][0]
|
||||
else:
|
||||
fingerprint = {}
|
||||
|
||||
@given(data=st.data())
|
||||
def test_car_interfaces(self, car_name, data):
|
||||
CarInterface, CarController, CarState = interfaces[car_name]
|
||||
fingerprints = gen_empty_fingerprint()
|
||||
fingerprints.update({k: fingerprint for k in fingerprints.keys()})
|
||||
|
||||
car_fw = []
|
||||
args = get_fuzzy_car_interface_args(data.draw)
|
||||
|
||||
car_params = CarInterface.get_params(car_name, fingerprints, car_fw, experimental_long=False, docs=False)
|
||||
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
|
||||
experimental_long=args['experimental_long'], docs=False)
|
||||
car_interface = CarInterface(car_params, CarController, CarState)
|
||||
assert car_params
|
||||
assert car_interface
|
||||
@@ -60,19 +80,23 @@ class TestCarInterfaces(unittest.TestCase):
|
||||
elif tune.which() == 'indi':
|
||||
self.assertTrue(len(tune.indi.outerLoopGainV))
|
||||
|
||||
cc_msg = FuzzyGenerator.get_random_msg(data.draw, car.CarControl, real_floats=True)
|
||||
# Run car interface
|
||||
now_nanos = 0
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
car_interface.apply(CC, 0)
|
||||
car_interface.apply(CC, 0)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
now_nanos += DT_CTRL * 1e9 # 10 ms
|
||||
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
CC.enabled = True
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
car_interface.apply(CC, 0)
|
||||
car_interface.apply(CC, 0)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
car_interface.apply(CC, now_nanos)
|
||||
now_nanos += DT_CTRL * 1e9 # 10ms
|
||||
|
||||
# Test radar interface
|
||||
RadarInterface = importlib.import_module(f'selfdrive.car.{car_params.carName}.radar_interface').RadarInterface
|
||||
|
||||
@@ -10,7 +10,8 @@ from cereal import car
|
||||
from common.params import Params
|
||||
from selfdrive.car.car_helpers import interfaces
|
||||
from selfdrive.car.fingerprints import FW_VERSIONS
|
||||
from selfdrive.car.fw_versions import FW_QUERY_CONFIGS, FUZZY_EXCLUDE_ECUS, VERSIONS, build_fw_dict, match_fw_to_car, get_fw_versions
|
||||
from selfdrive.car.fw_versions import FW_QUERY_CONFIGS, FUZZY_EXCLUDE_ECUS, VERSIONS, build_fw_dict, match_fw_to_car, get_fw_versions, get_present_ecus
|
||||
from selfdrive.car.vin import get_vin
|
||||
|
||||
CarFw = car.CarParams.CarFw
|
||||
Ecu = car.CarParams.Ecu
|
||||
@@ -157,36 +158,69 @@ class TestFwFingerprint(unittest.TestCase):
|
||||
|
||||
ecu_strings = ", ".join([f'Ecu.{ECU_NAME[ecu]}' for ecu in ecus_not_whitelisted])
|
||||
self.assertFalse(len(whitelisted_ecus) and len(ecus_not_whitelisted),
|
||||
f'{brand.title()}: FW query whitelist missing ecus: {ecu_strings}')
|
||||
f'{brand.title()}: ECUs not in any FW query whitelists: {ecu_strings}')
|
||||
|
||||
def test_fw_requests(self):
|
||||
# Asserts equal length request and response lists
|
||||
for brand, config in FW_QUERY_CONFIGS.items():
|
||||
with self.subTest(brand=brand):
|
||||
for request_obj in config.requests:
|
||||
self.assertEqual(len(request_obj.request), len(request_obj.response))
|
||||
|
||||
|
||||
class TestFwFingerprintTiming(unittest.TestCase):
|
||||
N: int = 5
|
||||
TOL: float = 0.1
|
||||
|
||||
@staticmethod
|
||||
def _benchmark(brand, num_pandas, n):
|
||||
def _run_thread(thread: threading.Thread) -> float:
|
||||
params = Params()
|
||||
params.put_bool("ObdMultiplexingEnabled", True)
|
||||
thread.start()
|
||||
t = time.perf_counter()
|
||||
while thread.is_alive():
|
||||
time.sleep(0.02)
|
||||
if not params.get_bool("ObdMultiplexingChanged"):
|
||||
params.put_bool("ObdMultiplexingChanged", True)
|
||||
return time.perf_counter() - t
|
||||
|
||||
def _benchmark_brand(self, brand, num_pandas):
|
||||
fake_socket = FakeSocket()
|
||||
brand_time = 0
|
||||
for _ in range(self.N):
|
||||
thread = threading.Thread(target=get_fw_versions, args=(fake_socket, fake_socket, brand),
|
||||
kwargs=dict(num_pandas=num_pandas))
|
||||
brand_time += self._run_thread(thread)
|
||||
|
||||
times = []
|
||||
for _ in range(n):
|
||||
params.put_bool("ObdMultiplexingEnabled", True)
|
||||
thread = threading.Thread(target=get_fw_versions, args=(fake_socket, fake_socket, brand), kwargs=dict(num_pandas=num_pandas))
|
||||
thread.start()
|
||||
t = time.perf_counter()
|
||||
while thread.is_alive():
|
||||
time.sleep(0.02)
|
||||
if not params.get_bool("ObdMultiplexingChanged"):
|
||||
params.put_bool("ObdMultiplexingChanged", True)
|
||||
times.append(time.perf_counter() - t)
|
||||
return round(brand_time / self.N, 2)
|
||||
|
||||
return round(sum(times) / len(times), 2)
|
||||
def _assert_timing(self, avg_time, ref_time):
|
||||
self.assertLess(avg_time, ref_time + self.TOL)
|
||||
self.assertGreater(avg_time, ref_time - self.TOL, "Performance seems to have improved, update test refs.")
|
||||
|
||||
def _assert_timing(self, avg_time, ref_time, tol):
|
||||
self.assertLess(avg_time, ref_time + tol)
|
||||
self.assertGreater(avg_time, ref_time - tol, "Performance seems to have improved, update test refs.")
|
||||
def test_startup_timing(self):
|
||||
# Tests worse-case VIN query time and typical present ECU query time
|
||||
vin_ref_time = 1.0
|
||||
present_ecu_ref_time = 0.8
|
||||
|
||||
fake_socket = FakeSocket()
|
||||
present_ecu_time = 0.0
|
||||
for _ in range(self.N):
|
||||
thread = threading.Thread(target=get_present_ecus, args=(fake_socket, fake_socket),
|
||||
kwargs=dict(num_pandas=2))
|
||||
present_ecu_time += self._run_thread(thread)
|
||||
self._assert_timing(present_ecu_time / self.N, present_ecu_ref_time)
|
||||
print(f'get_present_ecus, query time={present_ecu_time / self.N} seconds')
|
||||
|
||||
vin_time = 0.0
|
||||
for _ in range(self.N):
|
||||
thread = threading.Thread(target=get_vin, args=(fake_socket, fake_socket, 1))
|
||||
vin_time += self._run_thread(thread)
|
||||
self._assert_timing(vin_time / self.N, vin_ref_time)
|
||||
print(f'get_vin, query time={vin_time / self.N} seconds')
|
||||
|
||||
def test_fw_query_timing(self):
|
||||
tol = 0.1
|
||||
total_ref_time = 4.6
|
||||
total_ref_time = 6.0
|
||||
brand_ref_times = {
|
||||
1: {
|
||||
'body': 0.1,
|
||||
@@ -194,14 +228,15 @@ class TestFwFingerprintTiming(unittest.TestCase):
|
||||
'ford': 0.2,
|
||||
'honda': 0.5,
|
||||
'hyundai': 0.7,
|
||||
'mazda': 0.1,
|
||||
'mazda': 0.2,
|
||||
'nissan': 0.3,
|
||||
'subaru': 0.1,
|
||||
'tesla': 0.2,
|
||||
'toyota': 0.7,
|
||||
'toyota': 1.6,
|
||||
'volkswagen': 0.2,
|
||||
},
|
||||
2: {
|
||||
'ford': 0.4,
|
||||
'hyundai': 1.1,
|
||||
}
|
||||
}
|
||||
@@ -214,13 +249,13 @@ class TestFwFingerprintTiming(unittest.TestCase):
|
||||
if not len(multi_panda_requests) and num_pandas > 1:
|
||||
raise unittest.SkipTest("No multi-panda FW queries")
|
||||
|
||||
avg_time = self._benchmark(brand, num_pandas, 5)
|
||||
avg_time = self._benchmark_brand(brand, num_pandas)
|
||||
total_time += avg_time
|
||||
self._assert_timing(avg_time, brand_ref_times[num_pandas][brand], tol)
|
||||
self._assert_timing(avg_time, brand_ref_times[num_pandas][brand])
|
||||
print(f'{brand=}, {num_pandas=}, {len(config.requests)=}, avg FW query time={avg_time} seconds')
|
||||
|
||||
with self.subTest(brand='all_brands'):
|
||||
self._assert_timing(total_time, total_ref_time, tol)
|
||||
self._assert_timing(total_time, total_ref_time)
|
||||
print(f'all brands, total FW query time={total_time} seconds')
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ for r in routes:
|
||||
test_cases: List[Tuple[str, Optional[CarTestRoute]]] = []
|
||||
for i, c in enumerate(sorted(all_known_cars())):
|
||||
if i % NUM_JOBS == JOB_ID:
|
||||
test_cases.extend((c, r) for r in routes_by_car.get(c, (None, )))
|
||||
test_cases.extend(sorted((c, r) for r in routes_by_car.get(c, (None, ))))
|
||||
|
||||
SKIP_ENV_VAR = "SKIP_LONG_TESTS"
|
||||
|
||||
@@ -201,6 +201,42 @@ class TestCarModelBase(unittest.TestCase):
|
||||
self.assertTrue(self.safety.addr_checks_valid())
|
||||
self.assertFalse(len(failed_addrs), f"panda safety RX check failed: {failed_addrs}")
|
||||
|
||||
def test_panda_safety_tx_cases(self, data=None):
|
||||
"""Asserts we can tx common messages"""
|
||||
if self.CP.notCar:
|
||||
self.skipTest("Skipping test for notCar")
|
||||
|
||||
def test_car_controller(car_control):
|
||||
now_nanos = 0
|
||||
msgs_sent = 0
|
||||
CI = self.CarInterface(self.CP, self.CarController, self.CarState)
|
||||
for _ in range(round(10.0 / DT_CTRL)): # make sure we hit the slowest messages
|
||||
CI.update(car_control, [])
|
||||
_, sendcan = CI.apply(car_control, now_nanos)
|
||||
|
||||
now_nanos += DT_CTRL * 1e9
|
||||
msgs_sent += len(sendcan)
|
||||
for addr, _, dat, bus in sendcan:
|
||||
to_send = libpanda_py.make_CANPacket(addr, bus % 4, dat)
|
||||
self.assertTrue(self.safety.safety_tx_hook(to_send), (addr, dat, bus))
|
||||
|
||||
# Make sure we attempted to send messages
|
||||
self.assertGreater(msgs_sent, 50)
|
||||
|
||||
# Make sure we can send all messages while inactive
|
||||
CC = car.CarControl.new_message()
|
||||
test_car_controller(CC)
|
||||
|
||||
# Test cancel + general messages (controls_allowed=False & cruise_engaged=True)
|
||||
self.safety.set_cruise_engaged_prev(True)
|
||||
CC = car.CarControl.new_message(cruiseControl={'cancel': True})
|
||||
test_car_controller(CC)
|
||||
|
||||
# Test resume + general messages (controls_allowed=True & cruise_engaged=True)
|
||||
self.safety.set_controls_allowed(True)
|
||||
CC = car.CarControl.new_message(cruiseControl={'resume': True})
|
||||
test_car_controller(CC)
|
||||
|
||||
def test_panda_safety_carstate(self):
|
||||
"""
|
||||
Assert that panda safety matches openpilot's carState
|
||||
@@ -214,7 +250,7 @@ class TestCarModelBase(unittest.TestCase):
|
||||
for can in self.can_msgs[:300]:
|
||||
self.CI.update(CC, (can.as_builder().to_bytes(), ))
|
||||
for msg in filter(lambda m: m.src in range(64), can.can):
|
||||
to_send = libpanda_py.make_CANPacket(msg.address, msg.src, msg.dat)
|
||||
to_send = libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat)
|
||||
self.safety.safety_rx_hook(to_send)
|
||||
|
||||
controls_allowed_prev = False
|
||||
|
||||
@@ -19,6 +19,7 @@ TESLA AP2 MODEL S: [.nan, 2.5, .nan]
|
||||
FORD BRONCO SPORT 1ST GEN: [.nan, 1.5, .nan]
|
||||
FORD ESCAPE 4TH GEN: [.nan, 1.5, .nan]
|
||||
FORD EXPLORER 6TH GEN: [.nan, 1.5, .nan]
|
||||
FORD F-150 14TH GEN: [.nan, 1.5, .nan]
|
||||
FORD FOCUS 4TH GEN: [.nan, 1.5, .nan]
|
||||
FORD MAVERICK 1ST GEN: [.nan, 1.5, .nan]
|
||||
###
|
||||
@@ -29,7 +30,7 @@ COMMA BODY: [.nan, 1000, .nan]
|
||||
# Totally new cars
|
||||
RAM 1500 5TH GEN: [2.0, 2.0, 0.05]
|
||||
RAM HD 5TH GEN: [1.4, 1.4, 0.05]
|
||||
SUBARU OUTBACK 6TH GEN: [2.3, 2.3, 0.11]
|
||||
SUBARU OUTBACK 6TH GEN: [2.0, 2.0, 0.2]
|
||||
CADILLAC ESCALADE 2017: [1.899999976158142, 1.842270016670227, 0.1120000034570694]
|
||||
CHEVROLET BOLT EUV 2022: [2.0, 2.0, 0.05]
|
||||
CHEVROLET SILVERADO 1500 2020: [1.9, 1.9, 0.112]
|
||||
@@ -47,6 +48,8 @@ KIA SORENTO 4TH GEN: [2.5, 2.5, 0.1]
|
||||
KIA NIRO HYBRID 2ND GEN: [2.42, 2.5, 0.12]
|
||||
KIA NIRO EV 2ND GEN: [2.05, 2.5, 0.14]
|
||||
GENESIS GV80 2023: [2.5, 2.5, 0.1]
|
||||
KIA CARNIVAL 4TH GEN: [1.75, 1.75, 0.15]
|
||||
GMC ACADIA DENALI 2018: [1.6, 1.6, 0.2]
|
||||
HONDA ACCORD 4CYL 9TH GEN: [1.7, 0.35, 0.21]
|
||||
|
||||
# Dashcam or fallback configured as ideal car
|
||||
|
||||
@@ -10,7 +10,6 @@ CHRYSLER PACIFICA HYBRID 2017: [1.79422, 1.06831764583744, 0.116237]
|
||||
CHRYSLER PACIFICA HYBRID 2018: [2.08887, 1.2943025830995154, 0.114818]
|
||||
CHRYSLER PACIFICA HYBRID 2019: [1.90120, 1.1958788168371808, 0.131520]
|
||||
GENESIS G70 2018: [3.8520195946707947, 2.354697063349854, 0.06830285485626221]
|
||||
GMC ACADIA DENALI 2018: [1.3181430320331884, 1.1853735340610179, 0.3450592280031644]
|
||||
HONDA ACCORD 2018: [1.7135052593468778, 0.3461280068322071, 0.21579936052863807]
|
||||
HONDA ACCORD HYBRID 2018: [1.6651615004829625, 0.30322180951193245, 0.2083000440586149]
|
||||
HONDA CIVIC (BOSCH) 2019: [1.691708637466905, 0.40132900729454185, 0.25460295304024094]
|
||||
|
||||
@@ -36,6 +36,7 @@ HYUNDAI KONA ELECTRIC 2022: HYUNDAI KONA ELECTRIC 2019
|
||||
HYUNDAI IONIQ HYBRID 2017-2019: HYUNDAI IONIQ PLUG-IN HYBRID 2019
|
||||
HYUNDAI IONIQ HYBRID 2020-2022: HYUNDAI IONIQ PLUG-IN HYBRID 2019
|
||||
HYUNDAI IONIQ ELECTRIC 2020: HYUNDAI IONIQ PLUG-IN HYBRID 2019
|
||||
HYUNDAI IONIQ 6 2023: HYUNDAI IONIQ 5 2022
|
||||
HYUNDAI ELANTRA 2017: HYUNDAI SONATA 2019
|
||||
HYUNDAI ELANTRA HYBRID 2021: HYUNDAI SONATA 2020
|
||||
HYUNDAI ELANTRA SE 2022 NON-SCC: HYUNDAI ELANTRA 2021
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from cereal import car
|
||||
from common.numpy_fast import clip, interp
|
||||
from common.params import Params
|
||||
from selfdrive.car import apply_meas_steer_torque_limits, create_gas_interceptor_command, make_can_msg
|
||||
from selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, \
|
||||
create_gas_interceptor_command, make_can_msg
|
||||
from selfdrive.car.toyota.toyotacan import create_steer_command, create_ui_command, \
|
||||
create_accel_command, create_acc_cancel_command, \
|
||||
create_fcw_command, create_lta_steer_command
|
||||
@@ -10,8 +11,10 @@ from selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR,
|
||||
UNSUPPORTED_DSU_CAR
|
||||
from opendbc.can.packer import CANPacker
|
||||
|
||||
SteerControlType = car.CarParams.SteerControlType
|
||||
VisualAlert = car.CarControl.HUDControl.VisualAlert
|
||||
|
||||
# LKA limits
|
||||
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
|
||||
MAX_STEER_RATE = 100 # deg/s
|
||||
MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
|
||||
@@ -19,13 +22,19 @@ MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
|
||||
# EPS allows user torque above threshold for 50 frames before permanently faulting
|
||||
MAX_USER_TORQUE = 500
|
||||
|
||||
# LTA limits
|
||||
# EPS ignores commands above this angle and causes PCS to fault
|
||||
MAX_STEER_ANGLE = 94.9461 # deg
|
||||
MAX_DRIVER_TORQUE_ALLOWANCE = 150 # slightly above steering pressed allows some resistance when changing lanes
|
||||
|
||||
|
||||
class CarController:
|
||||
def __init__(self, dbc_name, CP, VM):
|
||||
self.CP = CP
|
||||
self.torque_rate_limits = CarControllerParams(self.CP)
|
||||
self.params = CarControllerParams(self.CP)
|
||||
self.frame = 0
|
||||
self.last_steer = 0
|
||||
self.last_angle = 0
|
||||
self.alert_active = False
|
||||
self.last_standstill = False
|
||||
self.standstill_req = False
|
||||
@@ -44,27 +53,12 @@ class CarController:
|
||||
pcm_cancel_cmd = CC.cruiseControl.cancel
|
||||
lat_active = CC.latActive and abs(CS.out.steeringTorque) < MAX_USER_TORQUE
|
||||
|
||||
# gas and brake
|
||||
if self.CP.enableGasInterceptor and CC.longActive:
|
||||
MAX_INTERCEPTOR_GAS = 0.5
|
||||
# RAV4 has very sensitive gas pedal
|
||||
if self.CP.carFingerprint in (CAR.RAV4, CAR.RAV4H, CAR.HIGHLANDER, CAR.HIGHLANDERH):
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0])
|
||||
elif self.CP.carFingerprint in (CAR.COROLLA,):
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0])
|
||||
else:
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0])
|
||||
# offset for creep and windbrake
|
||||
pedal_offset = interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-.4, 0.0, 0.2])
|
||||
pedal_command = PEDAL_SCALE * (actuators.accel + pedal_offset)
|
||||
interceptor_gas_cmd = clip(pedal_command, 0., MAX_INTERCEPTOR_GAS)
|
||||
else:
|
||||
interceptor_gas_cmd = 0.
|
||||
pcm_accel_cmd = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
|
||||
# *** control msgs ***
|
||||
can_sends = []
|
||||
|
||||
# steer torque
|
||||
new_steer = int(round(actuators.steer * CarControllerParams.STEER_MAX))
|
||||
apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.torque_rate_limits)
|
||||
# *** steer torque ***
|
||||
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
|
||||
apply_steer = apply_meas_steer_torque_limits(new_steer, self.last_steer, CS.out.steeringTorqueEps, self.params)
|
||||
|
||||
# Count up to MAX_STEER_RATE_FRAMES, at which point we need to cut torque to avoid a steering fault
|
||||
if lat_active and abs(CS.out.steeringRateDeg) >= MAX_STEER_RATE:
|
||||
@@ -80,10 +74,53 @@ class CarController:
|
||||
apply_steer_req = 0
|
||||
self.steer_rate_counter = 0
|
||||
|
||||
# Never actuate with LKA on cars that only support LTA
|
||||
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||
# *** steer angle ***
|
||||
if self.CP.steerControlType == SteerControlType.angle:
|
||||
# If using LTA control, disable LKA and set steering angle command
|
||||
apply_steer = 0
|
||||
apply_steer_req = 0
|
||||
if self.frame % 2 == 0:
|
||||
# EPS uses the torque sensor angle to control with, offset to compensate
|
||||
apply_angle = actuators.steeringAngleDeg + CS.out.steeringAngleOffsetDeg
|
||||
|
||||
# Angular rate limit based on speed
|
||||
apply_angle = apply_std_steer_angle_limits(apply_angle, self.last_angle, CS.out.vEgo, self.params)
|
||||
|
||||
if not lat_active:
|
||||
apply_angle = CS.out.steeringAngleDeg + CS.out.steeringAngleOffsetDeg
|
||||
|
||||
self.last_angle = clip(apply_angle, -MAX_STEER_ANGLE, MAX_STEER_ANGLE)
|
||||
|
||||
self.last_steer = apply_steer
|
||||
|
||||
# toyota can trace shows this message at 42Hz, with counter adding alternatively 1 and 2;
|
||||
# sending it at 100Hz seem to allow a higher rate limit, as the rate limit seems imposed
|
||||
# on consecutive messages
|
||||
can_sends.append(create_steer_command(self.packer, apply_steer, apply_steer_req))
|
||||
if self.frame % 2 == 0 and self.CP.carFingerprint in TSS2_CAR:
|
||||
lta_active = lat_active and self.CP.steerControlType == SteerControlType.angle
|
||||
full_torque_condition = (abs(CS.out.steeringTorqueEps) < self.params.STEER_MAX and
|
||||
abs(CS.out.steeringTorque) < MAX_DRIVER_TORQUE_ALLOWANCE)
|
||||
setme_x64 = 100 if lta_active and full_torque_condition else 0
|
||||
can_sends.append(create_lta_steer_command(self.packer, self.last_angle, lta_active, self.frame // 2, setme_x64))
|
||||
|
||||
# *** gas and brake ***
|
||||
if self.CP.enableGasInterceptor and CC.longActive:
|
||||
MAX_INTERCEPTOR_GAS = 0.5
|
||||
# RAV4 has very sensitive gas pedal
|
||||
if self.CP.carFingerprint in (CAR.RAV4, CAR.RAV4H, CAR.HIGHLANDER, CAR.HIGHLANDERH):
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0])
|
||||
elif self.CP.carFingerprint in (CAR.COROLLA,):
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0])
|
||||
else:
|
||||
PEDAL_SCALE = interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0])
|
||||
# offset for creep and windbrake
|
||||
pedal_offset = interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-.4, 0.0, 0.2])
|
||||
pedal_command = PEDAL_SCALE * (actuators.accel + pedal_offset)
|
||||
interceptor_gas_cmd = clip(pedal_command, 0., MAX_INTERCEPTOR_GAS)
|
||||
else:
|
||||
interceptor_gas_cmd = 0.
|
||||
pcm_accel_cmd = clip(actuators.accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
|
||||
|
||||
# TODO: probably can delete this. CS.pcm_acc_status uses a different signal
|
||||
# than CS.cruiseState.enabled. confirm they're not meaningfully different
|
||||
@@ -97,26 +134,8 @@ class CarController:
|
||||
# pcm entered standstill or it's disabled
|
||||
self.standstill_req = False
|
||||
|
||||
self.last_steer = apply_steer
|
||||
self.last_standstill = CS.out.standstill
|
||||
|
||||
can_sends = []
|
||||
|
||||
# *** control msgs ***
|
||||
# print("steer {0} {1} {2} {3}".format(apply_steer, min_lim, max_lim, CS.steer_torque_motor)
|
||||
|
||||
# toyota can trace shows this message at 42Hz, with counter adding alternatively 1 and 2;
|
||||
# sending it at 100Hz seem to allow a higher rate limit, as the rate limit seems imposed
|
||||
# on consecutive messages
|
||||
can_sends.append(create_steer_command(self.packer, apply_steer, apply_steer_req))
|
||||
if self.frame % 2 == 0 and self.CP.carFingerprint in TSS2_CAR:
|
||||
can_sends.append(create_lta_steer_command(self.packer, 0, 0, self.frame // 2))
|
||||
|
||||
# LTA mode. Set ret.steerControlType = car.CarParams.SteerControlType.angle and whitelist 0x191 in the panda
|
||||
# if self.frame % 2 == 0:
|
||||
# can_sends.append(create_steer_command(self.packer, 0, 0, self.frame // 2))
|
||||
# can_sends.append(create_lta_steer_command(self.packer, actuators.steeringAngleDeg, apply_steer_req, self.frame // 2))
|
||||
|
||||
# we can spam can to cancel the system even if we are using lat only control
|
||||
if (self.frame % 3 == 0 and self.CP.openpilotLongitudinalControl) or pcm_cancel_cmd:
|
||||
lead = hud_control.leadVisible or CS.out.vEgo < 12. # at low speed we always assume the lead is present so ACC can be engaged
|
||||
@@ -137,6 +156,7 @@ class CarController:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer, interceptor_gas_cmd, self.frame // 2))
|
||||
self.gas = interceptor_gas_cmd
|
||||
|
||||
# *** hud ui ***
|
||||
if self.CP.carFingerprint != CAR.PRIUS_V:
|
||||
# ui mesg is at 1Hz but we send asap if:
|
||||
# - there is something to display
|
||||
@@ -167,8 +187,9 @@ class CarController:
|
||||
can_sends.append(make_can_msg(addr, vl, bus))
|
||||
|
||||
new_actuators = actuators.copy()
|
||||
new_actuators.steer = apply_steer / CarControllerParams.STEER_MAX
|
||||
new_actuators.steer = apply_steer / self.params.STEER_MAX
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
new_actuators.steeringAngleDeg = self.last_angle
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ from opendbc.can.parser import CANParser
|
||||
from selfdrive.car.interfaces import CarStateBase
|
||||
from selfdrive.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR
|
||||
|
||||
SteerControlType = car.CarParams.SteerControlType
|
||||
|
||||
# These steering fault definitions seem to be common across LKA (torque) and LTA (angle):
|
||||
# - high steer rate fault: goes to 21 or 25 for 1 frame, then 9 for 2 seconds
|
||||
# - lka/lta msg drop out: goes to 9 then 11 for a combined total of 2 seconds, then 3.
|
||||
# if using the other control command, goes directly to 3 after 1.5 seconds
|
||||
# - initializing: LTA can report 0 as long as STEER_TORQUE_SENSOR->STEER_ANGLE_INITIALIZING is 1,
|
||||
# and is a catch-all for LKA
|
||||
TEMP_STEER_FAULTS = (0, 9, 11, 21, 25)
|
||||
# - lka/lta msg drop out: 3 (recoverable)
|
||||
# - prolonged high driver torque: 17 (permanent)
|
||||
PERM_STEER_FAULTS = (3, 17)
|
||||
|
||||
_TRAFFIC_SINGAL_MAP = {
|
||||
1: "kph",
|
||||
36: "mph",
|
||||
@@ -125,12 +138,14 @@ class CarState(CarStateBase):
|
||||
ret.steeringTorqueEps = cp.vl["STEER_TORQUE_SENSOR"]["STEER_TORQUE_EPS"] * self.eps_torque_scale
|
||||
# we could use the override bit from dbc, but it's triggered at too high torque values
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD
|
||||
# steer rate fault: goes to 21 or 25 for 1 frame, then 9 for 2 seconds
|
||||
# lka msg drop out: goes to 9 then 11 for a combined total of 2 seconds
|
||||
ret.steerFaultTemporary = cp.vl["EPS_STATUS"]["LKA_STATE"] in (0, 9, 11, 21, 25)
|
||||
# 17 is a fault from a prolonged high torque delta between cmd and user
|
||||
# 3 is a fault from the lka command message not being received by the EPS
|
||||
ret.steerFaultPermanent = cp.vl["EPS_STATUS"]["LKA_STATE"] in (3, 17)
|
||||
|
||||
# Check EPS LKA/LTA fault status
|
||||
ret.steerFaultTemporary = cp.vl["EPS_STATUS"]["LKA_STATE"] in TEMP_STEER_FAULTS
|
||||
ret.steerFaultPermanent = cp.vl["EPS_STATUS"]["LKA_STATE"] in PERM_STEER_FAULTS
|
||||
|
||||
if self.CP.steerControlType == SteerControlType.angle:
|
||||
ret.steerFaultTemporary = ret.steerFaultTemporary or cp.vl["EPS_STATUS"]["LTA_STATE"] in TEMP_STEER_FAULTS
|
||||
ret.steerFaultPermanent = ret.steerFaultPermanent or cp.vl["EPS_STATUS"]["LTA_STATE"] in PERM_STEER_FAULTS
|
||||
|
||||
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
|
||||
# TODO: find the bit likely in DSU_CRUISE that describes an ACC fault. one may also exist in CLUTCH
|
||||
@@ -310,6 +325,10 @@ class CarState(CarStateBase):
|
||||
("AUTO_HIGH_BEAM", "LIGHT_STALK"),
|
||||
]
|
||||
|
||||
# Check LTA state if using LTA angle control
|
||||
if CP.steerControlType == SteerControlType.angle:
|
||||
signals.append(("LTA_STATE", "EPS_STATUS"))
|
||||
|
||||
checks = [
|
||||
("GEAR_PACKET", 1),
|
||||
("LIGHT_STALK", 1),
|
||||
|
||||
@@ -10,6 +10,7 @@ from selfdrive.car.interfaces import CarInterfaceBase
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
EventName = car.CarEvent.EventName
|
||||
SteerControlType = car.CarParams.SteerControlType
|
||||
GearShifter = car.CarState.GearShifter
|
||||
|
||||
GAC_DICT = {3: 1, 2: 2, 1: 3}
|
||||
@@ -32,13 +33,18 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if candidate in ANGLE_CONTROL_CAR:
|
||||
ret.dashcamOnly = True
|
||||
ret.steerControlType = car.CarParams.SteerControlType.angle
|
||||
ret.steerControlType = SteerControlType.angle
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_LTA
|
||||
|
||||
# LTA control can be more delayed and winds up more often
|
||||
ret.steerActuatorDelay = 0.25
|
||||
ret.steerLimitTimer = 0.8
|
||||
else:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
ret.steerActuatorDelay = 0.12 # Default delay, Prius has larger delay
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.steerActuatorDelay = 0.12 # Default delay, Prius has larger delay
|
||||
ret.steerLimitTimer = 0.4
|
||||
|
||||
ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop
|
||||
|
||||
stop_and_go = False
|
||||
@@ -212,13 +218,29 @@ class CarInterface(CarInterfaceBase):
|
||||
if 0x2FF in fingerprint[0]:
|
||||
ret.flags |= ToyotaFlags.SMART_DSU.value
|
||||
|
||||
# No radar dbc for cars without DSU which are not TSS 2.0
|
||||
# TODO: make an adas dbc file for dsu-less models
|
||||
ret.radarUnavailable = DBC[candidate]['radar'] is None or candidate in (NO_DSU_CAR - TSS2_CAR)
|
||||
|
||||
# In TSS2 cars, the camera does long control
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) and not (ret.flags & ToyotaFlags.SMART_DSU)
|
||||
ret.enableGasInterceptor = 0x201 in fingerprint[0]
|
||||
|
||||
# if the smartDSU is detected, openpilot can send ACC_CMD (and the smartDSU will block it from the DSU) or not (the DSU is "connected")
|
||||
ret.openpilotLongitudinalControl = (bool(ret.flags & ToyotaFlags.SMART_DSU) or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR)) and not Params().get_bool("StockLongToyota")
|
||||
# if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar.
|
||||
# since we don't yet parse radar on TSS2 radar-based ACC cars, gate longitudinal behind experimental toggle
|
||||
use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU)
|
||||
if candidate in RADAR_ACC_CAR:
|
||||
ret.experimentalLongitudinalAvailable = use_sdsu
|
||||
use_sdsu = use_sdsu and experimental_long
|
||||
|
||||
# openpilot longitudinal enabled by default:
|
||||
# - non-(TSS2 radar ACC cars) w/ smartDSU installed
|
||||
# - cars w/ DSU disconnected
|
||||
# - TSS2 cars with camera sending ACC_CONTROL where we can block it
|
||||
# openpilot longitudinal behind experimental long toggle:
|
||||
# - TSS2 radar ACC cars w/ smartDSU installed
|
||||
ret.openpilotLongitudinalControl = (use_sdsu or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR)) and not Params().get_bool("StockLongToyota")
|
||||
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
|
||||
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
@@ -342,6 +364,11 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
events, ret = self.create_sp_events(self.CS, ret, events)
|
||||
|
||||
# Lane Tracing Assist control is unavailable (EPS_STATUS->LTA_STATE=0) until
|
||||
# the more accurate angle sensor signal is initialized
|
||||
if self.CP.steerControlType == SteerControlType.angle and not self.CS.accurate_steer_angle_seen:
|
||||
events.add(EventName.vehicleSensorsInvalid)
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if ret.cruiseState.standstill and not ret.brakePressed and not self.CP.enableGasInterceptor:
|
||||
events.add(EventName.resumeRequired)
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
from opendbc.can.parser import CANParser
|
||||
from cereal import car
|
||||
from selfdrive.car.toyota.values import NO_DSU_CAR, DBC, TSS2_CAR
|
||||
from selfdrive.car.toyota.values import DBC, TSS2_CAR
|
||||
from selfdrive.car.interfaces import RadarInterfaceBase
|
||||
|
||||
|
||||
def _create_radar_can_parser(car_fingerprint):
|
||||
if DBC[car_fingerprint]['radar'] is None:
|
||||
return None
|
||||
|
||||
if car_fingerprint in TSS2_CAR:
|
||||
RADAR_A_MSGS = list(range(0x180, 0x190))
|
||||
RADAR_B_MSGS = list(range(0x190, 0x1a0))
|
||||
@@ -42,16 +39,12 @@ class RadarInterface(RadarInterfaceBase):
|
||||
|
||||
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
|
||||
|
||||
self.rcp = _create_radar_can_parser(CP.carFingerprint)
|
||||
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = self.RADAR_B_MSGS[-1]
|
||||
self.updated_messages = set()
|
||||
|
||||
# No radar dbc for cars without DSU which are not TSS 2.0
|
||||
# TODO: make a adas dbc file for dsu-less models
|
||||
self.no_radar = CP.carFingerprint in NO_DSU_CAR and CP.carFingerprint not in TSS2_CAR
|
||||
|
||||
def update(self, can_strings):
|
||||
if self.no_radar or self.rcp is None:
|
||||
if self.rcp is None:
|
||||
return super().update(None)
|
||||
|
||||
vls = self.rcp.update_strings(can_strings)
|
||||
|
||||