diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index af67bdbe5..1115c375d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,11 +1,11 @@ FROM ghcr.io/commaai/openpilot-base:latest -RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa libegl1-mesa gdb bash-completion +RUN apt update && apt install -y vim net-tools usbutils htop ripgrep tmux wget mesa-utils xvfb libxtst6 libxv1 libglu1-mesa gdb bash-completion RUN pip install ipython jupyter jupyterlab RUN cd /tmp && \ ARCH=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && \ - curl -L -o virtualgl.deb "https://downloads.sourceforge.net/project/virtualgl/3.1/virtualgl_3.1_$ARCH.deb" && \ + curl -L -o virtualgl.deb "https://github.com/VirtualGL/virtualgl/releases/download/3.1.1/virtualgl_3.1.1_$ARCH.deb" && \ dpkg -i virtualgl.deb RUN usermod -aG video batman diff --git a/.github/workflows/setup/action.yaml b/.github/workflows/setup/action.yaml index 970d62030..701675942 100644 --- a/.github/workflows/setup/action.yaml +++ b/.github/workflows/setup/action.yaml @@ -60,4 +60,4 @@ runs: find . -type f -not -executable -not -perm 644 -exec chmod 644 {} \; # build our docker image - shell: bash - run: eval ${{ env.BUILD }} \ No newline at end of file + run: eval ${{ env.BUILD }} diff --git a/.github/workflows/tools_tests.yaml b/.github/workflows/tools_tests.yaml index c2f7d86e6..9a26ca9a2 100644 --- a/.github/workflows/tools_tests.yaml +++ b/.github/workflows/tools_tests.yaml @@ -39,12 +39,13 @@ jobs: source selfdrive/test/setup_vsound.sh && \ CI=1 pytest tools/sim/tests/test_metadrive_bridge.py" - test_python311: - name: test python3.11 support - runs-on: ubuntu-latest - timeout-minutes: 5 + test_compatibility: + name: test 20.04 + 3.11 + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 + with: + submodules: true - name: Installing ubuntu dependencies run: INSTALL_EXTRA_PACKAGES=no tools/install_ubuntu_dependencies.sh - name: Installing python @@ -57,6 +58,25 @@ jobs: run: pip install poetry==1.7.0 - name: Installing python dependencies run: poetry install --no-cache --no-root + - name: git LFS + run: git lfs pull + - run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV + - name: Getting scons cache + uses: 'actions/cache@v4' + with: + path: /tmp/scons_cache + key: scons-${{ runner.arch }}-ubuntu2004-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} + restore-keys: | + scons-${{ runner.arch }}-ubuntu2004-${{ env.CACHE_COMMIT_DATE }} + scons-${{ runner.arch }}-ubuntu2004 + - name: Building openpilot + run: poetry run scons -u -j$(nproc) + - name: Saving scons cache + uses: actions/cache/save@v4 + if: github.ref == 'refs/heads/master' + with: + path: /tmp/scons_cache + key: scons-${{ runner.arch }}-ubuntu2004-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} devcontainer: name: devcontainer diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61b782364..ecde9fe77 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: - -L bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn - --builtins clear,rare,informal,usage,code,names,en-GB_to_en-US - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.8 + rev: v0.4.9 hooks: - id: ruff exclude: '^(third_party/)|(msgq/)|(panda/)|(rednose/)|(rednose_repo/)|(tinygrad/)|(tinygrad_repo/)|(teleoprtc/)|(teleoprtc_repo/)' @@ -62,6 +62,8 @@ repos: - --quiet - --force - -j8 + - --library=qt + - --include=third_party/kaitai/kaitaistream.h - repo: https://github.com/cpplint/cpplint rev: 1.6.1 hooks: @@ -98,6 +100,6 @@ repos: args: - --lock - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.4 + rev: 0.28.5 hooks: - id: check-github-workflows diff --git a/Dockerfile.openpilot_base b/Dockerfile.openpilot_base index 45b29bdcb..7228d16ef 100644 --- a/Dockerfile.openpilot_base +++ b/Dockerfile.openpilot_base @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:24.04 ENV PYTHONUNBUFFERED 1 @@ -15,7 +15,7 @@ ENV LC_ALL en_US.UTF-8 COPY tools/install_ubuntu_dependencies.sh /tmp/tools/ RUN INSTALL_EXTRA_PACKAGES=no /tmp/tools/install_ubuntu_dependencies.sh && \ rm -rf /var/lib/apt/lists/* /tmp/* && \ - cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \ + cd /usr/lib/gcc/arm-none-eabi/* && \ rm -rf arm/ thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp # Add OpenCL @@ -30,23 +30,28 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-arm-none-eabi \ tmux \ vim \ - lsb-core \ libx11-6 \ + wget \ && rm -rf /var/lib/apt/lists/* -ARG INTEL_DRIVER=l_opencl_p_18.1.0.015.tgz -ARG INTEL_DRIVER_URL=https://registrationcenter-download.intel.com/akdlm/irc_nas/vcp/15532 -RUN mkdir -p /tmp/opencl-driver-intel - -RUN cd /tmp/opencl-driver-intel && \ - echo INTEL_DRIVER is $INTEL_DRIVER && \ - curl -O $INTEL_DRIVER_URL/$INTEL_DRIVER && \ - tar -xzf $INTEL_DRIVER && \ - for i in $(basename $INTEL_DRIVER .tgz)/rpm/*.rpm; do alien --to-deb $i; done && \ - dpkg -i *.deb && \ - rm -rf $INTEL_DRIVER $(basename $INTEL_DRIVER .tgz) *.deb && \ +RUN mkdir -p /tmp/opencl-driver-intel && \ + cd /tmp/opencl-driver-intel && \ + wget https://github.com/intel/llvm/releases/download/2024-WW14/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \ + wget https://github.com/oneapi-src/oneTBB/releases/download/v2021.12.0/oneapi-tbb-2021.12.0-lin.tgz && \ + mkdir -p /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \ + cd /opt/intel/oclcpuexp_2024.17.3.0.09_rel && \ + tar -zxvf /tmp/opencl-driver-intel/oclcpuexp-2024.17.3.0.09_rel.tar.gz && \ mkdir -p /etc/OpenCL/vendors && \ - echo /opt/intel/opencl_compilers_and_libraries_18.1.0.015/linux/compiler/lib/intel64_lin/libintelocl.so > /etc/OpenCL/vendors/intel.icd && \ + echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64/libintelocl.so > /etc/OpenCL/vendors/intel_expcpu.icd && \ + cd /opt/intel && \ + tar -zxvf /tmp/opencl-driver-intel/oneapi-tbb-2021.12.0-lin.tgz && \ + ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \ + ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \ + ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbb.so.12 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \ + ln -s /opt/intel/oneapi-tbb-2021.12.0/lib/intel64/gcc4.8/libtbbmalloc.so.2 /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 && \ + mkdir -p /etc/ld.so.conf.d && \ + echo /opt/intel/oclcpuexp_2024.17.3.0.09_rel/x64 > /etc/ld.so.conf.d/libintelopenclexp.conf && \ + ldconfig -f /etc/ld.so.conf.d/libintelopenclexp.conf && \ cd / && \ rm -rf /tmp/opencl-driver-intel @@ -57,7 +62,7 @@ ENV QTWEBENGINE_DISABLE_SANDBOX 1 RUN dbus-uuidgen > /etc/machine-id ARG USER=batman -ARG USER_UID=1000 +ARG USER_UID=1001 RUN useradd -m -s /bin/bash -u $USER_UID $USER RUN usermod -aG sudo $USER RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers diff --git a/SConstruct b/SConstruct index 4a51c59a6..09df57696 100644 --- a/SConstruct +++ b/SConstruct @@ -14,6 +14,8 @@ SCons.Warnings.warningAsException(True) TICI = os.path.isfile('/TICI') AGNOS = TICI +UBUNTU_FOCAL = int(subprocess.check_output('[ -f /etc/os-release ] && . /etc/os-release && [ "$ID" = "ubuntu" ] && [ "$VERSION_ID" = "20.04" ] && echo 1 || echo 0', shell=True, encoding='utf-8').rstrip()) +Export('UBUNTU_FOCAL') Decider('MD5-timestamp') diff --git a/cereal/car.capnp b/cereal/car.capnp index 7f3f64c91..c17e1c063 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -541,8 +541,8 @@ struct CarParams { kiBP @2 :List(Float32); kiV @3 :List(Float32); kf @6 :Float32; - deadzoneBP @4 :List(Float32); - deadzoneV @5 :List(Float32); + deadzoneBPDEPRECATED @4 :List(Float32); + deadzoneVDEPRECATED @5 :List(Float32); } struct LateralINDITuning { diff --git a/cereal/log.capnp b/cereal/log.capnp index 958c5c910..7d95ceff0 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -698,7 +698,6 @@ struct ControlsState @0x97ff69c53601abf1 { personality @66 :LongitudinalPersonality; longControlState @30 :Car.CarControl.Actuators.LongControlState; - vPid @2 :Float32; vTargetLead @3 :Float32; vCruise @22 :Float32; # actual set speed vCruiseCluster @63 :Float32; # set speed to display in the UI @@ -866,6 +865,7 @@ struct ControlsState @0x97ff69c53601abf1 { canMonoTimesDEPRECATED @21 :List(UInt64); desiredCurvatureRateDEPRECATED @62 :Float32; canErrorCounterDEPRECATED @57 :UInt32; + vPidDEPRECATED @2 :Float32; } # All SI units and in device frame @@ -1060,6 +1060,11 @@ struct LongitudinalPlan @0xe00b5b3eba12876c { accels @32 :List(Float32); speeds @33 :List(Float32); jerks @34 :List(Float32); + aTarget @18 :Float32; + shouldStop @37: Bool; + allowThrottle @38: Bool; + allowBrake @39: Bool; + solverExecutionTime @35 :Float32; @@ -1076,7 +1081,6 @@ struct LongitudinalPlan @0xe00b5b3eba12876c { aCruiseDEPRECATED @17 :Float32; vTargetDEPRECATED @3 :Float32; vTargetFutureDEPRECATED @14 :Float32; - aTargetDEPRECATED @18 :Float32; vStartDEPRECATED @26 :Float32; aStartDEPRECATED @27 :Float32; vMaxDEPRECATED @20 :Float32; diff --git a/docs/CARS.md b/docs/CARS.md index 3b163dcee..43bd8a8b3 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -90,9 +90,9 @@ A supported vehicle is one that just works when you install a comma device. All |Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (Non-US only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (with HDA II) 2022-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (without HDA II) 2022-24[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq 6 (with HDA II) 2023-24[5](#footnotes)|Highway Driving Assist II|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Hyundai|Ioniq Electric 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| @@ -127,7 +127,7 @@ A supported vehicle is one that just works when you install a comma device. All |Kia|Carnival 2022-24[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Carnival (China only) 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| -|Kia|EV6 (Southeast Asia only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (Non-US only) 2022-24[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|EV6 (with HDA II) 2022-24[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|EV6 (without HDA II) 2022-24[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|6 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma 3X
- 1 comma power v2
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| diff --git a/poetry.lock b/poetry.lock index 7e772276e..58fa7dee6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. [[package]] name = "aiohttp" @@ -1074,18 +1074,18 @@ files = [ [[package]] name = "filelock" -version = "3.14.0" +version = "3.15.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.15.1-py3-none-any.whl", hash = "sha256:71b3102950e91dfc1bb4209b64be4dc8854f40e5f534428d8684f953ac847fac"}, + {file = "filelock-3.15.1.tar.gz", hash = "sha256:58a2549afdf9e02e10720eaa4d4470f56386d7a6f72edd7d0596337af8ed7ad8"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] [[package]] @@ -2270,8 +2270,8 @@ ros = ["zmq"] [package.source] type = "git" url = "https://github.com/commaai/metadrive.git" -reference = "python3.12" -resolved_reference = "0823c7491a1ab870bcd53d10a41e985a3ad1b968" +reference = "anisotropic_off" +resolved_reference = "1606c844345f98aa79f76177856e328edc6ec428" [[package]] name = "mouseinfo" @@ -2730,6 +2730,7 @@ optional = false python-versions = ">=3.6" files = [ {file = "opencv-python-4.10.0.82.tar.gz", hash = "sha256:dbc021eaa310c4145c47cd648cb973db69bb5780d6e635386cd53d3ea76bd2d5"}, + {file = "opencv_python-4.10.0.82-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:5f78652339957ec24b80a782becfb32f822d2008a865512121fad8c3ce233e9a"}, {file = "opencv_python-4.10.0.82-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:e6be19a0615aa8c4e0d34e0c7b133e26e386f4b7e9b557b69479104ab2c133ec"}, {file = "opencv_python-4.10.0.82-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b49e530f7fd86f671514b39ffacdf5d14ceb073bc79d0de46bbb6b0cad78eaf"}, {file = "opencv_python-4.10.0.82-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955c5ce8ac90c9e4636ad7f5c0d9c75b80abbe347182cfd09b0e3eec6e50472c"}, @@ -2739,8 +2740,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -2751,6 +2752,7 @@ optional = false python-versions = ">=3.6" files = [ {file = "opencv-python-headless-4.10.0.82.tar.gz", hash = "sha256:de9e742c1b9540816fbd115b0b03841d41ed0c65566b0d7a5371f98b131b7e6d"}, + {file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a09ed50ba21cc5bf5d436cb0e784ad09c692d6b1d1454252772f6c8f2c7b4088"}, {file = "opencv_python_headless-4.10.0.82-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:977a5fd21e1fe0d3d2134887db4441f8725abeae95150126302f31fcd9f548fa"}, {file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db4ec6755838b0be12510bfc9ffb014779c612418f11f4f7e6f505c36124a3aa"}, {file = "opencv_python_headless-4.10.0.82-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a37fa5276967ecf6eb297295b16b28b7a2eb3b568ca0ee469fb1a5954de298"}, @@ -2760,8 +2762,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -2925,8 +2927,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -7217,28 +7219,28 @@ docs = ["furo (==2024.4.27)", "pyenchant (==3.2.2)", "sphinx (==7.1.2)", "sphinx [[package]] name = "ruff" -version = "0.4.8" +version = "0.4.9" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"}, - {file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"}, - {file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"}, - {file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"}, - {file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"}, - {file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"}, - {file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"}, - {file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"}, - {file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"}, - {file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"}, - {file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"}, + {file = "ruff-0.4.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b262ed08d036ebe162123170b35703aaf9daffecb698cd367a8d585157732991"}, + {file = "ruff-0.4.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:98ec2775fd2d856dc405635e5ee4ff177920f2141b8e2d9eb5bd6efd50e80317"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4555056049d46d8a381f746680db1c46e67ac3b00d714606304077682832998e"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2d45ddc6d82e1190ea737341326ecbc9a61447ba331b0a8962869fcada758505"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78de3fdb95c4af084087628132336772b1c5044f6e710739d440fc0bccf4d321"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893"}, + {file = "ruff-0.4.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88bffe9c6a454bf8529f9ab9091c99490578a593cc9f9822b7fc065ee0712a06"}, + {file = "ruff-0.4.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:673bddb893f21ab47a8334c8e0ea7fd6598ecc8e698da75bcd12a7b9d0a3206e"}, + {file = "ruff-0.4.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c1aff58c31948cc66d0b22951aa19edb5af0a3af40c936340cd32a8b1ab7438"}, + {file = "ruff-0.4.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:784d3ec9bd6493c3b720a0b76f741e6c2d7d44f6b2be87f5eef1ae8cc1d54c84"}, + {file = "ruff-0.4.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:732dd550bfa5d85af8c3c6cbc47ba5b67c6aed8a89e2f011b908fc88f87649db"}, + {file = "ruff-0.4.9-py3-none-win32.whl", hash = "sha256:8064590fd1a50dcf4909c268b0e7c2498253273309ad3d97e4a752bb9df4f521"}, + {file = "ruff-0.4.9-py3-none-win_amd64.whl", hash = "sha256:e0a22c4157e53d006530c902107c7f550b9233e9706313ab57b892d7197d8e52"}, + {file = "ruff-0.4.9-py3-none-win_arm64.whl", hash = "sha256:5d5460f789ccf4efd43f265a58538a2c24dbce15dbf560676e430375f20a8198"}, + {file = "ruff-0.4.9.tar.gz", hash = "sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3"}, ] [[package]] @@ -7725,19 +7727,23 @@ widechars = ["wcwidth"] [[package]] name = "timezonefinder" -version = "6.5.0" +version = "6.5.2" description = "python package for finding the timezone of any point on earth (coordinates) offline" optional = false -python-versions = ">=3.8,<4" +python-versions = "<4,>=3.8" files = [ - {file = "timezonefinder-6.5.0-cp38-cp38-manylinux_2_35_x86_64.whl", hash = "sha256:3c70e1ea468ecac11272aa0a727450fd7cf43039ae2a2c62f7900a0a42efb6d6"}, - {file = "timezonefinder-6.5.0.tar.gz", hash = "sha256:7afdb1516927e7766deb6da80c8bd66734a44ba3d898038fe73aef1e9cd39bb0"}, + {file = "timezonefinder-6.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7ca91f25d988d69efeae020ed828534ed01f6687646a50e464d82da4704b30"}, + {file = "timezonefinder-6.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4a3bca9933ea165380d54b336227def58e62682968437cd44223a065ecff7e9"}, + {file = "timezonefinder-6.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ac4ef31d379c7ba54710462857bea36a9630ea23550ffb15550322e6988efbd"}, + {file = "timezonefinder-6.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:524ab145a2f4ce5101e52308868ecd9da0cdcc2cb3024c13356616b41a685fd6"}, + {file = "timezonefinder-6.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b93927a4996a5c01f0628eb6858c61b2a7f9f49e02c07bed2a95e96a8dc1b65e"}, + {file = "timezonefinder-6.5.2.tar.gz", hash = "sha256:3279e67d5b1b9a54d1a16f3bd4234169f14e2b90c0f58ed37bcfa7303645d610"}, ] [package.dependencies] cffi = ">=1.15.1,<2" h3 = ">=3.7.6,<4" -numpy = {version = ">=1.23,<2", markers = "python_version >= \"3.9\""} +numpy = {version = ">=1.23,<3", markers = "python_version >= \"3.9\""} setuptools = ">=65.5" [package.extras] @@ -7824,13 +7830,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -8009,7 +8015,103 @@ files = [ doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +[[package]] +name = "zstd" +version = "1.5.5.1" +description = "ZSTD Bindings for Python" +optional = false +python-versions = "*" +files = [ + {file = "zstd-1.5.5.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:555779789bc75cd05089c3ba857f45a0a8c4b87d45e5ced02fec77fa8719237a"}, + {file = "zstd-1.5.5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:86496bd4830cdb7b4b05a9ce6ce2baee87d327ff90845da4ee308452bfbbed4e"}, + {file = "zstd-1.5.5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b487c2e67ed42a4e0d47997d209f4456b01b334023083ef61873f79577c84c62"}, + {file = "zstd-1.5.5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:45ccd45a5b681088fca1a863ca9236ded5112b8011f1d5bf69e908f5eb32023a"}, + {file = "zstd-1.5.5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8403fe84207d8b0c7b17bca6c4caad431ac765b1b9b626ad9fae4bb93a64a9d8"}, + {file = "zstd-1.5.5.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:0ab979c6357b8927f0c025ea2f72f25e15d03ce17a8a6c1789e2d5b108bf39ae"}, + {file = "zstd-1.5.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:98cbee6c1b2fe85f02fd475d885f98363c63bc64eebc249d7eb7469a0ff70283"}, + {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9962714b89641301029f3832bdf07c20f60b9e64e39e8d7b6253451a82b54f5c"}, + {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f59cc92d71537f8082306f75aa403ddb4a4a1069a39f104525673110e4d23f7"}, + {file = "zstd-1.5.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:569f13d0c926ddafceebce8ac73baddfc2bd9cbbbbc922b6b3073338cc43dae6"}, + {file = "zstd-1.5.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba530c44f252016acc6ef906d7d2070c1ad0cfe835c498fdcd37493e4772ac6e"}, + {file = "zstd-1.5.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ee3496ed8fff3add6c6e658b207f18d96474c3db0c28ab7a69623380b1a0a8c"}, + {file = "zstd-1.5.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:530d69bea2791cde8afa7fe988f3a37c3ba37015f6a1d5593c0500f089f3090e"}, + {file = "zstd-1.5.5.1-cp310-cp310-win32.whl", hash = "sha256:cf179e51f447b6a7ff47e449fcb98fb5fe15aedcc90401697cf7c93dd6e4434e"}, + {file = "zstd-1.5.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:5f5e6e0805d710d7509c8d175a467eb89c631a4142b1a630ceeb8e3e3138d152"}, + {file = "zstd-1.5.5.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:022f935a8666e08f0fff6204938a84d9fe4fcd8235a205787275933a07a164fb"}, + {file = "zstd-1.5.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3d15a2d18dac8bcafdde52fdf5d40ecae1f73b7de19b171f42339d2e51346d0"}, + {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45b9c67989f50ba63ffa0c50c9eaa037c2d14abacb0813e838ad705135245b4b"}, + {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97da6a842ba7e4acf8bba7c596057143ee39b3c4a467196c2096d460e44accd6"}, + {file = "zstd-1.5.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dafd492fb8ee4ae04c81ab00f5f137860e7071f611335dd4cdb1c38bd8f11bc"}, + {file = "zstd-1.5.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9ee83e0bcbfd776200b026b3b9e86c6c86b8f414749f58d87c85dcf456b27066"}, + {file = "zstd-1.5.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ae2fd4bc8ea772a7b5f1acd1cac9e34bb9cd8fcde191f170092fdeea779a3a12"}, + {file = "zstd-1.5.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:edea52a0109f48fd46f4763689d3d356dcafd20ddf6789c559a1bd2e62b40a32"}, + {file = "zstd-1.5.5.1-cp311-cp311-win32.whl", hash = "sha256:88410481209520298ec4430e0d1d57e004c45e0b27c3035674fb182ccd2d8b7b"}, + {file = "zstd-1.5.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:dce18aaefbacf8b133367be86beec670baf68c0420bfcca49be08dbdbf933db6"}, + {file = "zstd-1.5.5.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:634dc632f7cf87e95dabf74dcf682e3507bd5cb9dd1bcdb81f92a6521aab0bd2"}, + {file = "zstd-1.5.5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:608414eb75ead573891d97a1e529848b8f31749d21a440e80838548a19d8c0e6"}, + {file = "zstd-1.5.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:384128f7a731e3f45da49976591cec03fc4079e70653df10d9ea43a1d3b49d50"}, + {file = "zstd-1.5.5.1-cp35-cp35m-win32.whl", hash = "sha256:4bce254174ef05cea01021d67e18489d5d08db1168e758b62ecee121572a52a9"}, + {file = "zstd-1.5.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:3f0ff81232b49d7eb4f4d9e6f92443c9d242c139ad98ffedac0e889568f900ce"}, + {file = "zstd-1.5.5.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a871df41b801a260cc849c2c76f300ebb9d286c4b7a1fd6ce45fe0c91340b767"}, + {file = "zstd-1.5.5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5a53860dbfbea281eb690ce09cae28967cf1df8e6d7560e4a8bf5b9fcb258147"}, + {file = "zstd-1.5.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a37cbc0580fdfd66c8b3ec65f9af00a4a34e9781b54dfb89f04d301dc375c90a"}, + {file = "zstd-1.5.5.1-cp36-cp36m-win32.whl", hash = "sha256:5531b683539ae1f7b2ad23dacee8a73e5d7eaa6702ea8df5a24bd3318647dee1"}, + {file = "zstd-1.5.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eeaff418269b41eee8c7971fbba9d32d07d3f6aa26f962a72aff725071096a1b"}, + {file = "zstd-1.5.5.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8bd6a9050de8bbe844447348372ca17d01bc05207619f6a5d448567d111b5cd9"}, + {file = "zstd-1.5.5.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2ece3d20ef357370584f304407fbd1e4ff9c231209320e08a889b8e3725d56e"}, + {file = "zstd-1.5.5.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687f9e03dc9f9b8803840425bb23bf6bc700888b4860afcf43c4f238102752d2"}, + {file = "zstd-1.5.5.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a649daac9c8f1b37d29f2b3d0a43f134061659b54877fe4b0da6df2965dc91f"}, + {file = "zstd-1.5.5.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bddc7e3c3ce31c01fe1edaa7c03c0b9e71eadf4ce1609746d32f86d95a0449e6"}, + {file = "zstd-1.5.5.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:12bf8e04add8bb84f9fe9117f3de6d9394eade6a5a82fe4d6bd95914fc6ef423"}, + {file = "zstd-1.5.5.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9e6a15fa4d2e65c5902ab2a4e41279ac126cb371ce6c3c75ad5789bb20dd1f54"}, + {file = "zstd-1.5.5.1-cp37-cp37m-win32.whl", hash = "sha256:a1c269243a4321beb948635b544ccbe6390846358ace620fd000ab7099011d9c"}, + {file = "zstd-1.5.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:91366e36773241cb4b049a32f4495d33dd274df1eea5b55396f5f3984a3de22e"}, + {file = "zstd-1.5.5.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:d3ce2cb310690994274d133ea7f269dd4b81799fdbce158690556209723d7d4e"}, + {file = "zstd-1.5.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e0c87bfbfa9d852f79c90bcd7426c3ba46cf3285e6984013636d4fc854ba9230"}, + {file = "zstd-1.5.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce6d829d515f272fddb3a87e1a5f32cc0f1a7b0cba24d360c89f4a165b74b"}, + {file = "zstd-1.5.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e05f81f346213b23ed1b12d84fc1f72e65eacd8978e1e88facf185c82bd3d053"}, + {file = "zstd-1.5.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43ec66c4c3a76351c672c6ef9f0ff3412fca9ede0a56d18dddaf6418a93faef8"}, + {file = "zstd-1.5.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:58e554e91e0d49f4f2b2df390cdd0f64aa9b6fd5f4dcb208c094bfd079b30f3a"}, + {file = "zstd-1.5.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:883c6d3b6f5574e1765ca97f4b6a41b69094a41be56175552faebc0e0e43b65e"}, + {file = "zstd-1.5.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d52b6932cab5419c434bccfea3e5640e755369fc9eeb51e3d17e15bf8e8cb103"}, + {file = "zstd-1.5.5.1-cp38-cp38-win32.whl", hash = "sha256:dcaf44270ec88552e969be4dd3359b34aa3065663ccd8168a257c78f150a356c"}, + {file = "zstd-1.5.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:627f12cb7035723c8f3d8d4cefcad6d950ed9cba33fd3eb46bae04ccab479234"}, + {file = "zstd-1.5.5.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:c0dab132c1a5a7cc838a7c3e4e380ad153b9d7bd1fadafabf6cfeb780b916201"}, + {file = "zstd-1.5.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d4ab0a5dd9a41d3b083304beee7ada40ee36431acbeb75132032f4fe5cf0490a"}, + {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f6e38f496d287020658c6b4cdb5e815ecc6998889bd0f1f9ab0825f2e3d74ef"}, + {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0096c8ee0ed4bfe406bc961019f55552109e19771bfd3eb32d2af56ea27085c"}, + {file = "zstd-1.5.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a0f1527728c50b6aa8f04b47a07580f0ae13cfc6c6d9c96bb0bdf5259487559"}, + {file = "zstd-1.5.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6a64e420c904063c5c3de53c00ec0993ebc0a48cebbef97dc6c768562c5abab5"}, + {file = "zstd-1.5.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:03444e357b7632c64480a81ce7095242dab9d7f8aed317326563ef6c663263eb"}, + {file = "zstd-1.5.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:88b9a10f80d2b87bf8cc1a1fc20a815ed92b5eefdc15cbe8062021f0b5a26a10"}, + {file = "zstd-1.5.5.1-cp39-cp39-win32.whl", hash = "sha256:c91cc1606eb8b3a6fed11faaef4c6e55f1133d70cf0db0c829a2cf9c2ac1dfd9"}, + {file = "zstd-1.5.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:f462e2ebf26dcbfc2c8dddd6b5c56859683f0b77edb8f268e637f7d390a58f74"}, + {file = "zstd-1.5.5.1-pp27-pypy_73-macosx_10_14_x86_64.whl", hash = "sha256:c63f916732e3e309e49ec95e7a0af5d37ff1321f3df2aac10e507bd2b56fceda"}, + {file = "zstd-1.5.5.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:50d4850d758bb033df50722cc13ed913b2afcd5385250be4f3ffb79a26b319c3"}, + {file = "zstd-1.5.5.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:0412d666515e78a91ada7e2d78e9dd6b25ddda1b41623b145b99653275c7f3ce"}, + {file = "zstd-1.5.5.1-pp36-pypy36_pp73-macosx_10_14_x86_64.whl", hash = "sha256:0ea91f74869a3cdcb2dde08f8f30ee3da72782c5d1737afed9c703232815864e"}, + {file = "zstd-1.5.5.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:477548897dc2b8b595af7bec5f0f55dcba8e9a282335f687cc663b52b171357b"}, + {file = "zstd-1.5.5.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c518938b57a56001ee04dcf79a432152f5bd431416f3b22819ba959bc6054d89"}, + {file = "zstd-1.5.5.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:894a8fe0228d5e24dc286a8d98eb0ce2883f8e2e57f3b7e7619ebdb67967120a"}, + {file = "zstd-1.5.5.1-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:42ec0a4ae9bedd9909fa4f580f3c800469da1b631faeaa94f204e1b66c767fa2"}, + {file = "zstd-1.5.5.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d56dedaa04ab8ecc23492972b12e0bf8529f64c9bceb28c11f43c2369c9768b3"}, + {file = "zstd-1.5.5.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5b060770d796e4c01f5848b345c3cea8a177ab4e7cd95a1963a355042d429e1"}, + {file = "zstd-1.5.5.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea04805ef6e1cb93d6e5d6bbc7a03bc75a5c733fd352d5aaa81109986fdf1ef"}, + {file = "zstd-1.5.5.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:405c28a35756e57a434bbd7ed29dc5e6490cd2fc2118cbf78b60eaebd134f5e9"}, + {file = "zstd-1.5.5.1-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:c42e630443b01a891277426365a51a2aa630b059ce675992c70c1928d30eccb4"}, + {file = "zstd-1.5.5.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1520d23f24f26cdfbcdb4dc86947446b8f694838bfce728d7fc4b3492397357c"}, + {file = "zstd-1.5.5.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4730737f63cf802321743ded6acc85e747e7f5587c5ba2e51a760bf009f7de"}, + {file = "zstd-1.5.5.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9f8c014395e89ad7f67ffe873c0fa1d8e9b4dea8b1801d24e8d9ccd8259858d"}, + {file = "zstd-1.5.5.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5d9ba4f6af0945809bfa3387c6a1208a22937a876521b9ec347e7183d623311b"}, + {file = "zstd-1.5.5.1-pp39-pypy39_pp73-macosx_10_14_x86_64.whl", hash = "sha256:04dfd9f46b0b0b1bc413884fe028b726febcb726d4f66e3cf8afc00c2d9026bf"}, + {file = "zstd-1.5.5.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af52436a2eb5caa925d95461973984cb34d472a963b6be1c0a9f2dfbafad096f"}, + {file = "zstd-1.5.5.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610928b888a2e7ae9d2018ffa814859d47ec4ba75f89a1188ab4eb9232636ee5"}, + {file = "zstd-1.5.5.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee3c9feea99c7f4ff43129a885da056b5aa0cde3f7876bf6397bfb9433f44352"}, + {file = "zstd-1.5.5.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ac9768eeb3c6b530db93de2fec9b363776075dc8a00ee4049612ba5397ca8e"}, + {file = "zstd-1.5.5.1.tar.gz", hash = "sha256:1ef980abf0e1e072b028d2d76ef95b476632651c96225cf30b619c6eef625672"}, +] + [metadata] lock-version = "2.0" python-versions = ">=3.11, <3.13" -content-hash = "b5c62b81368f0e972fbcf0dbb88746f3ae930ebdc171ad004c926f3588d1f244" +content-hash = "32df734a6ad6ad6a665c94c41cd0e3643a48174904faaf6f21c5c84f350f3cec" diff --git a/pyproject.toml b/pyproject.toml index 7b780c87f..374b0f46c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,35 +139,20 @@ psutil = "*" timezonefinder = "*" # just used for nav ETA pycryptodome = "*" # used in updated/casync, panda, body, and a test -[tool.poetry.group.dev.dependencies] -av = "*" -azure-identity = "*" -azure-storage-blob = "*" -breathe = "*" -control = "*" -coverage = "*" -dictdiffer = "*" -flaky = "*" -hypothesis = "~6.47" -inputs = "*" +#logreader +zstd = "*" + +[tool.poetry.group.docs.dependencies] Jinja2 = "*" -lru-dict = "*" -matplotlib = "*" -metadrive-simulator = { git = "https://github.com/commaai/metadrive.git", branch = "python3.12", markers = "platform_machine != 'aarch64'" } # no linux/aarch64 wheels for certain dependencies -mpld3 = "*" +sphinx = "*" +sphinx-rtd-theme = "*" +sphinx-sitemap = "*" + +[tool.poetry.group.testing.dependencies] +coverage = "*" +hypothesis = "~6.47" mypy = "*" -myst-parser = "*" -natsort = "*" -opencv-python-headless = "*" -parameterized = "^0.8" -#pprofile = "*" -polyline = "*" pre-commit = "*" -pyautogui = "*" -pyopencl = { version = "*", markers = "platform_machine != 'aarch64'" } # broken on arm64 -pygame = "*" -pywinctl = "*" -pyprof2calltree = "*" pytest = "*" pytest-cov = "*" pytest-cpp = "*" @@ -178,11 +163,33 @@ pytest-randomly = "*" pytest-asyncio = "*" pytest-mock = "*" pytest-repeat = "*" -rerun-sdk = "*" ruff = "*" -sphinx = "*" -sphinx-rtd-theme = "*" -sphinx-sitemap = "*" + +[tool.poetry.group.dev.dependencies] +av = "*" +azure-identity = "*" +azure-storage-blob = "*" +breathe = "*" +control = "*" +dictdiffer = "*" +flaky = "*" +inputs = "*" +lru-dict = "*" +matplotlib = "*" +metadrive-simulator = { git = "https://github.com/commaai/metadrive.git", branch = "anisotropic_off", markers = "platform_machine != 'aarch64'" } # no linux/aarch64 wheels for certain dependencies +mpld3 = "*" +myst-parser = "*" +natsort = "*" +opencv-python-headless = "*" +parameterized = "^0.8" +#pprofile = "*" +polyline = "*" +pyautogui = "*" +pygame = "*" +pyopencl = { version = "*", markers = "platform_machine != 'aarch64'" } # broken on arm64 +pywinctl = "*" +pyprof2calltree = "*" +rerun-sdk = "*" tabulate = "*" types-requests = "*" types-tabulate = "*" diff --git a/release/check-submodules.sh b/release/check-submodules.sh index bff8d7a28..5305cecd3 100755 --- a/release/check-submodules.sh +++ b/release/check-submodules.sh @@ -1,7 +1,7 @@ #!/bin/bash while read hash submodule ref; do - git -C $submodule fetch --depth 2000 origin master + git -C $submodule fetch --depth 3000 origin master git -C $submodule branch -r --contains $hash | grep "origin/master" if [ "$?" -eq 0 ]; then echo "$submodule ok" diff --git a/selfdrive/car/ecu_addrs.py b/selfdrive/car/ecu_addrs.py index e7a9fbcf2..756cd7f96 100755 --- a/selfdrive/car/ecu_addrs.py +++ b/selfdrive/car/ecu_addrs.py @@ -71,25 +71,35 @@ def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, que if __name__ == "__main__": import argparse + from openpilot.common.params import Params + from openpilot.selfdrive.car.fw_versions import set_obd_multiplexing parser = argparse.ArgumentParser(description='Get addresses of all ECUs') parser.add_argument('--debug', action='store_true') parser.add_argument('--bus', type=int, default=1) + parser.add_argument('--no-obd', action='store_true') parser.add_argument('--timeout', type=float, default=1.0) args = parser.parse_args() logcan = messaging.sub_sock('can') sendcan = messaging.pub_sock('sendcan') - time.sleep(1.0) + # Set up params for pandad + params = Params() + params.remove("FirmwareQueryDone") + params.put_bool("IsOnroad", False) + time.sleep(0.2) # thread is 10 Hz + params.put_bool("IsOnroad", True) + + set_obd_multiplexing(params, not args.no_obd) print("Getting ECU addresses ...") ecu_addrs = get_all_ecu_addrs(logcan, sendcan, args.bus, args.timeout, debug=args.debug) print() - print("Found ECUs on addresses:") + print("Found ECUs on rx addresses:") for addr, subaddr, _ in ecu_addrs: - msg = f" 0x{hex(addr)}" + msg = f" {hex(addr)}" if subaddr is not None: msg += f" (sub-address: {hex(subaddr)})" print(msg) diff --git a/selfdrive/car/ford/interface.py b/selfdrive/car/ford/interface.py index 2ef5d427e..54cf86510 100644 --- a/selfdrive/car/ford/interface.py +++ b/selfdrive/car/ford/interface.py @@ -22,10 +22,6 @@ class CarInterface(CarInterfaceBase): ret.steerActuatorDelay = 0.2 ret.steerLimitTimer = 1.0 - ret.longitudinalTuning.kpBP = [0.] - ret.longitudinalTuning.kpV = [0.5] - ret.longitudinalTuning.kiV = [0.] - CAN = CanBus(fingerprint=fingerprint) cfgs = [get_safety_config(car.CarParams.SafetyModel.ford)] if CAN.main >= 4: diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index 2e194b12f..5ea2b1989 100755 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -94,11 +94,7 @@ class CarInterface(CarInterfaceBase): else: ret.transmissionType = TransmissionType.automatic - ret.longitudinalTuning.deadzoneBP = [0.] - ret.longitudinalTuning.deadzoneV = [0.15] - - ret.longitudinalTuning.kpBP = [5., 35.] - ret.longitudinalTuning.kiBP = [0.] + ret.longitudinalTuning.kiBP = [5., 35.] if candidate in CAMERA_ACC_CAR: ret.experimentalLongitudinalAvailable = True @@ -110,8 +106,7 @@ class CarInterface(CarInterfaceBase): ret.minSteerSpeed = 10 * CV.KPH_TO_MS # Tuning for experimental long - ret.longitudinalTuning.kpV = [2.0, 1.5] - ret.longitudinalTuning.kiV = [0.72] + ret.longitudinalTuning.kiV = [2.0, 1.5] ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling ret.vEgoStopping = 0.25 ret.vEgoStarting = 0.25 @@ -131,8 +126,7 @@ class CarInterface(CarInterfaceBase): ret.minSteerSpeed = 7 * CV.MPH_TO_MS # Tuning - ret.longitudinalTuning.kpV = [2.4, 1.5] - ret.longitudinalTuning.kiV = [0.36] + ret.longitudinalTuning.kiV = [2.4, 1.5] # 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 diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 43a4454b9..fdcba8bb8 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -72,17 +72,13 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.pid.kf = 0.00006 # conservative feed-forward if candidate in HONDA_BOSCH: - ret.longitudinalTuning.kpV = [0.25] - ret.longitudinalTuning.kiV = [0.05] ret.longitudinalActuatorDelay = 0.5 # s if candidate in HONDA_BOSCH_RADARLESS: ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model else: # default longitudinal tuning for all hondas - ret.longitudinalTuning.kpBP = [0., 5., 35.] - ret.longitudinalTuning.kpV = [1.2, 0.8, 0.5] - ret.longitudinalTuning.kiBP = [0., 35.] - ret.longitudinalTuning.kiV = [0.18, 0.12] + ret.longitudinalTuning.kiBP = [0., 5., 35.] + ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5] eps_modified = False for fw in car_fw: diff --git a/selfdrive/car/hyundai/fingerprints.py b/selfdrive/car/hyundai/fingerprints.py index d115283dd..e489a3cf6 100644 --- a/selfdrive/car/hyundai/fingerprints.py +++ b/selfdrive/car/hyundai/fingerprints.py @@ -2,6 +2,9 @@ from cereal import car from openpilot.selfdrive.car.hyundai.values import CAR +# The existence of SCC or RDR in the fwdRadar FW usually determines the radar's function, +# i.e. if it sends the SCC messages or if another ECU like the camera or ADAS Driving ECU does + Ecu = car.CarParams.Ecu FINGERPRINTS = { @@ -995,6 +998,8 @@ FW_VERSIONS = { b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.06 99211-GI010 230110', + b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI100 230915', + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.00 99211-GI100 230915', ], }, CAR.HYUNDAI_IONIQ_6: { diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 0ba4dcb5e..ecedf3fd7 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -80,12 +80,8 @@ class CarInterface(CarInterfaceBase): # *** longitudinal control *** if candidate in CANFD_CAR: - ret.longitudinalTuning.kpV = [0.1] - ret.longitudinalTuning.kiV = [0.0] ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR) else: - ret.longitudinalTuning.kpV = [0.5] - ret.longitudinalTuning.kiV = [0.0] ret.experimentalLongitudinalAvailable = candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR) ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable ret.pcmCruise = not ret.openpilotLongitudinalControl diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index c489ea004..8cb29c803 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -306,9 +306,9 @@ class CAR(Platforms): ) HYUNDAI_IONIQ_5 = HyundaiCanFDPlatformConfig( [ - HyundaiCarDocs("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), - HyundaiCarDocs("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), - HyundaiCarDocs("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), + HyundaiCarDocs("Hyundai Ioniq 5 (Non-US only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), + HyundaiCarDocs("Hyundai Ioniq 5 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), + HyundaiCarDocs("Hyundai Ioniq 5 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), ], CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65), flags=HyundaiFlags.EV, @@ -478,7 +478,7 @@ class CAR(Platforms): ) KIA_EV6 = HyundaiCanFDPlatformConfig( [ - HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), + HyundaiCarDocs("Kia EV6 (Non-US only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])), HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])) ], diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 5eac6062a..e5762ed52 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -202,13 +202,11 @@ class CarInterfaceBase(ABC): ret.vEgoStopping = 0.5 ret.vEgoStarting = 0.5 ret.stoppingControl = True - ret.longitudinalTuning.deadzoneBP = [0.] - ret.longitudinalTuning.deadzoneV = [0.] ret.longitudinalTuning.kf = 1. ret.longitudinalTuning.kpBP = [0.] - ret.longitudinalTuning.kpV = [1.] + ret.longitudinalTuning.kpV = [0.] ret.longitudinalTuning.kiBP = [0.] - ret.longitudinalTuning.kiV = [1.] + ret.longitudinalTuning.kiV = [0.] # TODO estimate car specific lag, use .15s for now ret.longitudinalActuatorDelay = 0.15 ret.steerLimitTimer = 1.0 diff --git a/selfdrive/car/subaru/interface.py b/selfdrive/car/subaru/interface.py index 1aa4bd95e..cb0093437 100644 --- a/selfdrive/car/subaru/interface.py +++ b/selfdrive/car/subaru/interface.py @@ -91,11 +91,6 @@ class CarInterface(CarInterfaceBase): ret.flags |= SubaruFlags.DISABLE_EYESIGHT.value if ret.openpilotLongitudinalControl: - ret.longitudinalTuning.kpBP = [0., 5., 35.] - ret.longitudinalTuning.kpV = [0.8, 1.0, 1.5] - ret.longitudinalTuning.kiBP = [0., 35.] - ret.longitudinalTuning.kiV = [0.54, 0.36] - ret.stoppingControl = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_SUBARU_LONG diff --git a/selfdrive/car/tesla/interface.py b/selfdrive/car/tesla/interface.py index 09de10b54..deb0e0023 100755 --- a/selfdrive/car/tesla/interface.py +++ b/selfdrive/car/tesla/interface.py @@ -18,11 +18,6 @@ class CarInterface(CarInterfaceBase): ret.steerControlType = car.CarParams.SteerControlType.angle - # Set kP and kI to 0 over the whole speed range to have the planner accel as actuator command - ret.longitudinalTuning.kpBP = [0] - ret.longitudinalTuning.kpV = [0] - ret.longitudinalTuning.kiBP = [0] - ret.longitudinalTuning.kiV = [0] ret.longitudinalActuatorDelay = 0.5 # s ret.radarTimeStep = (1.0 / 8) # 8Hz diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index dd3a8f633..b1156f0e8 100755 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -225,7 +225,6 @@ routes = [ CarTestRoute("cd9cff4b0b26c435|2021-05-13--15-12-39", TOYOTA.TOYOTA_CHR), CarTestRoute("57858ede0369a261|2021-05-18--20-34-20", TOYOTA.TOYOTA_CHR), # hybrid CarTestRoute("ea8fbe72b96a185c|2023-02-08--15-11-46", TOYOTA.TOYOTA_CHR_TSS2), - CarTestRoute("ea8fbe72b96a185c|2023-02-22--09-20-34", TOYOTA.TOYOTA_CHR_TSS2), # openpilot longitudinal, with smartDSU CarTestRoute("6719965b0e1d1737|2023-02-09--22-44-05", TOYOTA.TOYOTA_CHR_TSS2), # hybrid CarTestRoute("6719965b0e1d1737|2023-08-29--06-40-05", TOYOTA.TOYOTA_CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled CarTestRoute("14623aae37e549f3|2021-10-24--01-20-49", TOYOTA.TOYOTA_PRIUS_V), diff --git a/selfdrive/car/tests/test_car_interfaces.py b/selfdrive/car/tests/test_car_interfaces.py index 19096c23e..a6f74d685 100644 --- a/selfdrive/car/tests/test_car_interfaces.py +++ b/selfdrive/car/tests/test_car_interfaces.py @@ -75,7 +75,6 @@ class TestCarInterfaces: # Longitudinal sanity checks assert len(car_params.longitudinalTuning.kpV) == len(car_params.longitudinalTuning.kpBP) assert len(car_params.longitudinalTuning.kiV) == len(car_params.longitudinalTuning.kiBP) - assert len(car_params.longitudinalTuning.deadzoneV) == len(car_params.longitudinalTuning.deadzoneBP) # Lateral sanity checks if car_params.steerControlType != car.CarParams.SteerControlType.angle: diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 8315f24ae..a596881c3 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -132,8 +132,7 @@ class CarState(CarStateBase): cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp if self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value: - if not (self.CP.flags & ToyotaFlags.SMART_DSU.value): - self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"] + self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"] ret.stockFcw = bool(cp_acc.vl["PCS_HUD"]["FCW"]) # some TSS2 cars have low speed lockout permanently set, so ignore on those cars @@ -167,13 +166,11 @@ class CarState(CarStateBase): if self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR: self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"] - if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER): + if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR): # distance button is wired to the ACC module (camera or radar) self.prev_distance_button = self.distance_button if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR): self.distance_button = cp_acc.vl["ACC_CONTROL"]["DISTANCE"] - else: - self.distance_button = cp.vl["SDSU"]["FD_BUTTON"] return ret @@ -208,12 +205,9 @@ class CarState(CarStateBase): messages.append(("BSM", 1)) if CP.carFingerprint in RADAR_ACC_CAR and not CP.flags & ToyotaFlags.DISABLE_RADAR.value: - if not CP.flags & ToyotaFlags.SMART_DSU.value: - messages += [ - ("ACC_CONTROL", 33), - ] messages += [ ("PCS_HUD", 1), + ("ACC_CONTROL", 33), ] if CP.carFingerprint not in (TSS2_CAR - RADAR_ACC_CAR) and not CP.enableDsu and not CP.flags & ToyotaFlags.DISABLE_RADAR.value: @@ -221,11 +215,6 @@ class CarState(CarStateBase): ("PRE_COLLISION", 33), ] - if CP.flags & ToyotaFlags.SMART_DSU and not CP.flags & ToyotaFlags.RADAR_CAN_FILTER: - messages += [ - ("SDSU", 100), - ] - return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0) @staticmethod diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 3ea05f9fe..6487257bb 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -44,18 +44,9 @@ class CarInterface(CarInterfaceBase): stop_and_go = candidate in TSS2_CAR - # Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it - # 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs - if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR): - ret.flags |= ToyotaFlags.SMART_DSU.value - - if 0x2AA in fingerprint[0] and candidate in NO_DSU_CAR: - ret.flags |= ToyotaFlags.RADAR_CAN_FILTER.value - # 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.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) if candidate == CAR.TOYOTA_PRIUS: stop_and_go = True @@ -98,7 +89,7 @@ class CarInterface(CarInterfaceBase): # TODO: these models can do stop and go, but unclear if it requires sDSU or unplugging DSU. # For now, don't list stop and go functionality in the docs if ret.flags & ToyotaFlags.SNG_WITHOUT_DSU: - stop_and_go = stop_and_go or bool(ret.flags & ToyotaFlags.SMART_DSU.value) or (ret.enableDsu and not docs) + stop_and_go = stop_and_go or (ret.enableDsu and not docs) ret.centerToFront = ret.wheelbase * 0.44 @@ -110,28 +101,20 @@ class CarInterface(CarInterfaceBase): # 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) - # 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/TSS-P radar-based ACC cars, gate longitudinal behind experimental toggle - use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU) if candidate in (RADAR_ACC_CAR | NO_DSU_CAR): - ret.experimentalLongitudinalAvailable = use_sdsu or candidate in RADAR_ACC_CAR + ret.experimentalLongitudinalAvailable = candidate in RADAR_ACC_CAR - if not use_sdsu: - # Disabling radar is only supported on TSS2 radar-ACC cars - if experimental_long and candidate in RADAR_ACC_CAR: - ret.flags |= ToyotaFlags.DISABLE_RADAR.value - else: - use_sdsu = use_sdsu and experimental_long + # Disabling radar is only supported on TSS2 radar-ACC cars + if experimental_long and candidate in RADAR_ACC_CAR: + ret.flags |= ToyotaFlags.DISABLE_RADAR.value # 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 - # - TSS2 radar ACC cars w/o smartDSU installed (disables radar) - # - TSS-P DSU-less cars w/ CAN filter installed (no radar parser yet) - ret.openpilotLongitudinalControl = use_sdsu or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR) or bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value) + # - TSS2 radar ACC cars (disables radar) + ret.openpilotLongitudinalControl = ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR) or bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value) ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR if not ret.openpilotLongitudinalControl: @@ -142,28 +125,21 @@ class CarInterface(CarInterfaceBase): ret.minEnableSpeed = -1. if stop_and_go else MIN_ACC_SPEED tune = ret.longitudinalTuning - tune.deadzoneBP = [0., 9.] - tune.deadzoneV = [.0, .15] if candidate in TSS2_CAR: - tune.kpBP = [0., 5., 20.] - tune.kpV = [1.3, 1.0, 0.7] - tune.kiBP = [0., 5., 12., 20., 27.] - tune.kiV = [.35, .23, .20, .17, .1] - if candidate in TSS2_CAR: - ret.vEgoStopping = 0.25 - ret.vEgoStarting = 0.25 - ret.stoppingDecelRate = 0.3 # reach stopping target smoothly + tune.kpV = [0.0] + tune.kiV = [0.5] + ret.vEgoStopping = 0.25 + ret.vEgoStarting = 0.25 + ret.stoppingDecelRate = 0.3 # reach stopping target smoothly else: - tune.kpBP = [0., 5., 35.] - tune.kiBP = [0., 35.] - tune.kpV = [3.6, 2.4, 1.5] - tune.kiV = [0.54, 0.36] + tune.kiBP = [0., 5., 35.] + tune.kiV = [3.6, 2.4, 1.5] return ret @staticmethod def init(CP, logcan, sendcan): - # disable radar if alpha longitudinal toggled on radar-ACC car without CAN filter/smartDSU + # disable radar if alpha longitudinal toggled on radar-ACC car if CP.flags & ToyotaFlags.DISABLE_RADAR.value: communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL]) disable_ecu(logcan, sendcan, bus=0, addr=0x750, sub_addr=0xf, com_cont_req=communication_control) @@ -172,7 +148,7 @@ class CarInterface(CarInterfaceBase): def _update(self, c): ret = self.CS.update(self.cp, self.cp_cam) - if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER): + if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR): ret.buttonEvents = create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}) # events diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 2e781ad0a..8022e8b3a 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -44,9 +44,7 @@ class CarControllerParams: class ToyotaFlags(IntFlag): # Detected flags HYBRID = 1 - SMART_DSU = 2 DISABLE_RADAR = 4 - RADAR_CAN_FILTER = 1024 # Static flags TSS2 = 8 @@ -56,7 +54,7 @@ class ToyotaFlags(IntFlag): # these cars use the Lane Tracing Assist (LTA) message for lateral control ANGLE_CONTROL = 128 NO_STOP_TIMER = 256 - # these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU + # these cars are speculated to allow stop and go when the DSU is unplugged SNG_WITHOUT_DSU = 512 diff --git a/selfdrive/car/volkswagen/interface.py b/selfdrive/car/volkswagen/interface.py index 91cd300e9..77e56875b 100644 --- a/selfdrive/car/volkswagen/interface.py +++ b/selfdrive/car/volkswagen/interface.py @@ -96,8 +96,6 @@ class CarInterface(CarInterfaceBase): ret.stopAccel = -0.55 ret.vEgoStarting = 0.1 ret.vEgoStopping = 0.5 - ret.longitudinalTuning.kpV = [0.1] - ret.longitudinalTuning.kiV = [0.0] ret.autoResumeSng = ret.minEnableSpeed == -1 return ret diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 5746777f1..393824741 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -580,13 +580,12 @@ class Controls: if not CC.latActive: self.LaC.reset() if not CC.longActive: - self.LoC.reset(v_pid=CS.vEgo) + self.LoC.reset() if not self.joystick_mode: # accel PID loop pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, self.v_cruise_helper.v_cruise_kph * CV.KPH_TO_MS) - t_since_plan = (self.sm.frame - self.sm.recv_frame['longitudinalPlan']) * DT_CTRL - actuators.accel = self.LoC.update(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan) + actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits) # Steering PID loop and lateral MPC self.desired_curvature = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature) @@ -769,7 +768,6 @@ class Controls: controlsState.state = self.state controlsState.engageable = not self.events.contains(ET.NO_ENTRY) controlsState.longControlState = self.LoC.long_control_state - controlsState.vPid = float(self.LoC.v_pid) controlsState.vCruise = float(self.v_cruise_helper.v_cruise_kph) controlsState.vCruiseCluster = float(self.v_cruise_helper.v_cruise_cluster_kph) controlsState.upAccelCmd = float(self.LoC.pid.p) @@ -860,7 +858,7 @@ class Controls: while True: self.step() self.rk.monitor_time() - except SystemExit: + finally: e.set() t.join() diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 6a5b22f68..cfc6374a1 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -141,16 +141,6 @@ class VCruiseHelper: self.v_cruise_cluster_kph = self.v_cruise_kph -def apply_deadzone(error, deadzone): - if error > deadzone: - error -= deadzone - elif error < - deadzone: - error += deadzone - else: - error = 0. - return error - - def apply_center_deadzone(error, deadzone): if (error > - deadzone) and (error < deadzone): error = 0. diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index a619c4763..e4841c705 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -1,7 +1,7 @@ from cereal import car -from openpilot.common.numpy_fast import clip, interp +from openpilot.common.numpy_fast import clip from openpilot.common.realtime import DT_CTRL -from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone +from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N from openpilot.selfdrive.controls.lib.pid import PIDController from openpilot.selfdrive.modeld.constants import ModelConstants @@ -10,18 +10,10 @@ CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] LongCtrlState = car.CarControl.Actuators.LongControlState -def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, - v_target_1sec, brake_pressed, cruise_standstill): - accelerating = v_target_1sec > v_target - planned_stop = (v_target < CP.vEgoStopping and - v_target_1sec < CP.vEgoStopping and - not accelerating) - stay_stopped = (v_ego < CP.vEgoStopping and - (brake_pressed or cruise_standstill)) - stopping_condition = planned_stop or stay_stopped - - starting_condition = (v_target_1sec > CP.vEgoStarting and - accelerating and +def long_control_state_trans(CP, active, long_control_state, v_ego, + should_stop, brake_pressed, cruise_standstill): + stopping_condition = should_stop + starting_condition = (not should_stop and not cruise_standstill and not brake_pressed) started_condition = v_ego > CP.vEgoStarting @@ -34,7 +26,6 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, long_control_state = LongCtrlState.pid if stopping_condition: long_control_state = LongCtrlState.stopping - elif long_control_state == LongCtrlState.stopping: if starting_condition and CP.startingState: long_control_state = LongCtrlState.starting @@ -49,78 +40,45 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, v_target, return long_control_state - class LongControl: def __init__(self, CP): self.CP = CP - self.long_control_state = LongCtrlState.off # initialized to off + self.long_control_state = LongCtrlState.off self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV), (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV), k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL) - self.v_pid = 0.0 self.last_output_accel = 0.0 - def reset(self, v_pid): - """Reset PID controller and change setpoint""" + def reset(self): self.pid.reset() - self.v_pid = v_pid - def update(self, active, CS, long_plan, accel_limits, t_since_plan): + def update(self, active, CS, a_target, should_stop, accel_limits): """Update longitudinal control. This updates the state machine and runs a PID loop""" - # Interp control trajectory - speeds = long_plan.speeds - if len(speeds) == CONTROL_N: - v_target_now = interp(t_since_plan, CONTROL_N_T_IDX, speeds) - a_target_now = interp(t_since_plan, CONTROL_N_T_IDX, long_plan.accels) - - v_target = interp(self.CP.longitudinalActuatorDelay + t_since_plan, CONTROL_N_T_IDX, speeds) - a_target = 2 * (v_target - v_target_now) / self.CP.longitudinalActuatorDelay - a_target_now - - v_target_1sec = interp(self.CP.longitudinalActuatorDelay + t_since_plan + 1.0, CONTROL_N_T_IDX, speeds) - else: - v_target = 0.0 - v_target_now = 0.0 - v_target_1sec = 0.0 - a_target = 0.0 - self.pid.neg_limit = accel_limits[0] self.pid.pos_limit = accel_limits[1] - output_accel = self.last_output_accel self.long_control_state = long_control_state_trans(self.CP, active, self.long_control_state, CS.vEgo, - v_target, v_target_1sec, CS.brakePressed, + should_stop, CS.brakePressed, CS.cruiseState.standstill) - if self.long_control_state == LongCtrlState.off: - self.reset(CS.vEgo) + self.reset() output_accel = 0. elif self.long_control_state == LongCtrlState.stopping: + output_accel = self.last_output_accel if output_accel > self.CP.stopAccel: output_accel = min(output_accel, 0.0) output_accel -= self.CP.stoppingDecelRate * DT_CTRL - self.reset(CS.vEgo) + self.reset() elif self.long_control_state == LongCtrlState.starting: output_accel = self.CP.startAccel - self.reset(CS.vEgo) + self.reset() - elif self.long_control_state == LongCtrlState.pid: - self.v_pid = v_target_now - - # Toyota starts braking more when it thinks you want to stop - # Freeze the integrator so we don't accelerate to compensate, and don't allow positive acceleration - # TODO too complex, needs to be simplified and tested on toyotas - prevent_overshoot = not self.CP.stoppingControl and CS.vEgo < 1.5 and v_target_1sec < 0.7 and v_target_1sec < self.v_pid - deadzone = interp(CS.vEgo, self.CP.longitudinalTuning.deadzoneBP, self.CP.longitudinalTuning.deadzoneV) - freeze_integrator = prevent_overshoot - - error = self.v_pid - CS.vEgo - error_deadzone = apply_deadzone(error, deadzone) - output_accel = self.pid.update(error_deadzone, speed=CS.vEgo, - feedforward=a_target, - freeze_integrator=freeze_integrator) + else: # LongCtrlState.pid + error = a_target - CS.aEgo + output_accel = self.pid.update(error, speed=CS.vEgo, + feedforward=a_target) self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1]) - return self.last_output_accel diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 5126f0dab..2157a584f 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -23,6 +23,7 @@ LON_MPC_STEP = 0.2 # first step is 0.2s A_CRUISE_MIN = -1.2 A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6] A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] +CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] # Lookup table for turns _A_TOTAL_MAX_V = [1.7, 3.2] @@ -38,7 +39,6 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP): This function returns a limited long acceleration allowed, depending on the existing lateral acceleration this should avoid accelerating when losing the target in turns """ - # FIXME: This function to calculate lateral accel is incorrect and should use the VehicleModel # The lookup table for turns should also be updated if we do this a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V) @@ -48,6 +48,26 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP): return [a_target[0], min(a_target[1], a_x_allowed)] +def get_accel_from_plan(CP, long_plan): + speeds = long_plan.speeds + if len(speeds) == CONTROL_N: + v_target_now = interp(DT_MDL, CONTROL_N_T_IDX, speeds) + a_target_now = interp(DT_MDL, CONTROL_N_T_IDX, long_plan.accels) + + v_target = interp(CP.longitudinalActuatorDelay + DT_MDL, CONTROL_N_T_IDX, speeds) + a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now + + v_target_1sec = interp(CP.longitudinalActuatorDelay + DT_MDL + 1.0, CONTROL_N_T_IDX, speeds) + else: + v_target = 0.0 + v_target_now = 0.0 + v_target_1sec = 0.0 + a_target = 0.0 + should_stop = (v_target < CP.vEgoStopping and + v_target_1sec < CP.vEgoStopping) + return a_target, should_stop + + class LongitudinalPlanner: def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL): self.CP = CP @@ -165,9 +185,14 @@ class LongitudinalPlanner: plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState']) + longitudinalPlan = plan_send.longitudinalPlan longitudinalPlan.modelMonoTime = sm.logMonoTime['modelV2'] longitudinalPlan.processingDelay = (plan_send.logMonoTime / 1e9) - sm.logMonoTime['modelV2'] + longitudinalPlan.solverExecutionTime = self.mpc.solve_time + + longitudinalPlan.allowBrake = True + longitudinalPlan.allowThrottle = True longitudinalPlan.speeds = self.v_desired_trajectory.tolist() longitudinalPlan.accels = self.a_desired_trajectory.tolist() @@ -177,7 +202,11 @@ class LongitudinalPlanner: longitudinalPlan.longitudinalPlanSource = self.mpc.source longitudinalPlan.fcw = self.fcw - longitudinalPlan.solverExecutionTime = self.mpc.solve_time + a_target, should_stop = get_accel_from_plan(self.CP, longitudinalPlan) + longitudinalPlan.aTarget = a_target + longitudinalPlan.shouldStop = should_stop + longitudinalPlan.allowBrake = True + longitudinalPlan.allowThrottle = True pm.send('longitudinalPlan', plan_send) diff --git a/selfdrive/debug/internal/qlog_size.py b/selfdrive/debug/internal/qlog_size.py index 0b816604a..2d17a1ee2 100755 --- a/selfdrive/debug/internal/qlog_size.py +++ b/selfdrive/debug/internal/qlog_size.py @@ -6,6 +6,7 @@ from collections import defaultdict import matplotlib.pyplot as plt from openpilot.tools.lib.logreader import LogReader +from tqdm import tqdm MIN_SIZE = 0.5 # Percent size of total to show as separate entry @@ -15,19 +16,23 @@ def make_pie(msgs, typ): for m in msgs: msgs_by_type[m.which()].append(m.as_builder().to_bytes()) - length_by_type = {k: len(b"".join(v)) for k, v in msgs_by_type.items()} - compressed_length_by_type = {k: len(bz2.compress(b"".join(v))) for k, v in msgs_by_type.items()} - - total = sum(compressed_length_by_type.values()) + total = len(bz2.compress(b"".join([m.as_builder().to_bytes() for m in msgs]))) uncompressed_total = len(b"".join([m.as_builder().to_bytes() for m in msgs])) + length_by_type = {k: len(b"".join(v)) for k, v in msgs_by_type.items()} + # calculate compressed size by calculating diff when removed from the segment + compressed_length_by_type = {} + for k in tqdm(msgs_by_type.keys(), desc="Compressing"): + compressed_length_by_type[k] = total - len(bz2.compress(b"".join([m.as_builder().to_bytes() for m in msgs if m.which() != k]))) + sizes = sorted(compressed_length_by_type.items(), key=lambda kv: kv[1]) print("name - comp. size (uncomp. size)") for (name, sz) in sizes: print(f"{name:<22} - {sz / 1024:.2f} kB ({length_by_type[name] / 1024:.2f} kB)") print() - print(f"{typ} - Total {total / 1024:.2f} kB") + print(f"{typ} - Real total {total / 1024:.2f} kB") + print(f"{typ} - Breakdown total {sum(compressed_length_by_type.values()) / 1024:.2f} kB") print(f"{typ} - Uncompressed total {uncompressed_total / 1024 / 1024:.2f} MB") sizes_large = [(k, sz) for (k, sz) in sizes if sz >= total * MIN_SIZE / 100] diff --git a/selfdrive/navd/SConscript b/selfdrive/navd/SConscript index 295e8127d..378e76249 100644 --- a/selfdrive/navd/SConscript +++ b/selfdrive/navd/SConscript @@ -1,4 +1,4 @@ -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations', 'UBUNTU_FOCAL') map_env = qt_env.Clone() libs = ['qt_widgets', 'qt_util', 'QMapLibre', common, messaging, visionipc, transformations, @@ -6,7 +6,7 @@ libs = ['qt_widgets', 'qt_util', 'QMapLibre', common, messaging, visionipc, tran if arch == 'larch64': libs.append(':libEGL_mesa.so.0') -if arch in ['larch64', 'aarch64', 'x86_64']: +if arch in ['larch64', 'aarch64', 'x86_64'] and not UBUNTU_FOCAL: if arch == 'x86_64': rpath = Dir(f"#third_party/maplibre-native-qt/{arch}/lib").srcnode().abspath map_env["RPATH"] += [rpath, ] diff --git a/selfdrive/pandad/panda_comms.cc b/selfdrive/pandad/panda_comms.cc index 4e8a0a27b..59908f0cd 100644 --- a/selfdrive/pandad/panda_comms.cc +++ b/selfdrive/pandad/panda_comms.cc @@ -120,7 +120,7 @@ std::vector PandaUsbHandle::list() { libusb_close(handle); if (ret < 0) { goto finish; } - serials.push_back(std::string((char *)desc_serial, ret).c_str()); + serials.push_back(std::string((char *)desc_serial, ret)); } } diff --git a/selfdrive/pandad/tests/test_pandad_spi.py b/selfdrive/pandad/tests/test_pandad_spi.py index 11e20e72c..9c5990cd3 100644 --- a/selfdrive/pandad/tests/test_pandad_spi.py +++ b/selfdrive/pandad/tests/test_pandad_spi.py @@ -84,8 +84,8 @@ class TestBoarddSpi: ps = m.peripheralState assert ps.pandaType == "tres" assert 4000 < ps.voltage < 14000 - assert 100 < ps.current < 1000 - assert ps.fanSpeedRpm < 8000 + assert 50 < ps.current < 1000 + assert ps.fanSpeedRpm < 10000 time.sleep(0.5) et = time.monotonic() - st diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 227202839..96161a42d 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -6438bd5edad674c2de3c7e2d126271cb2576383d +8737e368e17f859291164286feb4246e00c0b4a5 diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 30fce52c5..14ff57ec7 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,6 @@ import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations', 'UBUNTU_FOCAL') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -13,7 +13,7 @@ dp_priv = len(os.listdir(Dir(f"#dp_priv/").abspath)) > 2 if arch == 'larch64': base_libs.append('EGL') -maps = arch in ['larch64', 'aarch64', 'x86_64'] +maps = arch in ['larch64', 'aarch64', 'x86_64'] and not UBUNTU_FOCAL if arch == "Darwin": del base_libs[base_libs.index('OpenCL')] diff --git a/selfdrive/ui/qt/maps/map_helpers.cc b/selfdrive/ui/qt/maps/map_helpers.cc index 16923f7a4..50e140116 100644 --- a/selfdrive/ui/qt/maps/map_helpers.cc +++ b/selfdrive/ui/qt/maps/map_helpers.cc @@ -143,11 +143,11 @@ std::pair map_format_distance(float d, bool is_metric) { d = std::max(d, 0.0f); if (is_metric) { - return (d > 500) ? std::pair{round_distance(d / 1000), QObject::tr("km")} - : std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("m")}; + return (d > 500) ? std::pair(round_distance(d / 1000), QObject::tr("km")) + : std::pair(QString::number(50 * std::nearbyint(d / 50)), QObject::tr("m")); } else { float feet = d * METER_TO_FOOT; - return (feet > 500) ? std::pair{round_distance(d * METER_TO_MILE), QObject::tr("mi")} - : std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("ft")}; + return (feet > 500) ? std::pair(round_distance(d * METER_TO_MILE), QObject::tr("mi")) + : std::pair(QString::number(50 * std::nearbyint(d / 50)), QObject::tr("ft")); } } diff --git a/third_party/maplibre-native-qt/x86_64/lib/libQMapLibre.so.3.0.0 b/third_party/maplibre-native-qt/x86_64/lib/libQMapLibre.so.3.0.0 index f2fcf107b..e49d321c7 100755 --- a/third_party/maplibre-native-qt/x86_64/lib/libQMapLibre.so.3.0.0 +++ b/third_party/maplibre-native-qt/x86_64/lib/libQMapLibre.so.3.0.0 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b718e4ea770105893dc41f64be36f806586a9471e2bb9d18d5ad97e906434548 -size 11000296 +oid sha256:d62e192aa7806302ed79866d9e3c51efc4bc315a6fdfe0e58e448dac5b279098 +size 11549728 diff --git a/tools/README.md b/tools/README.md index 361a27ded..f465ea123 100644 --- a/tools/README.md +++ b/tools/README.md @@ -2,11 +2,11 @@ ## System Requirements -openpilot is developed and tested on **Ubuntu 20.04**, which is the primary development target aside from the [supported embedded hardware](https://github.com/commaai/openpilot#running-on-a-dedicated-device-in-a-car). +openpilot is developed and tested on **Ubuntu 24.04**, which is the primary development target aside from the [supported embedded hardware](https://github.com/commaai/openpilot#running-on-a-dedicated-device-in-a-car). Running natively on any other system is not recommended and will require modifications. On Windows you can use WSL, and on macOS or incompatible Linux systems, it is recommended to use the dev containers. -## Native setup on Ubuntu 20.04 +## Native setup on Ubuntu 24.04 **1. Clone openpilot** @@ -55,7 +55,7 @@ GUI apps like `ui` or `cabana` can also run inside the container by leveraging X [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/about) should provide a similar experience to native Ubuntu. [WSL 2](https://docs.microsoft.com/en-us/windows/wsl/compare-versions) specifically has been reported by several users to be a seamless experience. -Follow [these instructions](https://docs.microsoft.com/en-us/windows/wsl/install) to setup the WSL and install the `Ubuntu-20.04` distribution. Once your Ubuntu WSL environment is setup, follow the Linux setup instructions to finish setting up your environment. See [these instructions](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps) for running GUI apps. +Follow [these instructions](https://docs.microsoft.com/en-us/windows/wsl/install) to setup the WSL and install the `Ubuntu-24.04` distribution. Once your Ubuntu WSL environment is setup, follow the Linux setup instructions to finish setting up your environment. See [these instructions](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps) for running GUI apps. **NOTE**: If you are running WSL and any GUIs are failing (segfaulting or other strange issues) even after following the steps above, you may need to enable software rendering with `LIBGL_ALWAYS_SOFTWARE=1`, e.g. `LIBGL_ALWAYS_SOFTWARE=1 selfdrive/ui/ui`. diff --git a/tools/cabana/chart/chart.cc b/tools/cabana/chart/chart.cc index fcf171a85..7a056c767 100644 --- a/tools/cabana/chart/chart.cc +++ b/tools/cabana/chart/chart.cc @@ -277,7 +277,7 @@ void ChartView::updateSeriesPoints() { } ((QScatterSeries *)s.series)->setMarkerSize(size); } else { - s.series->setPointsVisible(pixels_per_point > 20); + s.series->setPointsVisible(num_points == 1 || pixels_per_point > 20); } } } @@ -513,7 +513,7 @@ void ChartView::mouseReleaseEvent(QMouseEvent *event) { // no rubber dragged, seek to mouse position can->seekTo(min); } else if (rubber->width() > 10 && (max - min) > MIN_ZOOM_SECONDS) { - charts_widget->zoom_undo_stack->push(new ZoomCommand(charts_widget, {min, max})); + charts_widget->zoom_undo_stack->push(new ZoomCommand({min, max})); } else { viewport()->update(); } diff --git a/tools/cabana/chart/chartswidget.cc b/tools/cabana/chart/chartswidget.cc index a7bdd7464..59c188afb 100644 --- a/tools/cabana/chart/chartswidget.cc +++ b/tools/cabana/chart/chartswidget.cc @@ -103,6 +103,8 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QFrame(parent) { QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll); QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged); QObject::connect(can, &AbstractStream::msgsReceived, this, &ChartsWidget::updateState); + QObject::connect(can, &AbstractStream::seeking, this, &ChartsWidget::updateState); + QObject::connect(can, &AbstractStream::timeRangeChanged, this, &ChartsWidget::timeRangeChanged); QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange); QObject::connect(new_plot_btn, &QToolButton::clicked, this, &ChartsWidget::newChart); QObject::connect(remove_all_btn, &QToolButton::clicked, this, &ChartsWidget::removeAll); @@ -159,16 +161,13 @@ void ChartsWidget::eventsMerged(const MessageEventsMap &new_events) { } } -void ChartsWidget::setZoom(double min, double max) { - zoomed_range = {min, max}; - is_zoomed = zoomed_range != display_range; +void ChartsWidget::timeRangeChanged(const std::optional> &time_range) { updateToolBar(); updateState(); - emit rangeChanged(min, max, is_zoomed); } void ChartsWidget::zoomReset() { - setZoom(display_range.first, display_range.second); + can->setTimeRange(std::nullopt); zoom_undo_stack->clear(); } @@ -186,8 +185,9 @@ void ChartsWidget::showValueTip(double sec) { void ChartsWidget::updateState() { if (charts.isEmpty()) return; + const auto &time_range = can->timeRange(); const double cur_sec = can->currentSec(); - if (!is_zoomed) { + if (!time_range.has_value()) { double pos = (cur_sec - display_range.first) / std::max(1.0, max_chart_range); if (pos < 0 || pos > 0.8) { display_range.first = std::max(0.0, cur_sec - max_chart_range * 0.1); @@ -195,13 +195,9 @@ void ChartsWidget::updateState() { double max_sec = std::min(display_range.first + max_chart_range, can->totalSeconds()); display_range.first = std::max(0.0, max_sec - max_chart_range); display_range.second = display_range.first + max_chart_range; - } else if (cur_sec < (zoomed_range.first - 0.1) || cur_sec >= zoomed_range.second) { - // loop in zoomed range - QTimer::singleShot(0, [ts = zoomed_range.first]() { can->seekTo(ts);}); - return; } - const auto &range = is_zoomed ? zoomed_range : display_range; + const auto &range = time_range ? *time_range : display_range; for (auto c : charts) { c->updatePlot(cur_sec, range.first, range.second); } @@ -217,12 +213,14 @@ void ChartsWidget::updateToolBar() { title_label->setText(tr("Charts: %1").arg(charts.size())); columns_action->setText(tr("Column: %1").arg(column_count)); range_lb->setText(utils::formatSeconds(max_chart_range)); + + bool is_zoomed = can->timeRange().has_value(); range_lb_action->setVisible(!is_zoomed); range_slider_action->setVisible(!is_zoomed); undo_zoom_action->setVisible(is_zoomed); redo_zoom_action->setVisible(is_zoomed); reset_zoom_action->setVisible(is_zoomed); - reset_zoom_btn->setText(is_zoomed ? tr("%1-%2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : ""); + reset_zoom_btn->setText(is_zoomed ? tr("%1-%2").arg(can->timeRange()->first, 0, 'f', 2).arg(can->timeRange()->second, 0, 'f', 2) : ""); remove_all_btn->setEnabled(!charts.isEmpty()); dock_btn->setIcon(docking ? "arrow-up-right-square" : "arrow-down-left-square"); dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts")); @@ -252,7 +250,7 @@ ChartView *ChartsWidget::findChart(const MessageId &id, const cabana::Signal *si } ChartView *ChartsWidget::createChart() { - auto chart = new ChartView(is_zoomed ? zoomed_range : display_range, this); + auto chart = new ChartView(can->timeRange().value_or(display_range), this); chart->setFixedHeight(settings.chart_height); chart->setMinimumWidth(CHART_MIN_WIDTH); chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); diff --git a/tools/cabana/chart/chartswidget.h b/tools/cabana/chart/chartswidget.h index a39b4d73a..a9ac05db2 100644 --- a/tools/cabana/chart/chartswidget.h +++ b/tools/cabana/chart/chartswidget.h @@ -46,11 +46,10 @@ public: public slots: void setColumnCount(int n); void removeAll(); - void setZoom(double min, double max); + void timeRangeChanged(const std::optional> &time_range); signals: void dock(bool floating); - void rangeChanged(double min, double max, bool is_zommed); void seriesChanged(); private: @@ -102,9 +101,7 @@ private: ChartsContainer *charts_container; QScrollArea *charts_scroll; uint32_t max_chart_range = 0; - bool is_zoomed = false; std::pair display_range; - std::pair zoomed_range; QAction *columns_action; int column_count = 1; int current_column_count = 0; @@ -119,12 +116,11 @@ private: class ZoomCommand : public QUndoCommand { public: - ZoomCommand(ChartsWidget *charts, std::pair range) : charts(charts), range(range), QUndoCommand() { - prev_range = charts->is_zoomed ? charts->zoomed_range : charts->display_range; + ZoomCommand(std::pair range) : range(range), QUndoCommand() { + prev_range = can->timeRange(); setText(QObject::tr("Zoom to %1-%2").arg(range.first, 0, 'f', 2).arg(range.second, 0, 'f', 2)); } - void undo() override { charts->setZoom(prev_range.first, prev_range.second); } - void redo() override { charts->setZoom(range.first, range.second); } - ChartsWidget *charts; - std::pair prev_range, range; + void undo() override { can->setTimeRange(prev_range); } + void redo() override { can->setTimeRange(range); } + std::optional> prev_range, range; }; diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index e6c9b49ca..d099fcec9 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -192,7 +192,6 @@ void MainWindow::createDockWidgets() { video_splitter = new QSplitter(Qt::Vertical, this); video_widget = new VideoWidget(this); video_splitter->addWidget(video_widget); - QObject::connect(charts_widget, &ChartsWidget::rangeChanged, video_widget, &VideoWidget::updateTimeRange); video_splitter->addWidget(charts_container); video_splitter->setStretchFactor(1, 1); diff --git a/tools/cabana/streams/abstractstream.cc b/tools/cabana/streams/abstractstream.cc index 2e6ca9662..2584106ce 100644 --- a/tools/cabana/streams/abstractstream.cc +++ b/tools/cabana/streams/abstractstream.cc @@ -92,13 +92,23 @@ void AbstractStream::updateLastMessages() { std::set msgs; { std::lock_guard lk(mutex_); + double max_sec = 0; for (const auto &id : new_msgs_) { const auto &can_data = messages_[id]; - current_sec_ = std::max(current_sec_, can_data.ts); + max_sec = std::max(max_sec, can_data.ts); last_msgs[id] = can_data; sources.insert(id.source); } - msgs = std::move(new_msgs_); + + if (!new_msgs_.empty()) { + msgs = std::move(new_msgs_); + current_sec_ = max_sec; + } + } + + if (time_range_ && (current_sec_ < time_range_->first || current_sec_ >= time_range_->second)) { + seekTo(time_range_->first); + return; } if (sources.size() != prev_src_size) { @@ -108,6 +118,14 @@ void AbstractStream::updateLastMessages() { emit msgsReceived(&msgs, prev_msg_size != last_msgs.size()); } +void AbstractStream::setTimeRange(const std::optional> &range) { + time_range_ = range; + if (time_range_ && (current_sec_ < time_range_->first || current_sec_ >= time_range_->second)) { + seekTo(time_range_->first); + } + emit timeRangeChanged(time_range_); +} + void AbstractStream::updateEvent(const MessageId &id, double sec, const uint8_t *data, uint8_t size) { std::lock_guard lk(mutex_); messages_[id].compute(id, data, size, sec, getSpeed(), masks_[id]); diff --git a/tools/cabana/streams/abstractstream.h b/tools/cabana/streams/abstractstream.h index cfd423b36..822dd03d8 100644 --- a/tools/cabana/streams/abstractstream.h +++ b/tools/cabana/streams/abstractstream.h @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -77,6 +79,8 @@ public: virtual double getSpeed() { return 1; } virtual bool isPaused() const { return false; } virtual void pause(bool pause) {} + void setTimeRange(const std::optional> &range); + const std::optional> &timeRange() const { return time_range_; } inline const std::unordered_map &lastMessages() const { return last_msgs; } inline const MessageEventsMap &eventsMap() const { return events_; } @@ -91,8 +95,9 @@ public: signals: void paused(); void resume(); - void seekingTo(double sec); + void seeking(double sec); void seekedTo(double sec); + void timeRangeChanged(const std::optional> &range); void streamStarted(); void eventsMerged(const MessageEventsMap &events_map); void msgsReceived(const std::set *new_msgs, bool has_new_ids); @@ -110,6 +115,7 @@ protected: std::vector all_events_; double current_sec_ = 0; + std::optional> time_range_; uint64_t lastest_event_ts = 0; private: diff --git a/tools/cabana/streams/replaystream.cc b/tools/cabana/streams/replaystream.cc index 5fda6b048..bdb9ebb01 100644 --- a/tools/cabana/streams/replaystream.cc +++ b/tools/cabana/streams/replaystream.cc @@ -53,10 +53,31 @@ bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint {}, nullptr, replay_flags, data_dir, this)); replay->setSegmentCacheLimit(settings.max_cached_minutes); replay->installEventFilter(event_filter, this); + QObject::connect(replay.get(), &Replay::seeking, this, &AbstractStream::seeking); QObject::connect(replay.get(), &Replay::seekedTo, this, &AbstractStream::seekedTo); QObject::connect(replay.get(), &Replay::segmentsMerged, this, &ReplayStream::mergeSegments); - QObject::connect(replay.get(), &Replay::qLogLoaded, this, &ReplayStream::qLogLoaded, Qt::QueuedConnection); - return replay->load(); + bool success = replay->load(); + if (!success) { + if (replay->lastRouteError() == RouteLoadError::AccessDenied) { + auto auth_content = util::read_file(util::getenv("HOME") + "/.comma/auth.json"); + QString message; + if (auth_content.empty()) { + message = "Authentication Required. Please run the following command to authenticate:\n\n" + "python tools/lib/auth.py\n\n" + "This will grant access to routes from your comma account."; + } else { + message = tr("Access Denied. You do not have permission to access route:\n\n%1\n\n" + "This is likely a private route.").arg(route); + } + QMessageBox::warning(nullptr, tr("Access Denied"), message); + } else if (replay->lastRouteError() == RouteLoadError::NetworkError) { + QMessageBox::warning(nullptr, tr("Network Error"), + tr("Unable to load the route:\n\n %1.\n\nPlease check your network connection and try again.").arg(route)); + } else { + QMessageBox::warning(nullptr, tr("Route Load Failed"), tr("Failed to load route: '%1'").arg(route)); + } + } + return success; } void ReplayStream::start() { @@ -92,12 +113,7 @@ bool ReplayStream::eventFilter(const Event *event) { } void ReplayStream::seekTo(double ts) { - // Update timestamp and notify receivers of the time change. current_sec_ = ts; - std::set new_msgs; - msgsReceived(&new_msgs, false); - - // Seek to the specified timestamp replay->seekTo(std::max(double(0), ts), false); } @@ -149,7 +165,7 @@ OpenReplayWidget::OpenReplayWidget(AbstractStream **stream) : AbstractOpenStream bool OpenReplayWidget::open() { QString route = route_edit->text(); QString data_dir; - if (int idx = route.lastIndexOf('/'); idx != -1) { + if (int idx = route.lastIndexOf('/'); idx != -1 && util::file_exists(route.toStdString())) { data_dir = route.mid(0, idx + 1); route = route.mid(idx + 1); } @@ -166,8 +182,6 @@ bool OpenReplayWidget::open() { if (replay_stream->loadRoute(route, data_dir, flags)) { *stream = replay_stream.release(); - } else { - QMessageBox::warning(nullptr, tr("Warning"), tr("Failed to load route: '%1'").arg(route)); } } return *stream != nullptr; diff --git a/tools/cabana/streams/replaystream.h b/tools/cabana/streams/replaystream.h index 049ccddaf..ced78680d 100644 --- a/tools/cabana/streams/replaystream.h +++ b/tools/cabana/streams/replaystream.h @@ -34,9 +34,6 @@ public: void pause(bool pause) override; static AbstractOpenStreamWidget *widget(AbstractStream **stream); -signals: - void qLogLoaded(int segnum, std::shared_ptr qlog); - private: void mergeSegments(); std::unique_ptr replay = nullptr; diff --git a/tools/cabana/videowidget.cc b/tools/cabana/videowidget.cc index 7fca45c39..d2f78babc 100644 --- a/tools/cabana/videowidget.cc +++ b/tools/cabana/videowidget.cc @@ -38,6 +38,8 @@ VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { QObject::connect(can, &AbstractStream::paused, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::resume, this, &VideoWidget::updatePlayBtnState); QObject::connect(can, &AbstractStream::msgsReceived, this, &VideoWidget::updateState); + QObject::connect(can, &AbstractStream::seeking, this, &VideoWidget::updateState); + QObject::connect(can, &AbstractStream::timeRangeChanged, this, &VideoWidget::timeRangeChanged); updatePlayBtnState(); setWhatsThis(tr(R"( @@ -150,14 +152,16 @@ QWidget *VideoWidget::createCameraWidget() { setMaximumTime(can->totalSeconds()); QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->currentSecond()); }); - QObject::connect(slider, &Slider::updateMaximumTime, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); QObject::connect(can, &AbstractStream::eventsMerged, this, [this]() { slider->update(); }); - QObject::connect(static_cast(can), &ReplayStream::qLogLoaded, slider, &Slider::parseQLog); QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); QObject::connect(cam_widget, &CameraWidget::vipcAvailableStreamsUpdated, this, &VideoWidget::vipcAvailableStreamsUpdated); QObject::connect(camera_tab, &QTabBar::currentChanged, [this](int index) { if (index != -1) cam_widget->setStreamType((VisionStreamType)camera_tab->tabData(index).toInt()); }); + + auto replay = static_cast(can)->getReplay(); + QObject::connect(replay, &Replay::qLogLoaded, slider, &Slider::parseQLog, Qt::QueuedConnection); + QObject::connect(replay, &Replay::totalSecondsUpdated, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); return w; } @@ -198,13 +202,13 @@ void VideoWidget::setMaximumTime(double sec) { slider->setTimeRange(0, sec); } -void VideoWidget::updateTimeRange(double min, double max, bool is_zoomed) { +void VideoWidget::timeRangeChanged(const std::optional> &time_range) { if (can->liveStreaming()) { - skip_to_end_btn->setEnabled(!is_zoomed); + skip_to_end_btn->setEnabled(!time_range.has_value()); return; } - is_zoomed ? slider->setTimeRange(min, max) - : slider->setTimeRange(0, maximum_time); + time_range ? slider->setTimeRange(time_range->first, time_range->second) + : slider->setTimeRange(0, maximum_time); } QString VideoWidget::formatTime(double sec, bool include_milliseconds) { @@ -255,12 +259,7 @@ void Slider::setTimeRange(double min, double max) { setRange(min * factor, max * factor); } -void Slider::parseQLog(int segnum, std::shared_ptr qlog) { - const auto &segments = qobject_cast(can)->route()->segments(); - if (segments.size() > 0 && segnum == segments.rbegin()->first && !qlog->events.empty()) { - emit updateMaximumTime(qlog->events.back().mono_time / 1e9 - can->routeStartTime()); - } - +void Slider::parseQLog(std::shared_ptr qlog) { std::mutex mutex; QtConcurrent::blockingMap(qlog->events.cbegin(), qlog->events.cend(), [&mutex, this](const Event &e) { if (e.which == cereal::Event::Which::THUMBNAIL) { diff --git a/tools/cabana/videowidget.h b/tools/cabana/videowidget.h index b2039e09a..ac34c6600 100644 --- a/tools/cabana/videowidget.h +++ b/tools/cabana/videowidget.h @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include #include @@ -40,13 +42,10 @@ public: void setTimeRange(double min, double max); AlertInfo alertInfo(double sec); QPixmap thumbnail(double sec); - void parseQLog(int segnum, std::shared_ptr qlog); + void parseQLog(std::shared_ptr qlog); const double factor = 1000.0; -signals: - void updateMaximumTime(double); - private: void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; @@ -63,11 +62,11 @@ class VideoWidget : public QFrame { public: VideoWidget(QWidget *parnet = nullptr); - void updateTimeRange(double min, double max, bool is_zommed); void setMaximumTime(double sec); protected: QString formatTime(double sec, bool include_milliseconds = false); + void timeRangeChanged(const std::optional> &time_range); void updateState(); void updatePlayBtnState(); QWidget *createCameraWidget(); diff --git a/tools/lib/logreader.py b/tools/lib/logreader.py index 669c1520d..98c10941b 100755 --- a/tools/lib/logreader.py +++ b/tools/lib/logreader.py @@ -10,6 +10,7 @@ import sys import tqdm import urllib.parse import warnings +import zstd from collections.abc import Callable, Iterable, Iterator from urllib.parse import parse_qs, urlparse @@ -34,8 +35,8 @@ class _LogFileReader: ext = None if not dat: _, ext = os.path.splitext(urllib.parse.urlparse(fn).path) - if ext not in ('', '.bz2'): - # old rlogs weren't bz2 compressed + if ext not in ('', '.bz2', '.zst'): + # old rlogs weren't compressed raise Exception(f"unknown extension {ext}") with FileReader(fn) as f: @@ -43,18 +44,21 @@ class _LogFileReader: if ext == ".bz2" or dat.startswith(b'BZh9'): dat = bz2.decompress(dat) + elif ext == ".zst" or dat.startswith(b'\x28\xB5\x2F\xFD'): + # https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#zstandard-frames + dat = zstd.decompress(dat) ents = capnp_log.Event.read_multiple_bytes(dat) - _ents = [] + self._ents = [] try: for e in ents: - _ents.append(e) + self._ents.append(e) except capnp.KjException: warnings.warn("Corrupted events detected", RuntimeWarning, stacklevel=1) - self._ents = list(sorted(_ents, key=lambda x: x.logMonoTime) if sort_by_time else _ents) - self._ts = [x.logMonoTime for x in self._ents] + if sort_by_time: + self._ents.sort(key=lambda x: x.logMonoTime) def __iter__(self) -> Iterator[capnp._DynamicStructReader]: for ent in self._ents: @@ -126,12 +130,12 @@ def comma_api_source(sr: SegmentRange, mode: ReadMode) -> LogPaths: return apply_strategy(mode, rlog_paths, qlog_paths, valid_file=valid_file) -def internal_source(sr: SegmentRange, mode: ReadMode) -> LogPaths: +def internal_source(sr: SegmentRange, mode: ReadMode, file_ext: str = "bz2") -> LogPaths: if not internal_source_available(): raise InternalUnavailableException def get_internal_url(sr: SegmentRange, seg, file): - return f"cd:/{sr.dongle_id}/{sr.timestamp}/{seg}/{file}.bz2" + return f"cd:/{sr.dongle_id}/{sr.log_id}/{seg}/{file}.{file_ext}" rlog_paths = [get_internal_url(sr, seg, "rlog") for seg in sr.seg_idxs] qlog_paths = [get_internal_url(sr, seg, "qlog") for seg in sr.seg_idxs] @@ -139,6 +143,10 @@ def internal_source(sr: SegmentRange, mode: ReadMode) -> LogPaths: return apply_strategy(mode, rlog_paths, qlog_paths) +def internal_source_zst(sr: SegmentRange, mode: ReadMode, file_ext: str = "zst") -> LogPaths: + return internal_source(sr, mode, file_ext) + + def openpilotci_source(sr: SegmentRange, mode: ReadMode) -> LogPaths: rlog_paths = [get_url(sr.route_name, seg, "rlog") for seg in sr.seg_idxs] qlog_paths = [get_url(sr.route_name, seg, "qlog") for seg in sr.seg_idxs] @@ -162,7 +170,8 @@ def get_invalid_files(files): def check_source(source: Source, *args) -> LogPaths: files = source(*args) - assert next(get_invalid_files(files), False) is False + assert len(files) > 0, "No files on source" + assert next(get_invalid_files(files), False) is False, "Some files are invalid" return files @@ -170,8 +179,8 @@ def auto_source(sr: SegmentRange, mode=ReadMode.RLOG) -> LogPaths: if mode == ReadMode.SANITIZED: return comma_car_segments_source(sr, mode) - SOURCES: list[Source] = [internal_source, openpilotci_source, comma_api_source, comma_car_segments_source,] - exceptions = [] + SOURCES: list[Source] = [internal_source, internal_source_zst, openpilotci_source, comma_api_source, comma_car_segments_source,] + exceptions = {} # for automatic fallback modes, auto_source needs to first check if rlogs exist for any source if mode in [ReadMode.AUTO, ReadMode.AUTO_INTERACTIVE]: @@ -186,9 +195,10 @@ def auto_source(sr: SegmentRange, mode=ReadMode.RLOG) -> LogPaths: try: return check_source(source, sr, mode) except Exception as e: - exceptions.append(e) + exceptions[source.__name__] = e - raise Exception(f"auto_source could not find any valid source, exceptions for sources: {exceptions}") + raise Exception("auto_source could not find any valid source, exceptions for sources:\n - " + + "\n - ".join([f"{k}: {repr(v)}" for k, v in exceptions.items()])) def parse_useradmin(identifier: str): diff --git a/tools/lib/route.py b/tools/lib/route.py index d5fd41108..6ff5d1920 100644 --- a/tools/lib/route.py +++ b/tools/lib/route.py @@ -9,7 +9,7 @@ from openpilot.tools.lib.auth_config import get_token from openpilot.tools.lib.api import CommaApi from openpilot.tools.lib.helpers import RE -QLOG_FILENAMES = ['qlog', 'qlog.bz2'] +QLOG_FILENAMES = ['qlog', 'qlog.bz2', 'qlog.zst'] QCAMERA_FILENAMES = ['qcamera.ts'] LOG_FILENAMES = ['rlog', 'rlog.bz2', 'raw_log.bz2'] CAMERA_FILENAMES = ['fcamera.hevc', 'video.hevc'] @@ -260,10 +260,6 @@ class SegmentRange: def dongle_id(self) -> str: return self.m.group("dongle_id") - @property - def timestamp(self) -> str: - return self.m.group("timestamp") - @property def log_id(self) -> str: return self.m.group("log_id") diff --git a/tools/profiling/snapdragon/README.md b/tools/profiling/snapdragon/README.md index 664814b61..f56ca182a 100644 --- a/tools/profiling/snapdragon/README.md +++ b/tools/profiling/snapdragon/README.md @@ -3,7 +3,7 @@ snapdragon profiler * download from https://developer.qualcomm.com/software/snapdragon-profiler/tools-archive (need a qc developer account) - * choose v2021.5 (verified working with 20.04 dev environment) + * choose v2021.5 (verified working with 24.04 dev environment) * unzip to selfdrive/debug/profiling/snapdragon/SnapdragonProfiler * run ```./setup-profiler.sh``` * run ```./setup-agnos.sh``` diff --git a/tools/replay/camera.cc b/tools/replay/camera.cc index 9e711149c..b6d4ddc3a 100644 --- a/tools/replay/camera.cc +++ b/tools/replay/camera.cc @@ -60,7 +60,6 @@ void CameraServer::cameraThread(Camera &cam) { capnp::FlatArrayMessageReader reader(event->data); auto evt = reader.getRoot(); auto eidx = capnp::AnyStruct::Reader(evt).getPointerSection()[0].getAs(); - if (eidx.getType() != cereal::EncodeIndex::Type::FULL_H_E_V_C) continue; int segment_id = eidx.getSegmentId(); uint32_t frame_id = eidx.getFrameId(); diff --git a/tools/replay/logreader.cc b/tools/replay/logreader.cc index 0f1638145..9b726e067 100644 --- a/tools/replay/logreader.cc +++ b/tools/replay/logreader.cc @@ -42,10 +42,10 @@ bool LogReader::load(const char *data, size_t size, std::atomic *abort) { evt.which == cereal::Event::DRIVER_ENCODE_IDX || evt.which == cereal::Event::WIDE_ROAD_ENCODE_IDX) { auto idx = capnp::AnyStruct::Reader(event).getPointerSection()[0].getAs(); - if (uint64_t sof = idx.getTimestampSof()) { - mono_time = sof; + if (idx.getType() == cereal::EncodeIndex::Type::FULL_H_E_V_C) { + uint64_t sof = idx.getTimestampSof(); + events.emplace_back(which, sof ? sof : mono_time, event_data, idx.getSegmentNum()); } - events.emplace_back(which, mono_time, event_data, idx.getSegmentNum()); } } } catch (const kj::Exception &e) { diff --git a/tools/replay/replay.cc b/tools/replay/replay.cc index 861643718..c5ad2aa54 100644 --- a/tools/replay/replay.cc +++ b/tools/replay/replay.cc @@ -55,11 +55,11 @@ void Replay::stop() { rInfo("shutdown: in progress..."); if (stream_thread_ != nullptr) { exit_ = true; - paused_ = true; + pauseStreamThread(); stream_cv_.notify_one(); stream_thread_->quit(); stream_thread_->wait(); - delete stream_thread_; + stream_thread_->deleteLater(); stream_thread_ = nullptr; } timeline_future.waitForFinished(); @@ -104,28 +104,40 @@ void Replay::updateEvents(const std::function &update_events_function) { void Replay::seekTo(double seconds, bool relative) { updateEvents([&]() { - seeking_to_seconds_ = relative ? seconds + currentSeconds() : seconds; - seeking_to_seconds_ = std::max(double(0.0), seeking_to_seconds_); - int target_segment = (int)seeking_to_seconds_ / 60; + double target_time = relative ? seconds + currentSeconds() : seconds; + target_time = std::max(double(0.0), target_time); + int target_segment = (int)target_time / 60; if (segments_.count(target_segment) == 0) { - rWarning("can't seek to %d s segment %d is invalid", (int)seeking_to_seconds_, target_segment); + rWarning("Can't seek to %d s segment %d is invalid", (int)target_time, target_segment); return true; } - rInfo("seeking to %d s, segment %d", (int)seeking_to_seconds_, target_segment); + rInfo("Seeking to %d s, segment %d", (int)target_time, target_segment); current_segment_ = target_segment; - cur_mono_time_ = route_start_ts_ + seeking_to_seconds_ * 1e9; - bool segment_merged = isSegmentMerged(target_segment); - if (segment_merged) { - emit seekedTo(seeking_to_seconds_); - // Reset seeking_to_seconds_ to indicate completion of seek - seeking_to_seconds_ = -1; - } - return segment_merged; + cur_mono_time_ = route_start_ts_ + target_time * 1e9; + seeking_to_ = target_time; + return false; }); + + checkSeekProgress(); updateSegmentsCache(); } +void Replay::checkSeekProgress() { + if (seeking_to_) { + auto it = segments_.find(int(*seeking_to_ / 60)); + if (it != segments_.end() && it->second && it->second->isLoaded()) { + emit seekedTo(*seeking_to_); + seeking_to_ = std::nullopt; + // wake up stream thread + updateEvents([]() { return true; }); + } else { + // Emit signal indicating the ongoing seek operation + emit seeking(*seeking_to_); + } + } +} + void Replay::seekToFlag(FindFlag flag) { if (auto next = find(flag)) { seekTo(*next - 2, false); // seek to 2 seconds before next @@ -150,8 +162,9 @@ void Replay::buildTimeline() { const auto &route_segments = route_->segments(); for (auto it = route_segments.cbegin(); it != route_segments.cend() && !exit_; ++it) { std::shared_ptr log(new LogReader()); - if (!log->load(it->second.qlog.toStdString(), &exit_, !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3)) continue; + if (!log->load(it->second.qlog.toStdString(), &exit_, !hasFlag(REPLAY_FLAG_NO_FILE_CACHE), 0, 3) || log->events.empty()) continue; + std::vector> timeline; for (const Event &e : log->events) { if (e.which == cereal::Event::Which::CONTROLS_STATE) { capnp::FlatArrayMessageReader reader(e.data); @@ -160,7 +173,6 @@ void Replay::buildTimeline() { if (engaged != cs.getEnabled()) { if (engaged) { - std::lock_guard lk(timeline_lock); timeline.push_back({toSeconds(engaged_begin), toSeconds(e.mono_time), TimelineType::Engaged}); } engaged_begin = e.mono_time; @@ -169,7 +181,6 @@ void Replay::buildTimeline() { if (alert_type != cs.getAlertType().cStr() || alert_status != cs.getAlertStatus()) { if (!alert_type.empty() && alert_size != cereal::ControlsState::AlertSize::NONE) { - std::lock_guard lk(timeline_lock); timeline.push_back({toSeconds(alert_begin), toSeconds(e.mono_time), timeline_types[(int)alert_status]}); } alert_begin = e.mono_time; @@ -178,12 +189,20 @@ void Replay::buildTimeline() { alert_status = cs.getAlertStatus(); } } else if (e.which == cereal::Event::Which::USER_FLAG) { - std::lock_guard lk(timeline_lock); timeline.push_back({toSeconds(e.mono_time), toSeconds(e.mono_time), TimelineType::UserFlag}); } } - std::sort(timeline.begin(), timeline.end(), [](auto &l, auto &r) { return std::get<2>(l) < std::get<2>(r); }); - emit qLogLoaded(it->first, log); + + { + std::lock_guard lk(timeline_lock); + timeline_.insert(timeline_.end(), timeline.begin(), timeline.end()); + std::sort(timeline_.begin(), timeline_.end(), [](auto &l, auto &r) { return std::get<2>(l) < std::get<2>(r); }); + } + + if (it->first == route_segments.rbegin()->first) { + emit totalSecondsUpdated(toSeconds(log->events.back().mono_time)); + } + emit qLogLoaded(log); } } @@ -260,14 +279,13 @@ void Replay::updateSegmentsCache() { const auto &cur_segment = cur->second; if (stream_thread_ == nullptr && cur_segment->isLoaded()) { startStream(cur_segment.get()); - emit streamStarted(); } } void Replay::loadSegmentInRange(SegmentMap::iterator begin, SegmentMap::iterator cur, SegmentMap::iterator end) { - auto loadNext = [this](auto begin, auto end) { - auto it = std::find_if(begin, end, [](const auto &seg_it) { return !seg_it.second || !seg_it.second->isLoaded(); }); - if (it != end && !it->second) { + auto loadNextSegment = [this](auto first, auto last) { + auto it = std::find_if(first, last, [](const auto &seg_it) { return !seg_it.second || !seg_it.second->isLoaded(); }); + if (it != last && !it->second) { rDebug("loading segment %d...", it->first); it->second = std::make_unique(it->first, route_->at(it->first), flags_, filters_); QObject::connect(it->second.get(), &Segment::loadFinished, this, &Replay::segmentLoadFinished); @@ -276,9 +294,9 @@ void Replay::loadSegmentInRange(SegmentMap::iterator begin, SegmentMap::iterator return false; }; - // Load forward segments, then try reverse - if (!loadNext(cur, end)) { - loadNext(std::make_reverse_iterator(cur), segments_.rend()); + // Try loading forward segments, then reverse segments + if (!loadNextSegment(cur, end)) { + loadNextSegment(std::make_reverse_iterator(cur), std::make_reverse_iterator(begin)); } } @@ -316,15 +334,10 @@ void Replay::mergeSegments(const SegmentMap::iterator &begin, const SegmentMap:: updateEvents([&]() { events_.swap(new_events); merged_segments_ = segments_to_merge; - // Check if seeking is in progress - int target_segment = int(seeking_to_seconds_ / 60); - if (seeking_to_seconds_ >= 0 && segments_to_merge.count(target_segment) > 0) { - emit seekedTo(seeking_to_seconds_); - seeking_to_seconds_ = -1; // Reset seeking_to_seconds_ to indicate completion of seek - } // Wake up the stream thread if the current segment is loaded or invalid. - return isSegmentMerged(current_segment_) || (segments_.count(current_segment_) == 0); + return !seeking_to_ && (isSegmentMerged(current_segment_) || (segments_.count(current_segment_) == 0)); }); + checkSeekProgress(); } void Replay::startStream(const Segment *cur_segment) { @@ -379,6 +392,7 @@ void Replay::startStream(const Segment *cur_segment) { stream_thread_->start(); timeline_future = QtConcurrent::run(this, &Replay::buildTimeline); + emit streamStarted(); } void Replay::publishMessage(const Event *e) { @@ -473,6 +487,7 @@ std::vector::const_iterator Replay::publishEvents(std::vector::con // Skip events if socket is not present if (!sockets_[evt.which]) continue; + cur_mono_time_ = evt.mono_time; const uint64_t current_nanos = nanos_since_boot(); const int64_t time_diff = (evt.mono_time - evt_start_ts) / speed_ - (current_nanos - loop_start_ts); @@ -484,12 +499,11 @@ std::vector::const_iterator Replay::publishEvents(std::vector::con loop_start_ts = current_nanos; prev_replay_speed = speed_; } else if (time_diff > 0) { - precise_nano_sleep(time_diff); + precise_nano_sleep(time_diff, paused_); } if (paused_) break; - cur_mono_time_ = evt.mono_time; if (evt.eidx_segnum == -1) { publishMessage(&evt); } else if (camera_server_) { diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 4adbc14df..7e1815ede 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -53,6 +53,7 @@ public: uint32_t flags = REPLAY_FLAG_NONE, QString data_dir = "", QObject *parent = 0); ~Replay(); bool load(); + RouteLoadError lastRouteError() const { return route_->lastError(); } void start(int seconds = 0); void stop(); void pause(bool pause); @@ -85,14 +86,16 @@ public: inline const std::string &carFingerprint() const { return car_fingerprint_; } inline const std::vector> getTimeline() { std::lock_guard lk(timeline_lock); - return timeline; + return timeline_; } signals: void streamStarted(); void segmentsMerged(); + void seeking(double sec); void seekedTo(double sec); - void qLogLoaded(int segnum, std::shared_ptr qlog); + void qLogLoaded(std::shared_ptr qlog); + void totalSecondsUpdated(double sec); protected slots: void segmentLoadFinished(bool success); @@ -112,6 +115,7 @@ protected: void publishMessage(const Event *e); void publishFrame(const Event *e); void buildTimeline(); + void checkSeekProgress(); inline bool isSegmentMerged(int n) const { return merged_segments_.count(n) > 0; } pthread_t stream_thread_id = 0; @@ -120,7 +124,7 @@ protected: bool user_paused_ = false; std::condition_variable stream_cv_; std::atomic current_segment_ = 0; - double seeking_to_seconds_ = -1; + std::optional seeking_to_; SegmentMap segments_; // the following variables must be protected with stream_lock_ std::atomic exit_ = false; @@ -143,7 +147,7 @@ protected: std::mutex timeline_lock; QFuture timeline_future; - std::vector> timeline; + std::vector> timeline_; std::string car_fingerprint_; std::atomic speed_ = 1.0; replayEventFilter event_filter = nullptr; diff --git a/tools/replay/route.cc b/tools/replay/route.cc index e8e73459e..18c962abd 100644 --- a/tools/replay/route.cc +++ b/tools/replay/route.cc @@ -74,7 +74,10 @@ bool Route::loadFromServer(int retries) { return loadFromJson(result); } else if (err == QNetworkReply::ContentAccessDenied || err == QNetworkReply::AuthenticationRequiredError) { rWarning(">> Unauthorized. Authenticate with tools/lib/auth.py <<"); + err_ = RouteLoadError::AccessDenied; return false; + } else { + err_ = RouteLoadError::NetworkError; } rWarning("Retrying %d/%d", i, retries); util::sleep_for(3000); diff --git a/tools/replay/route.h b/tools/replay/route.h index f95649780..acc73d509 100644 --- a/tools/replay/route.h +++ b/tools/replay/route.h @@ -12,6 +12,14 @@ #include "tools/replay/logreader.h" #include "tools/replay/util.h" +enum class RouteLoadError { + None, + AccessDenied, + NetworkError, + FileNotFound, + UnknownError +}; + struct RouteIdentifier { QString dongle_id; QString timestamp; @@ -33,6 +41,7 @@ class Route { public: Route(const QString &route, const QString &data_dir = {}); bool load(); + RouteLoadError lastError() const { return err_; } inline const QString &name() const { return route_.str; } inline const QDateTime datetime() const { return date_time_; } inline const QString &dir() const { return data_dir_; } @@ -50,6 +59,7 @@ protected: QString data_dir_; std::map segments_; QDateTime date_time_; + RouteLoadError err_ = RouteLoadError::None; }; class Segment : public QObject { diff --git a/tools/replay/ui.py b/tools/replay/ui.py index a790f14ff..b1fe70ef3 100755 --- a/tools/replay/ui.py +++ b/tools/replay/ui.py @@ -158,7 +158,6 @@ def ui_thread(addr): # TODO brake is deprecated plot_arr[-1, name_to_arr_idx['computer_brake']] = clip(-sm['carControl'].actuators.accel/4.0, 0.0, 1.0) plot_arr[-1, name_to_arr_idx['v_ego']] = sm['carState'].vEgo - plot_arr[-1, name_to_arr_idx['v_pid']] = sm['controlsState'].vPid plot_arr[-1, name_to_arr_idx['v_cruise']] = sm['carState'].cruiseState.speed plot_arr[-1, name_to_arr_idx['a_ego']] = sm['carState'].aEgo diff --git a/tools/replay/util.cc b/tools/replay/util.cc index c8203fd79..a08b3b3d5 100644 --- a/tools/replay/util.cc +++ b/tools/replay/util.cc @@ -318,33 +318,23 @@ std::string decompressBZ2(const std::byte *in, size_t in_size, std::atomic return {}; } -void precise_nano_sleep(int64_t nanoseconds) { -#ifdef __APPLE__ - const long estimate_ns = 1 * 1e6; // 1ms - struct timespec req = {.tv_nsec = estimate_ns}; - uint64_t start_sleep = nanos_since_boot(); - while (nanoseconds > estimate_ns) { - nanosleep(&req, nullptr); - uint64_t end_sleep = nanos_since_boot(); - nanoseconds -= (end_sleep - start_sleep); - start_sleep = end_sleep; - } - // spin wait - if (nanoseconds > 0) { - while ((nanos_since_boot() - start_sleep) <= nanoseconds) { - std::this_thread::yield(); - } - } -#else +void precise_nano_sleep(int64_t nanoseconds, std::atomic &should_exit) { struct timespec req, rem; - - req.tv_sec = nanoseconds / 1e9; - req.tv_nsec = nanoseconds % (int64_t)1e9; - while (clock_nanosleep(CLOCK_MONOTONIC, 0, &req, &rem) && errno == EINTR) { + req.tv_sec = nanoseconds / 1000000000; + req.tv_nsec = nanoseconds % 1000000000; + while (!should_exit) { +#ifdef __APPLE_ + int ret = nanosleep(&req, &rem); + if (ret == 0 || errno != EINTR) + break; +#else + int ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &req, &rem); + if (ret == 0 || ret != EINTR) + break; +#endif // Retry sleep if interrupted by a signal req = rem; } -#endif } std::string sha256(const std::string &str) { diff --git a/tools/replay/util.h b/tools/replay/util.h index 750c13355..97516e181 100644 --- a/tools/replay/util.h +++ b/tools/replay/util.h @@ -37,7 +37,7 @@ private: }; std::string sha256(const std::string &str); -void precise_nano_sleep(int64_t nanoseconds); +void precise_nano_sleep(int64_t nanoseconds, std::atomic &should_exit); std::string decompressBZ2(const std::string &in, std::atomic *abort = nullptr); std::string decompressBZ2(const std::byte *in, size_t in_size, std::atomic *abort = nullptr); std::string getUrlWithoutQuery(const std::string &url); diff --git a/tools/sim/start_openpilot_docker.sh b/tools/sim/start_openpilot_docker.sh deleted file mode 100755 index 3dce66057..000000000 --- a/tools/sim/start_openpilot_docker.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -cd $DIR - -OPENPILOT_DIR="/tmp/openpilot" -if ! [[ -z "$MOUNT_OPENPILOT" ]]; then - OPENPILOT_DIR="$(dirname $(dirname $DIR))" - EXTRA_ARGS="-v $OPENPILOT_DIR:$OPENPILOT_DIR -e PYTHONPATH=$OPENPILOT_DIR:$PYTHONPATH" -fi - -if [[ "$CI" ]]; then - CMD="CI=1 ${OPENPILOT_DIR}/tools/sim/tests/test_metadrive_integration.py" -else - # expose X to the container - xhost +local:root - - docker pull ghcr.io/commaai/openpilot-sim:latest - CMD="./tmux_script.sh $*" - EXTRA_ARGS="${EXTRA_ARGS} -it" -fi - -docker kill openpilot_client || true -docker run --net=host\ - --name openpilot_client \ - --rm \ - --gpus all \ - --device=/dev/dri:/dev/dri \ - --device=/dev/input:/dev/input \ - -v /tmp/.X11-unix:/tmp/.X11-unix \ - --shm-size 1G \ - -e DISPLAY=$DISPLAY \ - -e QT_X11_NO_MITSHM=1 \ - -w "$OPENPILOT_DIR/tools/sim" \ - $EXTRA_ARGS \ - ghcr.io/commaai/openpilot-sim:latest \ - /bin/bash -c "$CMD" diff --git a/tools/webcam/README.md b/tools/webcam/README.md index 7334d87b3..c756069bb 100644 --- a/tools/webcam/README.md +++ b/tools/webcam/README.md @@ -1,7 +1,7 @@ # Run openpilot with webcam on PC What's needed: -- Ubuntu 20.04 +- Ubuntu 24.04 - GPU (recommended) - Two USB webcams, at least 720p and 78 degrees FOV (e.g. Logitech C920/C615) - [Car harness](https://comma.ai/shop/products/comma-car-harness) with black panda to connect to your car