mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-10 13:33:43 +08:00
Compare commits
312 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6aef256df | |||
| cfb38312db | |||
| 6fd4278e02 | |||
| 451cc3445a | |||
| 1daa2d9081 | |||
| 7a9099818f | |||
| ef4fa48f33 | |||
| e426ced4c0 | |||
| 7681ba763b | |||
| b04d666a68 | |||
| fb026b86a3 | |||
| cff4c5e589 | |||
| e0cca951f3 | |||
| 2439c048ed | |||
| 7b54d7d7a0 | |||
| b2bdb5787f | |||
| 282f517b78 | |||
| 1779144ff9 | |||
| 54fb2750ba | |||
| 14431ab77f | |||
| 9d45db41b3 | |||
| b57c593d92 | |||
| 0674c42866 | |||
| 1f7bcf246a | |||
| b0512dc523 | |||
| cd88fd3850 | |||
| ff78eaeba1 | |||
| 92c8aeeb92 | |||
| ecaa20e7be | |||
| 859c98c9d8 | |||
| 69cbe8c6ed | |||
| 1aafe92fc0 | |||
| adb6b9fb12 | |||
| f67a9f4624 | |||
| 094e834ac4 | |||
| e4067060b9 | |||
| 86a14640b5 | |||
| 59fb84f0d5 | |||
| 78de201ce7 | |||
| 17e49f69cc | |||
| bec75ebdd8 | |||
| 473832efa7 | |||
| 2cceeb3552 | |||
| 923a47f713 | |||
| 790a762a05 | |||
| 221c219fca | |||
| 8b60649eed | |||
| ded0b506d6 | |||
| b51d9af9a0 | |||
| 4460ce8166 | |||
| f1aa0c7f78 | |||
| e7c8126fd9 | |||
| 125999c364 | |||
| 5b25ea7f99 | |||
| b7e2631286 | |||
| a838871189 | |||
| 1827331599 | |||
| 5c777bbe01 | |||
| fb97f993d1 | |||
| 94b67077e3 | |||
| 14f17699b9 | |||
| 80e27d5cbb | |||
| 039dbcd877 | |||
| 8c134ae555 | |||
| 276c7a2b34 | |||
| 0e2dbcebfa | |||
| bbb7760a95 | |||
| 23a27b2fbf | |||
| 8b78107a40 | |||
| c27b6007de | |||
| 36c2dce247 | |||
| 8035039731 | |||
| 8aa6c9440f | |||
| 34ef40fd81 | |||
| 4d044d7618 | |||
| 6247e3dc84 | |||
| b1131289b7 | |||
| 35dc7d661e | |||
| c39b2dad94 | |||
| e6b769245c | |||
| 8b94f8b2f8 | |||
| dec014cd17 | |||
| 7b40272866 | |||
| 506456e7f0 | |||
| 4ea4b9d177 | |||
| 0e2313dc31 | |||
| 79ea7db103 | |||
| f9ae9192fa | |||
| 7ec23006c6 | |||
| 2be9447a6c | |||
| cfd926778e | |||
| a97a67e3d0 | |||
| 518b6de08d | |||
| 6933e3bcdb | |||
| 05e0ca8bee | |||
| 410614fcf3 | |||
| e2bc0996ef | |||
| 1be0c20cf5 | |||
| 839143b9ed | |||
| bce86637ae | |||
| b833d3ee89 | |||
| e0441dfb4b | |||
| 62ec40bba6 | |||
| 15c6d38028 | |||
| 56eb9f555c | |||
| 2f9951df02 | |||
| 6030bf4da3 | |||
| 074694d660 | |||
| df35f48f3b | |||
| 4fb9704540 | |||
| 48cbe266fc | |||
| 21aa7ff367 | |||
| 7caf05dd51 | |||
| 2d779f5db9 | |||
| 18208f1da0 | |||
| 5a3c6ddf57 | |||
| eaa8732ab0 | |||
| 6f0284c84f | |||
| 999ea03f23 | |||
| 0975db3ff1 | |||
| 8d70a8b80a | |||
| da93f92887 | |||
| 9117f6c071 | |||
| 3567ff9691 | |||
| f44ae2ced9 | |||
| 376e0ca615 | |||
| 32b7686468 | |||
| b9e0f52ea9 | |||
| a3163b680f | |||
| 8a927d808f | |||
| bc7d5e474d | |||
| 36f192b5fe | |||
| ffd5cd4ac2 | |||
| e4a00fcd6c | |||
| 0bdcb41103 | |||
| 78051085ca | |||
| 4910d5809a | |||
| 8dd862ff28 | |||
| 1b57497da9 | |||
| 8d8d1ffc7a | |||
| c5919d5495 | |||
| da71951c95 | |||
| 25a152cd8b | |||
| 9077a1082a | |||
| 7ae7000254 | |||
| 7029455706 | |||
| 3a71a62215 | |||
| 00622e8c33 | |||
| 549da3ee92 | |||
| e038a65ef8 | |||
| 8d0513c657 | |||
| 2cea48f4cd | |||
| 8855a9ab65 | |||
| 32321c01cc | |||
| 52cd65fefb | |||
| 1fcdeccd40 | |||
| 3517c36978 | |||
| fb30c3c1e8 | |||
| 1c25e568d5 | |||
| f172122b7c | |||
| 67d6cdc7cd | |||
| 6724085cfd | |||
| 6774f34eee | |||
| 6d0402896d | |||
| 344021a3d9 | |||
| a9b85ab27d | |||
| 17204a46e4 | |||
| 7c4d415462 | |||
| b8985b6d72 | |||
| c669473f88 | |||
| 8751435bf5 | |||
| 30ae210761 | |||
| 3eb693f58b | |||
| 7b4a31c5ac | |||
| 1527c8cf88 | |||
| ab0a7ae666 | |||
| 11ec2f1f21 | |||
| 2c6808d37e | |||
| 2e96382c49 | |||
| 0239e440ca | |||
| 76b972daff | |||
| bc3ef3e7dd | |||
| 9839291dd0 | |||
| 17cba328d6 | |||
| 86093765d8 | |||
| 05fa1c8ae8 | |||
| e8a40d6b85 | |||
| c265e0bb85 | |||
| c6b118788b | |||
| b004f6dbdc | |||
| cd4930b680 | |||
| f861aca628 | |||
| 1ff0d8e2ee | |||
| d76d70764e | |||
| dc73ce0b71 | |||
| 3f666748af | |||
| 8de8a8838c | |||
| f563b7eb71 | |||
| 3c18b83708 | |||
| bd35f5904b | |||
| 474f2737f6 | |||
| ea1ac4a212 | |||
| 165d7c7b36 | |||
| b53cbb2e18 | |||
| b95f8c5929 | |||
| e564bb0b85 | |||
| e2ec8a7b13 | |||
| 75c6f0f10e | |||
| af38044b42 | |||
| 684fa846d8 | |||
| 416e722855 | |||
| 9fd4613bbb | |||
| a0362e3c5f | |||
| e32ef1cdc0 | |||
| 20673ec8a6 | |||
| 53fbdf7329 | |||
| 6f72b74fac | |||
| e83705a32e | |||
| 95d36b9ba2 | |||
| b9e74254bd | |||
| b4405b200d | |||
| a8a3fdac54 | |||
| 4eddc622a7 | |||
| 4e42ada240 | |||
| 6266217655 | |||
| ea09d32e98 | |||
| 15958c88d3 | |||
| b80d7fb5ea | |||
| 1c3d25c6ff | |||
| c9f22b32c7 | |||
| c0524985bb | |||
| 83839c7ea7 | |||
| c1a1d4b4c3 | |||
| b5af7a905a | |||
| 96b1b2f55f | |||
| 9361ba5d70 | |||
| 4e9014311e | |||
| 232873fc70 | |||
| 0a61fca9c9 | |||
| 480bdc34dc | |||
| 716b475a13 | |||
| b1ec5ec034 | |||
| 470613c2b7 | |||
| 336c5b4154 | |||
| abdb9dc750 | |||
| ab98683973 | |||
| 186c24dbe6 | |||
| 9cdf6340a1 | |||
| 2855b1341c | |||
| cf28f99976 | |||
| a39d67dc47 | |||
| 7e75257f12 | |||
| df38449553 | |||
| 1385ef3bc5 | |||
| 7c23c11c51 | |||
| aeff2e12ec | |||
| 7274899671 | |||
| 6c00fd608f | |||
| df6a034c11 | |||
| acb109c290 | |||
| b227b00249 | |||
| 3ec9d6c18a | |||
| 86db8b95f0 | |||
| 4cfff8a35f | |||
| 962fedf48c | |||
| 04494414d1 | |||
| 0b83576e9b | |||
| ce4ef0f817 | |||
| f0b15c1c56 | |||
| f898e9fdfe | |||
| 8ee7804b0e | |||
| 923228194e | |||
| 8837b2e3f6 | |||
| f48c9dc1c2 | |||
| 74aa07a8cd | |||
| 5236e4860f | |||
| 3d174da1c3 | |||
| 8faa40f3a3 | |||
| 3e03275f28 | |||
| 7595cf8a25 | |||
| 2675d43adb | |||
| d9f4ce82e6 | |||
| 648a1845d8 | |||
| a87eff6d1c | |||
| dd6ad37e23 | |||
| c5e778b939 | |||
| a9ab81a77a | |||
| 2d40e1d8e5 | |||
| 7c8f367a5d | |||
| ff4cf558aa | |||
| 0e151e51bc | |||
| 60cc0031b0 | |||
| d24cac0998 | |||
| 45d110830c | |||
| ea3a9ae911 | |||
| 6bff8c0e7c | |||
| bc6b8802b8 | |||
| 252ef572d3 | |||
| 414d397e3f | |||
| 11b7b3789d | |||
| 871ac53717 | |||
| 64ea66b6e6 | |||
| 6d7c6759b3 | |||
| 4cea013570 | |||
| eb375c0587 | |||
| 7fd8a5a4bd | |||
| b3c90216bb | |||
| 10f345f956 | |||
| 956d2c36d0 | |||
| 55e688b6f2 | |||
| f017954027 | |||
| 7e992d11b1 |
@@ -2,5 +2,6 @@ Wen
|
||||
REGIST
|
||||
PullRequest
|
||||
cancelled
|
||||
indeces
|
||||
FOF
|
||||
NoO
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
name: Test Stock vs Sunnypilot Model Equivalence
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
model_ref:
|
||||
description: 'Upstream openpilot commit ref'
|
||||
required: false
|
||||
default: ''
|
||||
pull_request:
|
||||
paths:
|
||||
- 'openpilot/selfdrive/modeld/**'
|
||||
- 'openpilot/sunnypilot/modeld_v2/**'
|
||||
|
||||
jobs:
|
||||
test_stock_parity:
|
||||
name: Compare Stock vs Sunnypilot Model Replay
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: ./tools/op.sh setup
|
||||
- run: scons -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu) openpilot/cereal msgq_repo openpilot/common
|
||||
- name: Fetch Big Model ONNX
|
||||
run: |
|
||||
mkdir -p /tmp/onnx_models
|
||||
if [ -n "${{ inputs.model_ref }}" ]; then
|
||||
echo "Fetching ONNX from upstream openpilot ref ${{ inputs.model_ref }}..."
|
||||
git clone --depth 1 https://github.com/commaai/openpilot.git /tmp/upstream_openpilot
|
||||
cd /tmp/upstream_openpilot
|
||||
git fetch --depth 1 origin ${{ inputs.model_ref }}
|
||||
git checkout ${{ inputs.model_ref }}
|
||||
git lfs pull -I "**/selfdrive/modeld/models/big_driving_supercombo.onnx"
|
||||
find . -name "big_driving_supercombo.onnx" -exec cp {} /tmp/onnx_models/ \;
|
||||
else
|
||||
cp openpilot/selfdrive/modeld/models/big_driving_supercombo.onnx /tmp/onnx_models/
|
||||
fi
|
||||
|
||||
- name: Compile models
|
||||
env:
|
||||
DEV: "CPU"
|
||||
JIT_BATCH_SIZE: "0"
|
||||
run: |
|
||||
BIG_ONNX="/tmp/onnx_models/big_driving_supercombo.onnx"
|
||||
MODEL_SIZE=$(python3 -c "from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE as s; print(f'{s[0]}x{s[1]}')")
|
||||
CAMERA_RESOLUTIONS=$(python3 -c "from openpilot.common.transformations.camera import _ar_ox_fisheye as a, _os_fisheye as o; print(f'{a.width}x{a.height} {o.width}x{o.height}')")
|
||||
|
||||
python3 openpilot/selfdrive/modeld/compile_modeld.py \
|
||||
--onnx "$BIG_ONNX" \
|
||||
--model-size "$MODEL_SIZE" \
|
||||
--camera-resolutions $CAMERA_RESOLUTIONS \
|
||||
--output /tmp/stock_model.pkl \
|
||||
--frame-skip 4 \
|
||||
--benchmark-runs 1 &
|
||||
|
||||
python3 openpilot/sunnypilot/modeld_v2/compile_modeld.py \
|
||||
--model-type supercombo \
|
||||
--supercombo-onnx "$BIG_ONNX" \
|
||||
--model-size "$MODEL_SIZE" \
|
||||
--camera-resolutions $CAMERA_RESOLUTIONS \
|
||||
--output /tmp/sunnypilot_model.pkl \
|
||||
--frame-skip 4 \
|
||||
--benchmark-runs 1 &
|
||||
wait
|
||||
|
||||
- name: Run model replay
|
||||
env:
|
||||
DEV: "CPU"
|
||||
run: |
|
||||
python3 openpilot/sunnypilot/modeld_v2/model_replay.py \
|
||||
--sunnypilot-model /tmp/sunnypilot_model.pkl \
|
||||
--stock-model /tmp/stock_model.pkl \
|
||||
--frames 20 \
|
||||
--plot-dir /tmp/replay_plots
|
||||
|
||||
- name: Upload Replay Plots
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: model_replay_plots_${{ github.event.number || github.sha }}
|
||||
path: /tmp/replay_plots
|
||||
|
||||
- name: Checkout ci-artifacts
|
||||
if: github.repository == 'sunnypilot/sunnypilot' && github.event_name == 'pull_request'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: sunnypilot/ci-artifacts
|
||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||
path: ${{ github.workspace }}/ci-artifacts
|
||||
|
||||
- name: Push plots to ci-artifacts
|
||||
if: github.repository == 'sunnypilot/sunnypilot' && github.event_name == 'pull_request'
|
||||
working-directory: ${{ github.workspace }}/ci-artifacts
|
||||
run: |
|
||||
git config user.name "GitHub Actions Bot"
|
||||
git config user.email "<>"
|
||||
BRANCH="model_replay_pr_${{ github.event.number }}"
|
||||
git fetch origin $BRANCH || true
|
||||
git checkout $BRANCH 2>/dev/null || git checkout --orphan $BRANCH
|
||||
rm -rf plots && mkdir -p plots
|
||||
cp /tmp/replay_plots/*.png plots/
|
||||
echo "${{ github.sha }}" > ref_commit
|
||||
git add plots ref_commit
|
||||
git commit -m "Model replay plots for PR #${{ github.event.number }}@${{ github.sha }}" || echo "No changes to commit"
|
||||
git push origin $BRANCH --force
|
||||
|
||||
- name: Comment Model Replay Report on PR
|
||||
if: github.repository == 'sunnypilot/sunnypilot' && github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const branch = `model_replay_pr_${prNumber}`;
|
||||
const baseUrl = `https://raw.githubusercontent.com/sunnypilot/ci-artifacts/refs/heads/${branch}/plots`;
|
||||
|
||||
const priorityPlots = ['desiredCurvature.png', 'desiredAcceleration.png', 'velocity.x.png', 'leadsV3.x.png', 'execution_timings.png'];
|
||||
const allFiles = fs.readdirSync('/tmp/replay_plots').filter(f => f.endsWith('.png'));
|
||||
const orderedFiles = [
|
||||
...priorityPlots.filter(f => allFiles.includes(f)),
|
||||
...allFiles.filter(f => !priorityPlots.includes(f)).sort()
|
||||
];
|
||||
|
||||
let table = '<table>';
|
||||
for (let i = 0; i < orderedFiles.length; i += 2) {
|
||||
table += '<tr>';
|
||||
table += `<td><img src="${baseUrl}/${orderedFiles[i]}" alt="${orderedFiles[i]}"><br><b>${orderedFiles[i].replace('.png', '')}</b></td>`;
|
||||
if (i + 1 < orderedFiles.length) {
|
||||
table += `<td><img src="${baseUrl}/${orderedFiles[i+1]}" alt="${orderedFiles[i+1]}"><br><b>${orderedFiles[i+1].replace('.png', '')}</b></td>`;
|
||||
} else {
|
||||
table += '<td></td>';
|
||||
}
|
||||
table += '</tr>';
|
||||
}
|
||||
table += '</table>';
|
||||
|
||||
const body = `### Model Replay Parity Report for PR #${prNumber} (@${context.sha.substring(0, 7)})\n\n` +
|
||||
`<details><summary>All Model Replay Plots</summary>\n\n${table}\n\n</details>`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: body
|
||||
});
|
||||
Generated
+7
@@ -21,5 +21,12 @@
|
||||
</clean>
|
||||
</configuration>
|
||||
</target>
|
||||
<target id="f2590b2b-9b93-49f9-8510-da3f3724a2ae" name="replay" defaultType="TOOL">
|
||||
<configuration id="d475264f-6f4c-4092-9b4e-6773309f38b7" name="replay" toolchainName="Default">
|
||||
<build type="TOOL">
|
||||
<tool actionId="Tool_External Tools_uv build tools replay" />
|
||||
</build>
|
||||
</configuration>
|
||||
</target>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
@@ -20,4 +20,11 @@
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
<tool name="uv build tools replay" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
|
||||
<exec>
|
||||
<option name="COMMAND" value="bash" />
|
||||
<option name="PARAMETERS" value="-c "source .venv/bin/activate && scons -u -j$(nproc) tools/replay/"" />
|
||||
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
|
||||
</exec>
|
||||
</tool>
|
||||
</toolSet>
|
||||
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
|
||||
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="openpilot-special" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
|
||||
<envs>
|
||||
<env name="QT_DBL_CLICK_DIST" value="150" />
|
||||
</envs>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Debug Route Controls" type="PythonConfigurationType" factoryName="Python">
|
||||
<module name="openpilot-special" />
|
||||
<option name="ENV_FILES" value="" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
<env name="FINGERPRINT" value="KIA_EV9" />
|
||||
<env name="SKIP_FW_QUERY" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/selfdrive/car" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/selfdrive/car/card.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="true" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Replay for controls + ui" type="Multirun" separateTabs="false" reuseTabsWithFailures="false" startOneByOne="true" markFailedProcess="true" hideSuccessProcess="false" delayTime="0.0">
|
||||
<runConfiguration name="replay for controls" type="Native Application" />
|
||||
<runConfiguration name="Build Debug" type="Custom Build Application" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="replay for controls" type="CLionNativeAppRunConfigurationType" focusToolWindowBeforeRun="true" PROGRAM_PARAMS=""$Prompt$" --block "sendcan,carState,carParams,carOutput,liveTracks,carParamsSP,carStateSP,bookmarkButton"" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="true" WORKING_DIR="file://$ProjectFileDir$/tools/replay" PASS_PARENT_ENVS_2="true" PROJECT_NAME="openpilot-special" TARGET_NAME="replay" CONFIG_NAME="replay" version="1" RUN_PATH="replay">
|
||||
<method v="2">
|
||||
<option name="CLION.COMPOUND.BUILD" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
+1
-1
Submodule opendbc_repo updated: f95f996f59...b5156c66de
@@ -37,6 +37,8 @@ SP_BRANCH_MIGRATIONS = {
|
||||
("tizi", "release3-staging"): "release-tizi-staging",
|
||||
("mici", "release3"): "release-mici",
|
||||
("mici", "release3-staging"): "release-mici-staging",
|
||||
("tici", "hkg-angle-steering-2025"): "hkg-angle-steering-2025-tici",
|
||||
("tici", "hkg-angle-steering-2025-prebuilt"): "hkg-angle-steering-2025-tici-prebuilt"
|
||||
}
|
||||
|
||||
BUILD_METADATA_FILENAME = "build.json"
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from tinygrad.device import Device
|
||||
|
||||
from openpilot.common.file_chunker import open_file_chunked
|
||||
from openpilot.selfdrive.modeld.compile_modeld import MODELD_INPUTS, make_input_queues, nv12_copy_size
|
||||
from openpilot.selfdrive.modeld.helpers import load_oob
|
||||
from openpilot.selfdrive.test.process_replay.model_replay import SEGMENT, TEST_ROUTE
|
||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
||||
from openpilot.tools.lib.framereader import FrameReader
|
||||
from openpilot.tools.lib.openpilotci import get_url
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip
|
||||
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser
|
||||
|
||||
|
||||
def get_replay_video_source(route_or_path=None, segment_index=SEGMENT, camera_type="fcamera.hevc"):
|
||||
if route_or_path and os.path.exists(route_or_path):
|
||||
return route_or_path
|
||||
selected_route = route_or_path or TEST_ROUTE
|
||||
return get_url(selected_route, segment_index, camera_type)
|
||||
|
||||
|
||||
def initialize_replay_queues(model_dictionary, device="CPU"):
|
||||
metadata = model_dictionary.get("metadata", {})
|
||||
model_meta = metadata.get("model", metadata)
|
||||
input_shapes = model_meta.get("input_shapes", {})
|
||||
cam_resolutions = list(model_dictionary.get("run_model", {}).keys())
|
||||
cam_width, cam_height = cam_resolutions[0] if cam_resolutions else (1928, 1208)
|
||||
nv12_info = get_nv12_info(cam_width, cam_height)
|
||||
frame_copy_size = nv12_copy_size(nv12_info[0], nv12_info[1], nv12_info[2])
|
||||
frame_skip = model_meta.get("frame_skip") or derive_frame_skip({}, input_shapes)
|
||||
queues, npy_views, frame_views = make_input_queues(input_shapes, frame_skip, device, frame_copy_size)
|
||||
if "tfm" in npy_views:
|
||||
npy_views["tfm"][:] = np.eye(3, dtype=np.float32)
|
||||
if "big_tfm" in npy_views:
|
||||
npy_views["big_tfm"][:] = np.eye(3, dtype=np.float32)
|
||||
if "traffic_convention" in npy_views:
|
||||
npy_views["traffic_convention"][:] = np.array([1.0, 0.0], dtype=np.float32)
|
||||
return queues, npy_views, frame_views, model_meta
|
||||
|
||||
|
||||
def replay_model_on_frames(model_path, frame_reader, number_of_frames=20):
|
||||
with open_file_chunked(model_path) as file_handle:
|
||||
model_data = load_oob(file_handle)
|
||||
run_model_dict = model_data.get("run_model", {})
|
||||
runner = next(iter(run_model_dict.values()), None)
|
||||
if runner is None:
|
||||
raise ValueError("Failed to resolve runner from model dictionary")
|
||||
|
||||
queues, npy_views, frame_views, model_meta = initialize_replay_queues(model_data)
|
||||
output_slices = model_meta.get("output_slices", {})
|
||||
hidden_state_slice = output_slices.get("hidden_state")
|
||||
parser = Parser(ignore_missing=True)
|
||||
recorded_outputs = []
|
||||
max_frames = min(number_of_frames, getattr(frame_reader, "frame_count", number_of_frames))
|
||||
|
||||
for frame_index in range(max_frames):
|
||||
frame_raw = frame_reader.get(frame_index)
|
||||
if frame_raw is not None:
|
||||
for view in frame_views.values():
|
||||
copy_length = min(view.size, frame_raw.size)
|
||||
view.flat[:copy_length] = frame_raw.flat[:copy_length]
|
||||
|
||||
execution_arguments = {key: queues[key] for key in MODELD_INPUTS if key in queues}
|
||||
execution_start = time.perf_counter()
|
||||
step_output = runner(**execution_arguments)
|
||||
Device.default.synchronize()
|
||||
step_duration = time.perf_counter() - execution_start
|
||||
|
||||
output_array = (step_output[0].numpy() if hasattr(step_output[0], "numpy") else np.array(step_output[0]))
|
||||
flat_output = output_array.flatten()
|
||||
|
||||
if hidden_state_slice and "prev_feat" in npy_views:
|
||||
features_flat = flat_output[hidden_state_slice]
|
||||
target_slice = min(features_flat.size, npy_views["prev_feat"].size)
|
||||
npy_views["prev_feat"].flat[:target_slice] = features_flat[:target_slice]
|
||||
|
||||
sliced_outputs = {slice_name: flat_output[np.newaxis, slice_range] for slice_name, slice_range in output_slices.items()}
|
||||
parser.parse_outputs(sliced_outputs)
|
||||
|
||||
recorded_outputs.append({
|
||||
"frame_index": frame_index,
|
||||
"raw_output": output_array,
|
||||
"parsed_outputs": sliced_outputs,
|
||||
"execution_time": step_duration,
|
||||
})
|
||||
return recorded_outputs
|
||||
|
||||
|
||||
def plot_comparison(series_a, series_b, title, output_directory, label_a="modeld_v2 model", label_b="stock"):
|
||||
os.makedirs(output_directory, exist_ok=True)
|
||||
figure, axis = plt.subplots()
|
||||
axis.plot(series_b, label=label_b)
|
||||
axis.plot(series_a, label=label_a, linestyle="--")
|
||||
axis.set_title(title)
|
||||
axis.legend(loc="best")
|
||||
plot_path = os.path.join(output_directory, f"{title}.png")
|
||||
figure.savefig(plot_path)
|
||||
plt.close(figure)
|
||||
return plot_path
|
||||
|
||||
|
||||
def compare_models_on_route(new_model_path, old_model_path, route_or_path=None, segment_index=SEGMENT,
|
||||
number_of_frames=20, tolerance=1e-4, label_a="modeld_v2 model", label_b="stock",
|
||||
plot_directory=None, enforce_timings=False):
|
||||
video_url_or_path = get_replay_video_source(route_or_path, segment_index)
|
||||
frame_reader = FrameReader(video_url_or_path, pix_fmt="nv12")
|
||||
old_results = replay_model_on_frames(old_model_path, frame_reader, number_of_frames)
|
||||
new_results = replay_model_on_frames(new_model_path, frame_reader, number_of_frames)
|
||||
|
||||
for step_index, (new_step, old_step) in enumerate(zip(new_results, old_results, strict=True)):
|
||||
new_array = new_step["raw_output"]
|
||||
old_array = old_step["raw_output"]
|
||||
if not np.allclose(new_array, old_array, atol=tolerance, rtol=tolerance):
|
||||
max_absolute_error = np.max(np.abs(new_array - old_array))
|
||||
sys.stderr.write(f"Replay mismatch at frame {step_index}: max absolute error {max_absolute_error:.6f} exceeds tolerance {tolerance}\n")
|
||||
return False
|
||||
|
||||
if len(new_results) > 1 and len(old_results) > 1:
|
||||
new_timings = [step["execution_time"] * 1000.0 for step in new_results[1:] if "execution_time" in step]
|
||||
old_timings = [step["execution_time"] * 1000.0 for step in old_results[1:] if "execution_time" in step]
|
||||
if new_timings and old_timings:
|
||||
print("------------------------------------------------")
|
||||
print("----------------- Model Timing -----------------")
|
||||
print("------------------------------------------------")
|
||||
print(f"{label_a}: avg {np.mean(new_timings):6.2f} ms | max {np.max(new_timings):6.2f} ms")
|
||||
print(f"{label_b}: avg {np.mean(old_timings):6.2f} ms | max {np.max(old_timings):6.2f} ms")
|
||||
|
||||
if plot_directory:
|
||||
first_step_outputs = new_results[0].get("parsed_outputs", {})
|
||||
if "action" in first_step_outputs:
|
||||
series_a_curv = [step["parsed_outputs"]["action"].flatten()[0] for step in new_results]
|
||||
series_b_curv = [step["parsed_outputs"]["action"].flatten()[0] for step in old_results]
|
||||
plot_comparison(series_a_curv, series_b_curv, "desiredCurvature", plot_directory, label_a, label_b)
|
||||
|
||||
series_a_accel = [step["parsed_outputs"]["action"].flatten()[1] for step in new_results]
|
||||
series_b_accel = [step["parsed_outputs"]["action"].flatten()[1] for step in old_results]
|
||||
plot_comparison(series_a_accel, series_b_accel, "desiredAcceleration", plot_directory, label_a, label_b)
|
||||
|
||||
if "plan" in first_step_outputs:
|
||||
series_a_vel = [step["parsed_outputs"]["plan"].flatten()[0] for step in new_results]
|
||||
series_b_vel = [step["parsed_outputs"]["plan"].flatten()[0] for step in old_results]
|
||||
plot_comparison(series_a_vel, series_b_vel, "velocity.x", plot_directory, label_a, label_b)
|
||||
|
||||
if "lead" in first_step_outputs:
|
||||
series_a_lead = [step["parsed_outputs"]["lead"].flatten()[0] for step in new_results]
|
||||
series_b_lead = [step["parsed_outputs"]["lead"].flatten()[0] for step in old_results]
|
||||
plot_comparison(series_a_lead, series_b_lead, "leadsV3.x", plot_directory, label_a, label_b)
|
||||
|
||||
plot_comparison(new_timings, old_timings, "execution_timings", plot_directory, label_a, label_b)
|
||||
|
||||
for slice_name in first_step_outputs:
|
||||
series_a = [np.mean(step["parsed_outputs"][slice_name]) for step in new_results if slice_name in step["parsed_outputs"]]
|
||||
series_b = [np.mean(step["parsed_outputs"][slice_name]) for step in old_results if slice_name in step["parsed_outputs"]]
|
||||
if series_a and series_b:
|
||||
plot_comparison(series_a, series_b, f"output_{slice_name}", plot_directory, label_a, label_b)
|
||||
print(f"Replay comparison result on route ({label_a} vs {label_b}): True")
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argument_parser = argparse.ArgumentParser(description="Model Replay on Real Driving Video")
|
||||
argument_parser.add_argument("--sunnypilot-model", dest="model_a", default=None)
|
||||
argument_parser.add_argument("--stock-model", dest="model_b", default=None)
|
||||
argument_parser.add_argument("--route", default=TEST_ROUTE)
|
||||
argument_parser.add_argument("--segment", type=int, default=SEGMENT)
|
||||
argument_parser.add_argument("--frames", type=int, default=20)
|
||||
argument_parser.add_argument("--plot-dir", default=None)
|
||||
parsed_arguments = argument_parser.parse_args()
|
||||
|
||||
matches = compare_models_on_route(parsed_arguments.model_a, parsed_arguments.model_b, route_or_path=parsed_arguments.route,
|
||||
segment_index=parsed_arguments.segment, number_of_frames=parsed_arguments.frames,
|
||||
tolerance=1e-4, label_a="modeld_v2 model",
|
||||
label_b="stock", plot_directory=parsed_arguments.plot_dir)
|
||||
if not matches:
|
||||
sys.exit(1)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user