mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-09-18 10:23:43 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea4e165020 | |||
| 80663f0406 | |||
| 6a741b0539 | |||
| 66cc67cfcb | |||
| f9695484ef | |||
| 1f8f980729 |
@@ -0,0 +1,58 @@
|
|||||||
|
name: 'automatically cache based on current runner'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
path:
|
||||||
|
description: 'path to cache'
|
||||||
|
required: true
|
||||||
|
key:
|
||||||
|
description: 'key'
|
||||||
|
required: true
|
||||||
|
restore-keys:
|
||||||
|
description: 'restore-keys'
|
||||||
|
required: true
|
||||||
|
save:
|
||||||
|
description: 'whether to save the cache'
|
||||||
|
default: 'true'
|
||||||
|
required: false
|
||||||
|
outputs:
|
||||||
|
cache-hit:
|
||||||
|
description: 'cache hit occurred'
|
||||||
|
value: ${{ (contains(runner.name, 'nsc') && steps.ns-cache.outputs.cache-hit) ||
|
||||||
|
(!contains(runner.name, 'nsc') && inputs.save != 'false' && steps.gha-cache.outputs.cache-hit) ||
|
||||||
|
(!contains(runner.name, 'nsc') && inputs.save == 'false' && steps.gha-cache-ro.outputs.cache-hit) }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: setup namespace cache
|
||||||
|
id: ns-cache
|
||||||
|
if: ${{ contains(runner.name, 'nsc') }}
|
||||||
|
uses: namespacelabs/nscloud-cache-action@v1
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
|
||||||
|
- name: setup github cache
|
||||||
|
id: gha-cache
|
||||||
|
if: ${{ !contains(runner.name, 'nsc') && inputs.save != 'false' }}
|
||||||
|
uses: 'actions/cache@v4'
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
key: ${{ inputs.key }}
|
||||||
|
restore-keys: ${{ inputs.restore-keys }}
|
||||||
|
|
||||||
|
- name: setup github cache
|
||||||
|
id: gha-cache-ro
|
||||||
|
if: ${{ !contains(runner.name, 'nsc') && inputs.save == 'false' }}
|
||||||
|
uses: 'actions/cache/restore@v4'
|
||||||
|
with:
|
||||||
|
path: ${{ inputs.path }}
|
||||||
|
key: ${{ inputs.key }}
|
||||||
|
restore-keys: ${{ inputs.restore-keys }}
|
||||||
|
|
||||||
|
# make the directory manually in case we didn't get a hit, so it doesn't fail on future steps
|
||||||
|
- id: scons-cache-setup
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir -p ${{ inputs.path }}
|
||||||
|
sudo chmod -R 777 ${{ inputs.path }}
|
||||||
|
sudo chown -R $USER ${{ inputs.path }}
|
||||||
@@ -5,7 +5,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
BASE_IMAGE: sunnypilot-base
|
||||||
|
DOCKER_REGISTRY: ghcr.io/sunnypilot
|
||||||
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/bash -c
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
badges:
|
badges:
|
||||||
@@ -18,10 +20,10 @@ jobs:
|
|||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Push badges
|
- name: Push badges
|
||||||
run: |
|
run: |
|
||||||
python3 selfdrive/ui/translations/create_badges.py
|
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
|
||||||
|
|
||||||
rm .gitattributes
|
rm .gitattributes
|
||||||
|
|
||||||
|
|||||||
@@ -20,23 +20,27 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CI: 1
|
PYTHONWARNINGS: error
|
||||||
|
BASE_IMAGE: openpilot-base
|
||||||
|
BUILD: selfdrive/test/docker_build.sh base
|
||||||
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
generate_cereal_artifact:
|
generate_cereal_artifact:
|
||||||
name: Generate cereal validation artifacts
|
name: Generate cereal validation artifacts
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc) cereal
|
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||||
- name: Generate the log file
|
- name: Generate the log file
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=${{ github.workspace }}
|
${{ env.RUN }} "cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin" && \
|
||||||
python3 cereal/messaging/tests/validate_sp_cereal_upstream.py -g -f schema_instances.bin
|
ls -la
|
||||||
|
ls -la cereal/messaging/tests
|
||||||
- name: 'Prepare artifact'
|
- name: 'Prepare artifact'
|
||||||
run: |
|
run: |
|
||||||
mkdir -p "cereal/messaging/tests/cereal_validations"
|
mkdir -p "cereal/messaging/tests/cereal_validations"
|
||||||
@@ -53,26 +57,20 @@ jobs:
|
|||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: generate_cereal_artifact
|
needs: generate_cereal_artifact
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sunnypilot
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v6
|
|
||||||
- name: Checkout upstream openpilot
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
with:
|
||||||
repository: 'commaai/openpilot'
|
repository: 'commaai/openpilot'
|
||||||
path: openpilot
|
|
||||||
submodules: true
|
submodules: true
|
||||||
ref: "refs/heads/master"
|
ref: "refs/heads/master"
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
working-directory: openpilot
|
run: ${{ env.RUN }} "scons -j$(nproc) cereal"
|
||||||
run: scons -j$(nproc) cereal
|
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: cereal_validations
|
name: cereal_validations
|
||||||
path: openpilot/cereal/messaging/tests/cereal_validations
|
path: cereal/messaging/tests/cereal_validations
|
||||||
- name: 'Run the validation'
|
- name: 'Run the validation'
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=${{ github.workspace }}/openpilot
|
chmod +x cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
||||||
chmod +x openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py
|
${{ env.RUN }} "cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f cereal/messaging/tests/cereal_validations/schema_instances.bin"
|
||||||
python3 openpilot/cereal/messaging/tests/cereal_validations/validate_sp_cereal_upstream.py -r -f openpilot/cereal/messaging/tests/cereal_validations/schema_instances.bin
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
name: weekly CI test report
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '37 9 * * 1' # 9:37AM UTC -> 2:37AM PST every monday
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
ci_runs:
|
||||||
|
description: 'The amount of runs to trigger in CI test report'
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
CI_RUNS: ${{ github.event.inputs.ci_runs || '50' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
|
||||||
|
steps:
|
||||||
|
- id: ci_runs_setup
|
||||||
|
name: CI_RUNS=${{ env.CI_RUNS }}
|
||||||
|
run: |
|
||||||
|
matrix=$(python3 -c "import json; print(json.dumps({ 'run_number' : list(range(${{ env.CI_RUNS }})) }))")
|
||||||
|
echo "matrix=$matrix" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
ci_matrix_run:
|
||||||
|
needs: [ setup ]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
|
||||||
|
uses: sunnypilot/sunnypilot/.github/workflows/ci_weekly_run.yaml@master
|
||||||
|
with:
|
||||||
|
run_number: ${{ matrix.run_number }}
|
||||||
|
|
||||||
|
report:
|
||||||
|
needs: [ci_matrix_run]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always() && github.repository == 'commaai/openpilot'
|
||||||
|
steps:
|
||||||
|
- name: Get job results
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
id: get-job-results
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const jobs = await github
|
||||||
|
.paginate("GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt}/jobs", {
|
||||||
|
owner: "commaai",
|
||||||
|
repo: "${{ github.event.repository.name }}",
|
||||||
|
run_id: "${{ github.run_id }}",
|
||||||
|
attempt: "${{ github.run_attempt }}",
|
||||||
|
})
|
||||||
|
var report = {}
|
||||||
|
jobs.slice(1, jobs.length-1).forEach(job => {
|
||||||
|
if (job.conclusion === "skipped") return;
|
||||||
|
const jobName = job.name.split(" / ")[2];
|
||||||
|
const runRegex = /\((.*?)\)/;
|
||||||
|
const run = job.name.match(runRegex)[1];
|
||||||
|
report[jobName] = report[jobName] || { successes: [], failures: [], canceled: [] };
|
||||||
|
switch (job.conclusion) {
|
||||||
|
case "success":
|
||||||
|
report[jobName].successes.push({ "run_number": run, "link": job.html_url}); break;
|
||||||
|
case "failure":
|
||||||
|
report[jobName].failures.push({ "run_number": run, "link": job.html_url }); break;
|
||||||
|
case "canceled":
|
||||||
|
report[jobName].canceled.push({ "run_number": run, "link": job.html_url }); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return JSON.stringify({"jobs": report});
|
||||||
|
|
||||||
|
- name: Add job results to summary
|
||||||
|
env:
|
||||||
|
JOB_RESULTS: ${{ fromJSON(steps.get-job-results.outputs.result) }}
|
||||||
|
run: |
|
||||||
|
cat <<EOF >> template.html
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>Job</th>
|
||||||
|
<th>✅ Passing</th>
|
||||||
|
<th>❌ Failure Details</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for key in jobs.keys() %}<tr>
|
||||||
|
<td>{% for i in range(5) %}{% if i+1 <= (5 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }}) %}🟩{% else %}🟥{% endif %}{% endfor%}</td>
|
||||||
|
<td>{{ key }}</td>
|
||||||
|
<td>{{ 100 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }} }}%</td>
|
||||||
|
<td>{% if jobs[key]["failures"]|length > 0 %}<details>{% for failure in jobs[key]["failures"] %}<a href="{{ failure['link'] }}">Log for run #{{ failure['run_number'] }}</a><br>{% endfor %}</details>{% else %}{% endif %}</td>
|
||||||
|
</td>
|
||||||
|
</tr>{% endfor %}
|
||||||
|
</table>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
pip install jinja2-cli
|
||||||
|
echo $JOB_RESULTS | jinja2 template.html > report.html
|
||||||
|
echo "# CI Test Report - ${{ env.CI_RUNS }} Runs" >> $GITHUB_STEP_SUMMARY
|
||||||
|
cat report.html >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
name: weekly CI test run
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
run_number:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-run-${{ inputs.run_number }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests:
|
||||||
|
uses: sunnypilot/sunnypilot/.github/workflows/tests.yaml@master
|
||||||
|
with:
|
||||||
|
run_number: ${{ inputs.run_number }}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: 'compile openpilot'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
name: Build openpilot with all flags
|
||||||
|
run: |
|
||||||
|
${{ env.RUN }} "scons -j$(nproc)"
|
||||||
|
${{ env.RUN }} "release/check-dirty.sh"
|
||||||
|
- shell: bash
|
||||||
|
name: Cleanup scons cache and rebuild
|
||||||
|
run: |
|
||||||
|
${{ env.RUN }} "rm -rf /tmp/scons_cache/* && \
|
||||||
|
scons -j$(nproc) --cache-populate"
|
||||||
|
- name: Save scons cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
with:
|
||||||
|
path: .ci_cache/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
name: "mici raylib ui preview"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request_target:
|
||||||
|
types: [assigned, opened, synchronize, reopened, edited]
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
paths:
|
||||||
|
- 'selfdrive/assets/**'
|
||||||
|
- 'selfdrive/ui/**'
|
||||||
|
- 'system/ui/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
UI_JOB_NAME: "Create mici raylib UI Report"
|
||||||
|
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
|
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||||
|
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-mici-raylib-ui"
|
||||||
|
MASTER_BRANCH_NAME: "openpilot_master_ui_mici_raylib"
|
||||||
|
# All report files are pushed here
|
||||||
|
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
preview:
|
||||||
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
name: preview
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
actions: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Waiting for ui generation to end
|
||||||
|
uses: lewagon/wait-on-check-action@v1.3.4
|
||||||
|
with:
|
||||||
|
ref: ${{ env.SHA }}
|
||||||
|
check-name: ${{ env.UI_JOB_NAME }}
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
allowed-conclusions: success
|
||||||
|
wait-interval: 20
|
||||||
|
|
||||||
|
- name: Getting workflow run ID
|
||||||
|
id: get_run_id
|
||||||
|
run: |
|
||||||
|
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Getting proposed ui # filename: pr_ui/mici_ui_replay.mp4
|
||||||
|
id: download-artifact
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||||
|
search_artifacts: true
|
||||||
|
name: mici-raylib-report-1-${{ env.REPORT_NAME }}
|
||||||
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
|
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: sunnypilot/ci-artifacts
|
||||||
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
path: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
ref: ${{ env.MASTER_BRANCH_NAME }}
|
||||||
|
|
||||||
|
- name: Saving new master ui
|
||||||
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
run: |
|
||||||
|
git checkout --orphan=new_master_ui_mici_raylib
|
||||||
|
git rm -rf *
|
||||||
|
git branch -D ${{ env.MASTER_BRANCH_NAME }}
|
||||||
|
git branch -m ${{ env.MASTER_BRANCH_NAME }}
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
|
git add .
|
||||||
|
git commit -m "mici raylib video for commit ${{ env.SHA }}"
|
||||||
|
git push origin ${{ env.MASTER_BRANCH_NAME }} --force
|
||||||
|
|
||||||
|
- name: Setup FFmpeg
|
||||||
|
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
||||||
|
|
||||||
|
- name: Finding diff
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
id: find_diff
|
||||||
|
run: |
|
||||||
|
# Find the video file from PR
|
||||||
|
pr_video="${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4"
|
||||||
|
mv "${{ github.workspace }}/pr_ui/mici_ui_replay.mp4" "$pr_video"
|
||||||
|
|
||||||
|
master_video="${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4"
|
||||||
|
mv "${{ github.workspace }}/master_ui_raylib/mici_ui_replay.mp4" "$master_video"
|
||||||
|
|
||||||
|
# Run report
|
||||||
|
export PYTHONPATH=${{ github.workspace }}
|
||||||
|
baseurl="https://github.com/sunnypilot/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
||||||
|
diff_exit_code=0
|
||||||
|
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py "${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4" "${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4" "diff.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
||||||
|
|
||||||
|
# Copy diff report files
|
||||||
|
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html ${{ github.workspace }}/pr_ui/
|
||||||
|
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.mp4 ${{ github.workspace }}/pr_ui/
|
||||||
|
|
||||||
|
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/diff_pr_${{ github.event.number }}.html"
|
||||||
|
if [ $diff_exit_code -eq 0 ]; then
|
||||||
|
DIFF="✅ Videos are identical! [View Diff Report]($REPORT_URL)"
|
||||||
|
else
|
||||||
|
DIFF="❌ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"
|
||||||
|
fi
|
||||||
|
echo "DIFF=$DIFF" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Saving proposed ui
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
run: |
|
||||||
|
# Overwrite PR branch w/ proposed ui, and master ui at this point in time for future reference
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||||
|
git rm -rf *
|
||||||
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
|
git add .
|
||||||
|
git commit -m "mici raylib video for PR #${{ github.event.number }}"
|
||||||
|
git push origin ${{ env.BRANCH_NAME }} --force
|
||||||
|
|
||||||
|
# Append diff report to report files branch
|
||||||
|
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html diff_pr_${{ github.event.number }}.html
|
||||||
|
git add diff_pr_${{ github.event.number }}.html
|
||||||
|
git commit -m "mici raylib ui diff report for PR #${{ github.event.number }}" || echo "No changes to commit"
|
||||||
|
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
||||||
|
|
||||||
|
- name: Comment Video on PR
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
|
with:
|
||||||
|
message: |
|
||||||
|
<!-- _(run_id_video_mici_raylib **${{ github.run_id }}**)_ -->
|
||||||
|
## mici raylib UI Preview
|
||||||
|
${{ steps.find_diff.outputs.DIFF }}
|
||||||
|
comment_tag: run_id_video_mici_raylib
|
||||||
|
pr_number: ${{ github.event.number }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -17,8 +17,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
- name: Checkout master
|
- name: Checkout master
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
@@ -27,12 +25,14 @@ jobs:
|
|||||||
- run: git lfs pull
|
- run: git lfs pull
|
||||||
- run: cd base && git lfs pull
|
- run: cd base && git lfs pull
|
||||||
|
|
||||||
|
- run: pip install onnx
|
||||||
|
|
||||||
- name: scripts/reporter.py
|
- name: scripts/reporter.py
|
||||||
id: report
|
id: report
|
||||||
run: |
|
run: |
|
||||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||||
echo "## Model Review" >> $GITHUB_OUTPUT
|
echo "## Model Review" >> $GITHUB_OUTPUT
|
||||||
PYTHONPATH=${{ github.workspace }} MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
|
||||||
echo "EOF" >> $GITHUB_OUTPUT
|
echo "EOF" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Post model report comment
|
- name: Post model report comment
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
BUILD: release/ci/docker_build_sp.sh
|
BUILD: release/ci/docker_build_sp.sh prebuilt
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_prebuilt:
|
build_prebuilt:
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build prebuilt'
|
running-workflow-name: 'build prebuilt'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build master-ci|create badges).*).)*$
|
check-regexp: ^((?!.*(build master-ci).*).)*$
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
name: "raylib ui preview"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request_target:
|
||||||
|
types: [assigned, opened, synchronize, reopened, edited]
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
paths:
|
||||||
|
- 'selfdrive/assets/**'
|
||||||
|
- 'selfdrive/ui/**'
|
||||||
|
- 'system/ui/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
UI_JOB_NAME: "Create raylib UI Report"
|
||||||
|
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
|
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
||||||
|
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
preview:
|
||||||
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
name: preview
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
actions: read
|
||||||
|
steps:
|
||||||
|
- name: Waiting for ui generation to start
|
||||||
|
run: sleep 30
|
||||||
|
|
||||||
|
- name: Waiting for ui generation to end
|
||||||
|
uses: lewagon/wait-on-check-action@v1.3.4
|
||||||
|
with:
|
||||||
|
ref: ${{ env.SHA }}
|
||||||
|
check-name: ${{ env.UI_JOB_NAME }}
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
allowed-conclusions: success
|
||||||
|
wait-interval: 20
|
||||||
|
|
||||||
|
- name: Getting workflow run ID
|
||||||
|
id: get_run_id
|
||||||
|
run: |
|
||||||
|
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Getting proposed ui
|
||||||
|
id: download-artifact
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
||||||
|
search_artifacts: true
|
||||||
|
name: raylib-report-1-${{ env.REPORT_NAME }}
|
||||||
|
path: ${{ github.workspace }}/pr_ui
|
||||||
|
|
||||||
|
- name: Getting master ui
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
repository: sunnypilot/ci-artifacts
|
||||||
|
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
||||||
|
path: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
ref: openpilot_master_ui_raylib
|
||||||
|
|
||||||
|
- name: Saving new master ui
|
||||||
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||||
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
run: |
|
||||||
|
git checkout --orphan=new_master_ui_raylib
|
||||||
|
git rm -rf *
|
||||||
|
git branch -D openpilot_master_ui_raylib
|
||||||
|
git branch -m openpilot_master_ui_raylib
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
mv ${{ github.workspace }}/pr_ui/*.png .
|
||||||
|
git add .
|
||||||
|
git commit -m "raylib screenshots for commit ${{ env.SHA }}"
|
||||||
|
git push origin openpilot_master_ui_raylib --force
|
||||||
|
|
||||||
|
- name: Finding diff
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
id: find_diff
|
||||||
|
run: >-
|
||||||
|
sudo apt-get update && sudo apt-get install -y imagemagick
|
||||||
|
|
||||||
|
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
||||||
|
A=($scenes)
|
||||||
|
|
||||||
|
DIFF=""
|
||||||
|
TABLE="<details><summary>All Screenshots</summary>"
|
||||||
|
TABLE="${TABLE}<table>"
|
||||||
|
|
||||||
|
for ((i=0; i<${#A[*]}; i=i+1));
|
||||||
|
do
|
||||||
|
# Check if the master file exists
|
||||||
|
if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then
|
||||||
|
# This is a new file in PR UI that doesn't exist in master
|
||||||
|
DIFF="${DIFF}<details open>"
|
||||||
|
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
|
||||||
|
DIFF="${DIFF}<table>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}<tr>"
|
||||||
|
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||||
|
DIFF="${DIFF}</tr>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}</table>"
|
||||||
|
DIFF="${DIFF}</details>"
|
||||||
|
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
|
||||||
|
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
|
||||||
|
composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png
|
||||||
|
convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
|
||||||
|
|
||||||
|
mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
|
||||||
|
|
||||||
|
DIFF="${DIFF}<details open>"
|
||||||
|
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
|
||||||
|
DIFF="${DIFF}<table>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}<tr>"
|
||||||
|
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
|
||||||
|
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||||
|
DIFF="${DIFF}</tr>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}<tr>"
|
||||||
|
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
|
||||||
|
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
|
||||||
|
DIFF="${DIFF}</tr>"
|
||||||
|
|
||||||
|
DIFF="${DIFF}</table>"
|
||||||
|
DIFF="${DIFF}</details>"
|
||||||
|
else
|
||||||
|
rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png
|
||||||
|
fi
|
||||||
|
|
||||||
|
INDEX=$(($i % 2))
|
||||||
|
if [[ $INDEX -eq 0 ]]; then
|
||||||
|
TABLE="${TABLE}<tr>"
|
||||||
|
fi
|
||||||
|
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/sunnypilot/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
|
||||||
|
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
|
||||||
|
TABLE="${TABLE}</tr>"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
TABLE="${TABLE}</table></details>"
|
||||||
|
|
||||||
|
echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Saving proposed ui
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
working-directory: ${{ github.workspace }}/master_ui_raylib
|
||||||
|
run: |
|
||||||
|
git config user.name "GitHub Actions Bot"
|
||||||
|
git config user.email "<>"
|
||||||
|
git checkout --orphan=${{ env.BRANCH_NAME }}
|
||||||
|
git rm -rf *
|
||||||
|
mv ${{ github.workspace }}/pr_ui/* .
|
||||||
|
git add .
|
||||||
|
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
|
||||||
|
git push origin ${{ env.BRANCH_NAME }} --force
|
||||||
|
|
||||||
|
- name: Comment Screenshots on PR
|
||||||
|
if: github.event_name == 'pull_request_target'
|
||||||
|
uses: thollander/actions-comment-pull-request@v2
|
||||||
|
with:
|
||||||
|
message: |
|
||||||
|
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
|
||||||
|
## raylib UI Preview
|
||||||
|
${{ steps.find_diff.outputs.DIFF }}
|
||||||
|
comment_tag: run_id_screenshots_raylib
|
||||||
|
pr_number: ${{ github.event.number }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -7,12 +7,20 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build___nightly:
|
build___nightly:
|
||||||
name: build __nightly
|
name: build __nightly
|
||||||
|
env:
|
||||||
|
ImageOS: ubuntu24
|
||||||
|
container:
|
||||||
|
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
permissions:
|
permissions:
|
||||||
checks: read
|
checks: read
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install wait-on-check-action dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libyaml-dev
|
||||||
- name: Wait for green check mark
|
- name: Wait for green check mark
|
||||||
if: ${{ github.event_name == 'schedule' }}
|
if: ${{ github.event_name == 'schedule' }}
|
||||||
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
|
||||||
@@ -21,11 +29,14 @@ jobs:
|
|||||||
wait-interval: 30
|
wait-interval: 30
|
||||||
running-workflow-name: 'build __nightly'
|
running-workflow-name: 'build __nightly'
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
check-regexp: ^((?!.*(build prebuilt|create badges).*).)*$
|
check-regexp: ^((?!.*(build prebuilt).*).)*$
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: ./tools/op.sh setup
|
- name: Pull LFS
|
||||||
|
run: |
|
||||||
|
git config --global --add safe.directory '*'
|
||||||
|
git lfs pull
|
||||||
- name: Push __nightly
|
- name: Push __nightly
|
||||||
run: BRANCH=__nightly release/build_stripped.sh
|
run: BRANCH=__nightly release/build_stripped.sh
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
BASE_IMAGE: sunnypilot-base
|
||||||
|
BUILD: release/ci/docker_build_sp.sh base
|
||||||
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update_translations:
|
update_translations:
|
||||||
@@ -14,11 +16,10 @@ jobs:
|
|||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
submodules: true
|
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: Update translations
|
- name: Update translations
|
||||||
run: python3 selfdrive/ui/update_translations.py --vanish
|
run: |
|
||||||
|
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||||
with:
|
with:
|
||||||
@@ -34,36 +35,27 @@ jobs:
|
|||||||
package_updates:
|
package_updates:
|
||||||
name: package_updates
|
name: package_updates
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
|
||||||
- name: uv lock
|
- name: uv lock
|
||||||
run: uv lock --upgrade
|
run: |
|
||||||
|
python3 -m ensurepip --upgrade
|
||||||
|
pip3 install uv
|
||||||
|
uv lock --upgrade
|
||||||
- name: uv pip tree
|
- name: uv pip tree
|
||||||
id: pip_tree
|
id: pip_tree
|
||||||
run: |
|
run: |
|
||||||
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
echo 'PIP_TREE<<EOF' >> $GITHUB_OUTPUT
|
||||||
uv pip tree >> $GITHUB_OUTPUT
|
uv pip tree >> $GITHUB_OUTPUT
|
||||||
echo 'EOF' >> $GITHUB_OUTPUT
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
- name: venv size
|
|
||||||
id: venv_size
|
|
||||||
run: |
|
|
||||||
echo 'VENV_SIZE<<EOF' >> $GITHUB_OUTPUT
|
|
||||||
echo "Total: $(du -sh .venv | cut -f1)" >> $GITHUB_OUTPUT
|
|
||||||
echo "" >> $GITHUB_OUTPUT
|
|
||||||
echo "Top 10 by size:" >> $GITHUB_OUTPUT
|
|
||||||
du -sh .venv/lib/python*/site-packages/* 2>/dev/null \
|
|
||||||
| grep -v '\.dist-info' \
|
|
||||||
| grep -v '__pycache__' \
|
|
||||||
| sort -rh \
|
|
||||||
| head -10 \
|
|
||||||
| while IFS=$'\t' read size path; do echo "$size ${path##*/}"; done >> $GITHUB_OUTPUT
|
|
||||||
echo 'EOF' >> $GITHUB_OUTPUT
|
|
||||||
- name: bump submodules
|
- name: bump submodules
|
||||||
run: |
|
run: |
|
||||||
|
git config --global --add safe.directory '*'
|
||||||
git config submodule.msgq.update none
|
git config submodule.msgq.update none
|
||||||
git config submodule.rednose_repo.update none
|
git config submodule.rednose_repo.update none
|
||||||
git config submodule.teleoprtc_repo.update none
|
git config submodule.teleoprtc_repo.update none
|
||||||
@@ -72,6 +64,7 @@ jobs:
|
|||||||
git add .
|
git add .
|
||||||
- name: update car docs
|
- name: update car docs
|
||||||
run: |
|
run: |
|
||||||
|
export PYTHONPATH="$PWD"
|
||||||
scons -j$(nproc) --minimal opendbc_repo
|
scons -j$(nproc) --minimal opendbc_repo
|
||||||
python selfdrive/car/docs.py
|
python selfdrive/car/docs.py
|
||||||
git add docs/CARS.md
|
git add docs/CARS.md
|
||||||
@@ -89,12 +82,6 @@ jobs:
|
|||||||
Automatic PR from repo-maintenance -> package_updates
|
Automatic PR from repo-maintenance -> package_updates
|
||||||
|
|
||||||
```
|
```
|
||||||
$ du -sh .venv && du -sh .venv/lib/python*/site-packages/* | sort -rh | head -10
|
|
||||||
${{ steps.venv_size.outputs.VENV_SIZE }}
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
$ uv pip tree
|
|
||||||
${{ steps.pip_tree.outputs.PIP_TREE }}
|
${{ steps.pip_tree.outputs.PIP_TREE }}
|
||||||
```
|
```
|
||||||
labels: bot
|
labels: bot
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
name: 'openpilot env setup, with retry on failure'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
docker_hub_pat:
|
||||||
|
description: 'Auth token for Docker Hub, required for BuildJet jobs'
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
sleep_time:
|
||||||
|
description: 'Time to sleep between retries'
|
||||||
|
required: false
|
||||||
|
default: 30
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
duration:
|
||||||
|
description: 'Duration of the setup process in seconds'
|
||||||
|
value: ${{ steps.get_duration.outputs.duration }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- id: start_time
|
||||||
|
shell: bash
|
||||||
|
run: echo "START_TIME=$(date +%s)" >> $GITHUB_ENV
|
||||||
|
- id: setup1
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
|
- if: steps.setup1.outcome == 'failure'
|
||||||
|
shell: bash
|
||||||
|
run: sleep ${{ inputs.sleep_time }}
|
||||||
|
- id: setup2
|
||||||
|
if: steps.setup1.outcome == 'failure'
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
continue-on-error: true
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
|
- if: steps.setup2.outcome == 'failure'
|
||||||
|
shell: bash
|
||||||
|
run: sleep ${{ inputs.sleep_time }}
|
||||||
|
- id: setup3
|
||||||
|
if: steps.setup2.outcome == 'failure'
|
||||||
|
uses: ./.github/workflows/setup
|
||||||
|
with:
|
||||||
|
is_retried: true
|
||||||
|
- id: get_duration
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
END_TIME=$(date +%s)
|
||||||
|
DURATION=$((END_TIME - START_TIME))
|
||||||
|
echo "Total duration: $DURATION seconds"
|
||||||
|
echo "duration=$DURATION" >> $GITHUB_OUTPUT
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
name: 'openpilot env setup'
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
is_retried:
|
||||||
|
description: 'A mock param that asserts that we use the setup-with-retry instead of this action directly'
|
||||||
|
required: false
|
||||||
|
default: 'false'
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
# assert that this action is retried using the setup-with-retry
|
||||||
|
- shell: bash
|
||||||
|
if: ${{ inputs.is_retried == 'false' }}
|
||||||
|
run: |
|
||||||
|
echo "You should not run this action directly. Use setup-with-retry instead"
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- shell: bash
|
||||||
|
name: No retries!
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.run_attempt }}" -gt ${{ github.event.pull_request.head.repo.fork && github.event.pull_request.author_association == 'NONE' && 2 || 1}} ]; then
|
||||||
|
echo -e "\033[0;31m##################################################"
|
||||||
|
echo -e "\033[0;31m Retries not allowed! Fix the flaky test! "
|
||||||
|
echo -e "\033[0;31m##################################################\033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# do this after checkout to ensure our custom LFS config is used to pull from GitLab
|
||||||
|
- shell: bash
|
||||||
|
run: git lfs pull
|
||||||
|
|
||||||
|
# build cache
|
||||||
|
- id: date
|
||||||
|
shell: bash
|
||||||
|
run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||||
|
- shell: bash
|
||||||
|
run: echo "$CACHE_COMMIT_DATE"
|
||||||
|
- id: scons-cache
|
||||||
|
uses: ./.github/workflows/auto-cache
|
||||||
|
with:
|
||||||
|
path: .ci_cache/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
scons-${{ runner.arch }}-${{ env.CACHE_COMMIT_DATE }}
|
||||||
|
scons-${{ runner.arch }}
|
||||||
|
# as suggested here: https://github.com/moby/moby/issues/32816#issuecomment-910030001
|
||||||
|
- id: normalize-file-permissions
|
||||||
|
shell: bash
|
||||||
|
name: Normalize file permissions to ensure a consistent docker build cache
|
||||||
|
run: |
|
||||||
|
find . -type f -executable -not -perm 755 -exec chmod 755 {} \;
|
||||||
|
find . -type f -not -executable -not -perm 644 -exec chmod 644 {} \;
|
||||||
|
# build our docker image
|
||||||
|
- shell: bash
|
||||||
|
run: eval ${{ env.BUILD }}
|
||||||
@@ -173,18 +173,9 @@ jobs:
|
|||||||
|
|
||||||
echo "Compiling: $onnx_file -> $output_file"
|
echo "Compiling: $onnx_file -> $output_file"
|
||||||
QCOM=1 python3 "${{ env.TINYGRAD_PATH }}/examples/openpilot/compile3.py" "$onnx_file" "$output_file"
|
QCOM=1 python3 "${{ env.TINYGRAD_PATH }}/examples/openpilot/compile3.py" "$onnx_file" "$output_file"
|
||||||
DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
QCOM=1 python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Validate Model Outputs
|
|
||||||
run: |
|
|
||||||
source /etc/profile
|
|
||||||
export UV_PROJECT_ENVIRONMENT=${HOME}/venv
|
|
||||||
export VIRTUAL_ENV=$UV_PROJECT_ENVIRONMENT
|
|
||||||
python3 "${{ github.workspace }}/release/ci/model_generator.py" \
|
|
||||||
--validate-only \
|
|
||||||
--model-dir "${{ env.MODELS_DIR }}"
|
|
||||||
|
|
||||||
- name: Prepare Output
|
- name: Prepare Output
|
||||||
run: |
|
run: |
|
||||||
sudo rm -rf ${{ env.OUTPUT_DIR }}
|
sudo rm -rf ${{ env.OUTPUT_DIR }}
|
||||||
@@ -193,6 +184,7 @@ jobs:
|
|||||||
# Copy the model files
|
# Copy the model files
|
||||||
rsync -avm \
|
rsync -avm \
|
||||||
--include='*.dlc' \
|
--include='*.dlc' \
|
||||||
|
--include='*.thneed' \
|
||||||
--include='*.pkl' \
|
--include='*.pkl' \
|
||||||
--include='*.onnx' \
|
--include='*.onnx' \
|
||||||
--exclude='*' \
|
--exclude='*' \
|
||||||
|
|||||||
@@ -180,6 +180,8 @@ jobs:
|
|||||||
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
./release/release_files.py | sort | uniq | rsync -rRl${RUNNER_DEBUG:+v} --files-from=- . $BUILD_DIR/
|
||||||
cd $BUILD_DIR
|
cd $BUILD_DIR
|
||||||
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
sed -i '/from .board.jungle import PandaJungle, PandaJungleDFU/s/^/#/' panda/__init__.py
|
||||||
|
echo "Building sunnypilot's modeld..."
|
||||||
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/modeld
|
||||||
echo "Building sunnypilot's modeld_v2..."
|
echo "Building sunnypilot's modeld_v2..."
|
||||||
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/modeld_v2
|
scons -j$(nproc) cache_dir=${{env.SCONS_CACHE_DIR}} --minimal sunnypilot/modeld_v2
|
||||||
echo "Building sunnypilot's locationd..."
|
echo "Building sunnypilot's locationd..."
|
||||||
@@ -217,6 +219,7 @@ jobs:
|
|||||||
--exclude='**/.venv/' \
|
--exclude='**/.venv/' \
|
||||||
--exclude='selfdrive/modeld/models/driving_vision.onnx' \
|
--exclude='selfdrive/modeld/models/driving_vision.onnx' \
|
||||||
--exclude='selfdrive/modeld/models/driving_policy.onnx' \
|
--exclude='selfdrive/modeld/models/driving_policy.onnx' \
|
||||||
|
--exclude='sunnypilot/modeld*/models/supercombo.onnx' \
|
||||||
--exclude='third_party/*x86*' \
|
--exclude='third_party/*x86*' \
|
||||||
--exclude='third_party/*Darwin*' \
|
--exclude='third_party/*Darwin*' \
|
||||||
--delete-excluded \
|
--delete-excluded \
|
||||||
|
|||||||
+160
-58
@@ -18,8 +18,13 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CI: 1
|
PYTHONWARNINGS: error
|
||||||
PYTHONPATH: ${{ github.workspace }}
|
BASE_IMAGE: sunnypilot-base
|
||||||
|
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
BUILD: release/ci/docker_build_sp.sh base
|
||||||
|
|
||||||
|
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||||
|
|
||||||
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -29,11 +34,10 @@ jobs:
|
|||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
env:
|
env:
|
||||||
STRIPPED_DIR: /tmp/releasepilot
|
STRIPPED_DIR: /tmp/releasepilot
|
||||||
PYTHONPATH: /tmp/releasepilot
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
@@ -47,15 +51,17 @@ jobs:
|
|||||||
- name: Build devel
|
- name: Build devel
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
|
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot and run checks
|
- name: Build openpilot and run checks
|
||||||
timeout-minutes: 30
|
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
|
||||||
working-directory: ${{ env.STRIPPED_DIR }}
|
run: |
|
||||||
run: python3 system/manager/build.py
|
cd $STRIPPED_DIR
|
||||||
|
${{ env.RUN }} "python3 system/manager/build.py"
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
working-directory: ${{ env.STRIPPED_DIR }}
|
run: |
|
||||||
run: release/check-dirty.sh
|
cd $STRIPPED_DIR
|
||||||
|
${{ env.RUN }} "release/check-dirty.sh"
|
||||||
- name: Check submodules
|
- name: Check submodules
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
if: github.repository == 'sunnypilot/sunnypilot'
|
||||||
timeout-minutes: 3
|
timeout-minutes: 3
|
||||||
@@ -77,20 +83,73 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
release/check-submodules.sh
|
release/check-submodules.sh
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Setup docker push
|
||||||
|
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'sunnypilot/sunnypilot'
|
||||||
|
run: |
|
||||||
|
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
|
||||||
|
$DOCKER_LOGIN
|
||||||
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
- uses: ./.github/workflows/compile-openpilot
|
||||||
|
timeout-minutes: 30
|
||||||
|
|
||||||
build_mac:
|
build_mac:
|
||||||
name: build macOS
|
name: build macOS
|
||||||
|
if: false # tmp disable due to brew install not working
|
||||||
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- name: Remove Homebrew from environment
|
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
|
||||||
run: |
|
- name: Homebrew cache
|
||||||
FILTERED=$(echo "$PATH" | tr ':' '\n' | grep -v '/opt/homebrew' | tr '\n' ':')
|
uses: ./.github/workflows/auto-cache
|
||||||
echo "PATH=${FILTERED}/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" >> $GITHUB_ENV
|
with:
|
||||||
- run: ./tools/op.sh setup
|
save: false # No need save here if we manually save it later conditionally
|
||||||
|
path: ~/Library/Caches/Homebrew
|
||||||
|
key: brew-macos-${{ hashFiles('tools/Brewfile') }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
brew-macos-${{ hashFiles('tools/Brewfile') }}
|
||||||
|
brew-macos-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: ./tools/mac_setup.sh
|
||||||
|
env:
|
||||||
|
PYTHONWARNINGS: default # package install has DeprecationWarnings
|
||||||
|
HOMEBREW_DISPLAY_INSTALL_TIMES: 1
|
||||||
|
- name: Save Homebrew cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
with:
|
||||||
|
path: ~/Library/Caches/Homebrew
|
||||||
|
key: brew-macos-${{ hashFiles('tools/Brewfile') }}-${{ github.sha }}
|
||||||
|
- run: git lfs pull
|
||||||
|
- name: Getting scons cache
|
||||||
|
uses: ./.github/workflows/auto-cache
|
||||||
|
with:
|
||||||
|
save: false # No need save here if we manually save it later conditionally
|
||||||
|
path: /tmp/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}
|
||||||
|
scons-${{ runner.arch }}-macos
|
||||||
- name: Building openpilot
|
- name: Building openpilot
|
||||||
run: scons
|
run: . .venv/bin/activate && scons -j$(nproc)
|
||||||
|
- name: Save scons cache
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
with:
|
||||||
|
path: /tmp/scons_cache
|
||||||
|
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
|
||||||
|
|
||||||
static_analysis:
|
static_analysis:
|
||||||
name: static analysis
|
name: static analysis
|
||||||
@@ -98,16 +157,18 @@ jobs:
|
|||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
env:
|
||||||
|
PYTHONWARNINGS: default
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Static analysis
|
- name: Static analysis
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
run: scripts/lint/lint.sh
|
run: ${{ env.RUN }} "scripts/lint/lint.sh"
|
||||||
|
|
||||||
unit_tests:
|
unit_tests:
|
||||||
name: unit tests
|
name: unit tests
|
||||||
@@ -115,22 +176,24 @@ jobs:
|
|||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
id: setup-step
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc)
|
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 999 }}
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 999 }}
|
||||||
run: |
|
run: |
|
||||||
source selfdrive/test/setup_xvfb.sh
|
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||||
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
# Pre-compile Python bytecode so each pytest worker doesn't need to
|
||||||
$PYTEST --collect-only -m 'not slow' -qq
|
$PYTEST --collect-only -m 'not slow' -qq && \
|
||||||
MAX_EXAMPLES=1 $PYTEST -m 'not slow'
|
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
|
||||||
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
|
|
||||||
process_replay:
|
process_replay:
|
||||||
name: process replay
|
name: process replay
|
||||||
@@ -139,19 +202,29 @@ jobs:
|
|||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
id: setup-step
|
||||||
|
- name: Cache test routes
|
||||||
|
id: dependency-cache
|
||||||
|
uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: .ci_cache/comma_download_cache
|
||||||
|
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/test_processes.py') }}
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc)
|
run: |
|
||||||
|
${{ env.RUN }} "scons -j$(nproc)"
|
||||||
- name: Run replay
|
- name: Run replay
|
||||||
timeout-minutes: ${{ contains(runner.name, 'nsc') && 2 || 20 }}
|
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
|
||||||
continue-on-error: ${{ github.ref == 'refs/heads/master' }}
|
continue-on-error: ${{ github.ref == 'refs/heads/master' }}
|
||||||
run: selfdrive/test/process_replay/test_processes.py -j$(nproc)
|
run: |
|
||||||
|
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
|
||||||
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
- name: Print diff
|
- name: Print diff
|
||||||
id: print-diff
|
id: print-diff
|
||||||
if: always()
|
if: always()
|
||||||
@@ -173,21 +246,21 @@ jobs:
|
|||||||
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
if: github.repository == 'commaai/openpilot' && github.ref == 'refs/heads/master'
|
||||||
working-directory: ${{ github.workspace }}/ci-artifacts
|
working-directory: ${{ github.workspace }}/ci-artifacts
|
||||||
run: |
|
run: |
|
||||||
|
git checkout --orphan process-replay
|
||||||
|
git rm -rf .
|
||||||
git config user.name "GitHub Actions Bot"
|
git config user.name "GitHub Actions Bot"
|
||||||
git config user.email "<>"
|
git config user.email "<>"
|
||||||
git fetch origin process-replay || true
|
|
||||||
git checkout process-replay 2>/dev/null || git checkout --orphan process-replay
|
|
||||||
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
|
cp ${{ github.workspace }}/selfdrive/test/process_replay/fakedata/*.zst .
|
||||||
echo "${{ github.sha }}" > ref_commit
|
echo "${{ github.sha }}" > ref_commit
|
||||||
git add .
|
git add .
|
||||||
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}" || echo "No changes to commit"
|
git commit -m "process-replay refs for ${{ github.repository }}@${{ github.sha }}"
|
||||||
git push origin process-replay
|
git push origin process-replay --force
|
||||||
- name: Run regen
|
- name: Run regen
|
||||||
if: false
|
if: false
|
||||||
timeout-minutes: 4
|
timeout-minutes: 4
|
||||||
env:
|
run: |
|
||||||
ONNXCPU: 1
|
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
|
||||||
run: $PYTEST selfdrive/test/process_replay/test_regen.py
|
chmod -R 777 /tmp/comma_download_cache"
|
||||||
|
|
||||||
simulator_driving:
|
simulator_driving:
|
||||||
name: simulator driving
|
name: simulator driving
|
||||||
@@ -195,44 +268,73 @@ jobs:
|
|||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
if: false # FIXME: Started to timeout recently
|
if: false # FIXME: Started to timeout recently
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
id: setup-step
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc)
|
|
||||||
- name: Driving test
|
|
||||||
timeout-minutes: 2
|
|
||||||
run: |
|
run: |
|
||||||
source selfdrive/test/setup_xvfb.sh
|
${{ env.RUN }} "scons -j$(nproc)"
|
||||||
pytest -s tools/sim/tests/test_metadrive_bridge.py
|
- name: Driving test
|
||||||
|
timeout-minutes: ${{ (steps.setup-step.outputs.duration < 18) && 1 || 2 }}
|
||||||
|
run: |
|
||||||
|
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
|
||||||
|
source selfdrive/test/setup_vsound.sh && \
|
||||||
|
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
|
||||||
|
|
||||||
create_ui_report:
|
create_raylib_ui_report:
|
||||||
name: Create UI Report
|
name: Create raylib UI Report
|
||||||
runs-on: ${{
|
runs-on: ${{
|
||||||
(github.repository == 'commaai/openpilot') &&
|
(github.repository == 'commaai/openpilot') &&
|
||||||
((github.event_name != 'pull_request') ||
|
((github.event_name != 'pull_request') ||
|
||||||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
&& fromJSON('["namespace-profile-amd64-8x16"]')
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|| fromJSON('["ubuntu-24.04"]') }}
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- run: ./tools/op.sh setup
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
- name: Build openpilot
|
- name: Build openpilot
|
||||||
run: scons -j$(nproc)
|
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||||
- name: Create UI Report
|
- name: Create raylib UI Report
|
||||||
run: |
|
run: >
|
||||||
source selfdrive/test/setup_xvfb.sh
|
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||||
python3 selfdrive/ui/tests/diff/replay.py
|
source selfdrive/test/setup_xvfb.sh &&
|
||||||
python3 selfdrive/ui/tests/diff/replay.py --big
|
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
|
||||||
- name: Upload UI Report
|
- name: Upload Raylib UI Report
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ui-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
|
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
|
||||||
|
|
||||||
|
create_mici_raylib_ui_report:
|
||||||
|
name: Create mici raylib UI Report
|
||||||
|
runs-on: ${{
|
||||||
|
(github.repository == 'commaai/openpilot') &&
|
||||||
|
((github.event_name != 'pull_request') ||
|
||||||
|
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
|
||||||
|
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|
||||||
|
|| fromJSON('["ubuntu-24.04"]') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- uses: ./.github/workflows/setup-with-retry
|
||||||
|
- name: Build openpilot
|
||||||
|
run: ${{ env.RUN }} "scons -j$(nproc)"
|
||||||
|
- name: Create mici raylib UI Report
|
||||||
|
run: >
|
||||||
|
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
|
||||||
|
source selfdrive/test/setup_xvfb.sh &&
|
||||||
|
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
|
||||||
|
- name: Upload Raylib UI Report
|
||||||
|
uses: actions/upload-artifact@v6
|
||||||
|
with:
|
||||||
|
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
||||||
path: selfdrive/ui/tests/diff/report
|
path: selfdrive/ui/tests/diff/report
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
name: "ui preview"
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request_target:
|
|
||||||
types: [assigned, opened, synchronize, reopened, edited]
|
|
||||||
branches:
|
|
||||||
- 'master'
|
|
||||||
paths:
|
|
||||||
- 'selfdrive/assets/**'
|
|
||||||
- 'selfdrive/ui/**'
|
|
||||||
- 'system/ui/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
UI_JOB_NAME: "Create UI Report"
|
|
||||||
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
|
|
||||||
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
|
|
||||||
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-ui-preview"
|
|
||||||
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
|
|
||||||
|
|
||||||
# variant:video_prefix:master_branch
|
|
||||||
VARIANTS: "mici:mici_ui_replay:openpilot_master_ui_mici_raylib big:tizi_ui_replay:openpilot_master_ui_big_raylib"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
preview:
|
|
||||||
if: github.repository == 'sunnypilot/sunnypilot'
|
|
||||||
name: preview
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 20
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
actions: read
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Waiting for ui generation to end
|
|
||||||
uses: lewagon/wait-on-check-action@v1.3.4
|
|
||||||
with:
|
|
||||||
ref: ${{ env.SHA }}
|
|
||||||
check-name: ${{ env.UI_JOB_NAME }}
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
allowed-conclusions: success
|
|
||||||
wait-interval: 20
|
|
||||||
|
|
||||||
- name: Getting workflow run ID
|
|
||||||
id: get_run_id
|
|
||||||
run: |
|
|
||||||
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Getting proposed ui
|
|
||||||
uses: dawidd6/action-download-artifact@v6
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run_id: ${{ steps.get_run_id.outputs.run_id }}
|
|
||||||
search_artifacts: true
|
|
||||||
name: ui-report-1-${{ env.REPORT_NAME }}
|
|
||||||
path: ${{ github.workspace }}/pr_ui
|
|
||||||
|
|
||||||
- name: Getting mici master ui
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
repository: sunnypilot/ci-artifacts
|
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
|
||||||
path: ${{ github.workspace }}/master_mici
|
|
||||||
ref: openpilot_master_ui_mici_raylib
|
|
||||||
|
|
||||||
- name: Getting big master ui
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
repository: sunnypilot/ci-artifacts
|
|
||||||
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
|
|
||||||
path: ${{ github.workspace }}/master_big
|
|
||||||
ref: openpilot_master_ui_big_raylib
|
|
||||||
|
|
||||||
- name: Saving new master ui
|
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
|
||||||
run: |
|
|
||||||
for variant in $VARIANTS; do
|
|
||||||
IFS=':' read -r name video branch <<< "$variant"
|
|
||||||
master_dir="${{ github.workspace }}/master_${name}"
|
|
||||||
cd "$master_dir"
|
|
||||||
git checkout --orphan=new_branch
|
|
||||||
git rm -rf *
|
|
||||||
git branch -D "$branch"
|
|
||||||
git branch -m "$branch"
|
|
||||||
git config user.name "GitHub Actions Bot"
|
|
||||||
git config user.email "<>"
|
|
||||||
cp "${{ github.workspace }}/pr_ui/${video}.mp4" .
|
|
||||||
git add .
|
|
||||||
git commit -m "${name} video for commit ${{ env.SHA }}"
|
|
||||||
git push origin "$branch" --force
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Setup FFmpeg
|
|
||||||
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
|
|
||||||
|
|
||||||
- name: Finding diffs
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
id: find_diff
|
|
||||||
run: |
|
|
||||||
export PYTHONPATH=${{ github.workspace }}
|
|
||||||
baseurl="https://github.com/sunnypilot/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
|
|
||||||
|
|
||||||
COMMENT=""
|
|
||||||
for variant in $VARIANTS; do
|
|
||||||
IFS=':' read -r name video _ <<< "$variant"
|
|
||||||
diff_name="${name}_diff"
|
|
||||||
|
|
||||||
mv "${{ github.workspace }}/pr_ui/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_proposed.mp4"
|
|
||||||
cp "${{ github.workspace }}/master_${name}/${video}.mp4" "${{ github.workspace }}/pr_ui/${video}_master.mp4"
|
|
||||||
|
|
||||||
diff_exit_code=0
|
|
||||||
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py \
|
|
||||||
"${{ github.workspace }}/pr_ui/${video}_master.mp4" \
|
|
||||||
"${{ github.workspace }}/pr_ui/${video}_proposed.mp4" \
|
|
||||||
"${diff_name}.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
|
|
||||||
|
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${{ github.workspace }}/pr_ui/"
|
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.mp4" "${{ github.workspace }}/pr_ui/"
|
|
||||||
|
|
||||||
REPORT_URL="https://sunnypilot.github.io/ci-artifacts/${diff_name}_pr_${{ github.event.number }}.html"
|
|
||||||
if [ $diff_exit_code -eq 0 ]; then
|
|
||||||
COMMENT+="**${name}**: Videos are identical! [View Diff Report]($REPORT_URL)"$'\n'
|
|
||||||
else
|
|
||||||
COMMENT+="**${name}**: ⚠️ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"$'\n'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "COMMENT<<EOF"
|
|
||||||
echo "$COMMENT"
|
|
||||||
echo "EOF"
|
|
||||||
} >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Saving proposed ui
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
working-directory: ${{ github.workspace }}/master_mici
|
|
||||||
run: |
|
|
||||||
git config user.name "GitHub Actions Bot"
|
|
||||||
git config user.email "<>"
|
|
||||||
git checkout --orphan=${{ env.BRANCH_NAME }}
|
|
||||||
git rm -rf *
|
|
||||||
mv ${{ github.workspace }}/pr_ui/* .
|
|
||||||
git add .
|
|
||||||
git commit -m "ui videos for PR #${{ github.event.number }}"
|
|
||||||
git push origin ${{ env.BRANCH_NAME }} --force
|
|
||||||
|
|
||||||
# Append diff reports to report files branch
|
|
||||||
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
for variant in $VARIANTS; do
|
|
||||||
IFS=':' read -r name _ _ <<< "$variant"
|
|
||||||
diff_name="${name}_diff"
|
|
||||||
cp "${{ github.workspace }}/selfdrive/ui/tests/diff/report/${diff_name}.html" "${diff_name}_pr_${{ github.event.number }}.html"
|
|
||||||
git add "${diff_name}_pr_${{ github.event.number }}.html"
|
|
||||||
done
|
|
||||||
git commit -m "ui diff reports for PR #${{ github.event.number }}" || echo "No changes to commit"
|
|
||||||
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
|
|
||||||
|
|
||||||
- name: Comment on PR
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
uses: thollander/actions-comment-pull-request@v2
|
|
||||||
with:
|
|
||||||
message: |
|
|
||||||
<!-- _(run_id_ui_preview **${{ github.run_id }}**)_ -->
|
|
||||||
## UI Preview
|
|
||||||
${{ steps.find_diff.outputs.COMMENT }}
|
|
||||||
comment_tag: run_id_ui_preview
|
|
||||||
pr_number: ${{ github.event.number }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
name: vendor third_party
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.ref != 'refs/heads/master'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-24.04, macos-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: Build
|
||||||
|
run: third_party/build.sh
|
||||||
|
- name: Package artifacts
|
||||||
|
run: |
|
||||||
|
git add -A third_party/
|
||||||
|
git diff --cached --name-only -- third_party/ | tar -cf /tmp/third_party_build.tar -T -
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: third-party-${{ runner.os }}
|
||||||
|
path: /tmp/third_party_build.tar
|
||||||
|
|
||||||
|
commit:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/artifacts
|
||||||
|
- name: Commit vendored libraries
|
||||||
|
run: |
|
||||||
|
for f in /tmp/artifacts/*/third_party_build.tar; do
|
||||||
|
tar xf "$f"
|
||||||
|
done
|
||||||
|
git add third_party/
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git commit -m "third_party: rebuild vendor libraries"
|
||||||
|
git push
|
||||||
+3
-1
@@ -64,7 +64,9 @@ flycheck_*
|
|||||||
cppcheck_report.txt
|
cppcheck_report.txt
|
||||||
comma*.sh
|
comma*.sh
|
||||||
|
|
||||||
selfdrive/modeld/models/*.pkl*
|
selfdrive/modeld/models/*.pkl
|
||||||
|
sunnypilot/modeld*/thneed/compile
|
||||||
|
sunnypilot/modeld*/models/*.thneed
|
||||||
sunnypilot/modeld*/models/*.pkl
|
sunnypilot/modeld*/models/*.pkl
|
||||||
|
|
||||||
# openpilot log files
|
# openpilot log files
|
||||||
|
|||||||
+6
-30
@@ -1,38 +1,14 @@
|
|||||||
FROM ubuntu:24.04
|
FROM ghcr.io/commaai/openpilot-base:latest
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV OPENPILOT_PATH=/home/batman/openpilot
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends sudo tzdata locales && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
|
||||||
ENV LANG=en_US.UTF-8
|
|
||||||
ENV LANGUAGE=en_US:en
|
|
||||||
ENV LC_ALL=en_US.UTF-8
|
|
||||||
|
|
||||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
|
||||||
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
|
||||||
|
|
||||||
ARG USER=batman
|
|
||||||
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
|
|
||||||
USER $USER
|
|
||||||
|
|
||||||
ENV OPENPILOT_PATH=/home/$USER/openpilot
|
|
||||||
RUN mkdir -p ${OPENPILOT_PATH}
|
RUN mkdir -p ${OPENPILOT_PATH}
|
||||||
WORKDIR ${OPENPILOT_PATH}
|
WORKDIR ${OPENPILOT_PATH}
|
||||||
|
|
||||||
COPY --chown=$USER . ${OPENPILOT_PATH}/
|
COPY . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
ENV UV_BIN="/home/$USER/.local/bin/"
|
ENV UV_BIN="/home/batman/.local/bin/"
|
||||||
ENV VIRTUAL_ENV=${OPENPILOT_PATH}/.venv
|
ENV PATH="$UV_BIN:$PATH"
|
||||||
ENV PATH="$UV_BIN:$VIRTUAL_ENV/bin:$PATH"
|
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
|
||||||
RUN tools/setup_dependencies.sh && \
|
|
||||||
sudo rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
USER root
|
|
||||||
RUN git config --global --add safe.directory '*'
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends sudo tzdata locales ssh pulseaudio xvfb x11-xserver-utils gnome-screenshot python3-tk python3-dev && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
ENV LANGUAGE=en_US:en
|
||||||
|
ENV LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
|
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
|
||||||
|
RUN /tmp/tools/install_ubuntu_dependencies.sh && \
|
||||||
|
rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||||
|
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
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
apt-utils \
|
||||||
|
alien \
|
||||||
|
unzip \
|
||||||
|
tar \
|
||||||
|
curl \
|
||||||
|
xz-utils \
|
||||||
|
dbus \
|
||||||
|
gcc-arm-none-eabi \
|
||||||
|
tmux \
|
||||||
|
vim \
|
||||||
|
libx11-6 \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
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/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
|
||||||
|
|
||||||
|
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||||
|
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
||||||
|
ENV QTWEBENGINE_DISABLE_SANDBOX=1
|
||||||
|
|
||||||
|
RUN dbus-uuidgen > /etc/machine-id
|
||||||
|
RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji
|
||||||
|
|
||||||
|
ARG USER=batman
|
||||||
|
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
|
||||||
|
USER $USER
|
||||||
|
|
||||||
|
COPY --chown=$USER pyproject.toml uv.lock /home/$USER
|
||||||
|
COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/
|
||||||
|
|
||||||
|
ENV VIRTUAL_ENV=/home/$USER/.venv
|
||||||
|
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
|
RUN cd /home/$USER && \
|
||||||
|
tools/install_python_dependencies.sh && \
|
||||||
|
rm -rf tools/ pyproject.toml uv.lock .cache
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN sudo git config --global --add safe.directory /tmp/openpilot
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM ghcr.io/sunnypilot/sunnypilot-base:latest
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
ENV OPENPILOT_PATH=/home/batman/openpilot
|
||||||
|
|
||||||
|
RUN mkdir -p ${OPENPILOT_PATH}
|
||||||
|
WORKDIR ${OPENPILOT_PATH}
|
||||||
|
|
||||||
|
COPY . ${OPENPILOT_PATH}/
|
||||||
|
|
||||||
|
ENV UV_BIN="/home/batman/.local/bin/"
|
||||||
|
ENV PATH="$UV_BIN:$PATH"
|
||||||
|
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends sudo tzdata locales ssh pulseaudio xvfb x11-xserver-utils gnome-screenshot python3-tk python3-dev && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||||
|
ENV LANG=en_US.UTF-8
|
||||||
|
ENV LANGUAGE=en_US:en
|
||||||
|
ENV LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
|
COPY tools/install_ubuntu_dependencies.sh /tmp/tools/
|
||||||
|
RUN /tmp/tools/install_ubuntu_dependencies.sh && \
|
||||||
|
rm -rf /var/lib/apt/lists/* /tmp/* && \
|
||||||
|
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
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
apt-utils \
|
||||||
|
alien \
|
||||||
|
unzip \
|
||||||
|
tar \
|
||||||
|
curl \
|
||||||
|
xz-utils \
|
||||||
|
dbus \
|
||||||
|
gcc-arm-none-eabi \
|
||||||
|
tmux \
|
||||||
|
vim \
|
||||||
|
libx11-6 \
|
||||||
|
wget \
|
||||||
|
rsync \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
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/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
|
||||||
|
|
||||||
|
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||||
|
ENV NVIDIA_DRIVER_CAPABILITIES=graphics,utility,compute
|
||||||
|
ENV QTWEBENGINE_DISABLE_SANDBOX=1
|
||||||
|
|
||||||
|
RUN dbus-uuidgen > /etc/machine-id
|
||||||
|
RUN apt-get update && apt-get install -y fonts-noto-cjk fonts-noto-color-emoji
|
||||||
|
|
||||||
|
ARG USER=batman
|
||||||
|
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
|
||||||
|
USER $USER
|
||||||
|
|
||||||
|
COPY --chown=$USER pyproject.toml uv.lock /home/$USER
|
||||||
|
COPY --chown=$USER tools/install_python_dependencies.sh /home/$USER/tools/
|
||||||
|
|
||||||
|
ENV VIRTUAL_ENV=/home/$USER/.venv
|
||||||
|
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
|
RUN cd /home/$USER && \
|
||||||
|
tools/install_python_dependencies.sh && \
|
||||||
|
rm -rf tools/ pyproject.toml uv.lock .cache
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN sudo git config --global --add safe.directory /tmp/openpilot
|
||||||
Vendored
+14
-5
@@ -210,23 +210,30 @@ node {
|
|||||||
'HW + Unit Tests': {
|
'HW + Unit Tests': {
|
||||||
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
deviceStage("tizi-hardware", "tizi-common", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
|
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
||||||
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
|
step("test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"),
|
||||||
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
|
step("test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py", [diffPaths: ["system/loggerd/"]]),
|
||||||
step("test manager", "pytest system/manager/test/test_manager.py"),
|
step("test manager", "pytest system/manager/test/test_manager.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
'loopback': {
|
||||||
|
deviceStage("loopback", "tizi-loopback", ["UNSAFE=1"], [
|
||||||
|
step("build openpilot", "cd system/manager && ./build.py"),
|
||||||
|
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||||
|
])
|
||||||
|
},
|
||||||
'camerad OX03C10': {
|
'camerad OX03C10': {
|
||||||
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
|
deviceStage("OX03C10", "tizi-ox03c10", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'camerad OS04C10': {
|
'camerad OS04C10': {
|
||||||
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
||||||
step("build", "cd system/manager && ./build.py"),
|
step("build", "cd system/manager && ./build.py"),
|
||||||
step("test pandad", "pytest selfdrive/pandad/tests/test_pandad.py", [diffPaths: ["panda", "selfdrive/pandad/"]]),
|
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 60]),
|
||||||
step("test camerad", "pytest system/camerad/test/test_camerad.py", [timeout: 90]),
|
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
'sensord': {
|
'sensord': {
|
||||||
@@ -244,9 +251,11 @@ node {
|
|||||||
'tizi': {
|
'tizi': {
|
||||||
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
deviceStage("tizi", "tizi", ["UNSAFE=1"], [
|
||||||
step("build openpilot", "cd system/manager && ./build.py"),
|
step("build openpilot", "cd system/manager && ./build.py"),
|
||||||
step("test pandad loopback", "pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
step("test pandad loopback", "SINGLE_PANDA=1 pytest selfdrive/pandad/tests/test_pandad_loopback.py"),
|
||||||
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
|
step("test pandad spi", "pytest selfdrive/pandad/tests/test_pandad_spi.py"),
|
||||||
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
step("test amp", "pytest system/hardware/tici/tests/test_amplifier.py"),
|
||||||
|
// TODO: enable once new AGNOS is available
|
||||||
|
// step("test esim", "pytest system/hardware/tici/tests/test_esim.py"),
|
||||||
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
step("test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py", [diffPaths: ["system/qcomgpsd/"]]),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
Version 0.10.4 (2026-02-17)
|
Version 0.10.4 (2026-02-17)
|
||||||
========================
|
========================
|
||||||
* Kia K7 2017 support thanks to royjr!
|
|
||||||
* Lexus LS 2018 support thanks to Hacheoy!
|
* Lexus LS 2018 support thanks to Hacheoy!
|
||||||
* Reduce comma four standby power usage by 77% to 52 mW
|
|
||||||
|
|
||||||
Version 0.10.3 (2025-12-17)
|
Version 0.10.3 (2025-12-17)
|
||||||
========================
|
========================
|
||||||
|
|||||||
+20
-44
@@ -18,7 +18,6 @@ AddOption('--asan', action='store_true', help='turn on ASAN')
|
|||||||
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
AddOption('--ubsan', action='store_true', help='turn on UBSan')
|
||||||
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
|
||||||
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
|
||||||
AddOption('--verbose', action='store_true', default=False, help='show full build commands')
|
|
||||||
AddOption('--minimal',
|
AddOption('--minimal',
|
||||||
action='store_false',
|
action='store_false',
|
||||||
dest='extras',
|
dest='extras',
|
||||||
@@ -29,6 +28,7 @@ AddOption('--minimal',
|
|||||||
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
arch = "Darwin"
|
arch = "Darwin"
|
||||||
|
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
|
||||||
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
elif arch == "aarch64" and os.path.isfile('/TICI'):
|
||||||
arch = "larch64"
|
arch = "larch64"
|
||||||
assert arch in [
|
assert arch in [
|
||||||
@@ -38,25 +38,6 @@ assert arch in [
|
|||||||
"Darwin", # macOS arm64 (x86 not supported)
|
"Darwin", # macOS arm64 (x86 not supported)
|
||||||
]
|
]
|
||||||
|
|
||||||
if arch != "larch64":
|
|
||||||
import bzip2
|
|
||||||
import capnproto
|
|
||||||
import eigen
|
|
||||||
import ffmpeg as ffmpeg_pkg
|
|
||||||
import libjpeg
|
|
||||||
import libyuv
|
|
||||||
import ncurses
|
|
||||||
import openssl3
|
|
||||||
import python3_dev
|
|
||||||
import zeromq
|
|
||||||
import zstd
|
|
||||||
pkgs = [bzip2, capnproto, eigen, ffmpeg_pkg, libjpeg, libyuv, ncurses, openssl3, zeromq, zstd]
|
|
||||||
py_include = python3_dev.INCLUDE_DIR
|
|
||||||
else:
|
|
||||||
# TODO: remove when AGNOS has our new vendor pkgs
|
|
||||||
pkgs = []
|
|
||||||
py_include = sysconfig.get_paths()['include']
|
|
||||||
|
|
||||||
env = Environment(
|
env = Environment(
|
||||||
ENV={
|
ENV={
|
||||||
"PATH": os.environ['PATH'],
|
"PATH": os.environ['PATH'],
|
||||||
@@ -65,13 +46,15 @@ env = Environment(
|
|||||||
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
|
||||||
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
|
||||||
},
|
},
|
||||||
|
CC='clang',
|
||||||
|
CXX='clang++',
|
||||||
CCFLAGS=[
|
CCFLAGS=[
|
||||||
"-g",
|
"-g",
|
||||||
"-fPIC",
|
"-fPIC",
|
||||||
"-O2",
|
"-O2",
|
||||||
"-Wunused",
|
"-Wunused",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-Wshadow" if arch in ("Darwin", "larch64") else "-Wshadow=local",
|
"-Wshadow",
|
||||||
"-Wno-unknown-warning-option",
|
"-Wno-unknown-warning-option",
|
||||||
"-Wno-inconsistent-missing-override",
|
"-Wno-inconsistent-missing-override",
|
||||||
"-Wno-c99-designator",
|
"-Wno-c99-designator",
|
||||||
@@ -90,7 +73,7 @@ env = Environment(
|
|||||||
"#third_party/acados/include/blasfeo/include",
|
"#third_party/acados/include/blasfeo/include",
|
||||||
"#third_party/acados/include/hpipm/include",
|
"#third_party/acados/include/hpipm/include",
|
||||||
"#third_party/catch2/include",
|
"#third_party/catch2/include",
|
||||||
[x.INCLUDE_DIR for x in pkgs],
|
"#third_party/libyuv/include",
|
||||||
],
|
],
|
||||||
LIBPATH=[
|
LIBPATH=[
|
||||||
"#common",
|
"#common",
|
||||||
@@ -98,8 +81,8 @@ env = Environment(
|
|||||||
"#third_party",
|
"#third_party",
|
||||||
"#selfdrive/pandad",
|
"#selfdrive/pandad",
|
||||||
"#rednose/helpers",
|
"#rednose/helpers",
|
||||||
|
f"#third_party/libyuv/{arch}/lib",
|
||||||
f"#third_party/acados/{arch}/lib",
|
f"#third_party/acados/{arch}/lib",
|
||||||
[x.LIB_DIR for x in pkgs],
|
|
||||||
],
|
],
|
||||||
RPATH=[],
|
RPATH=[],
|
||||||
CYTHONCFILESUFFIX=".cpp",
|
CYTHONCFILESUFFIX=".cpp",
|
||||||
@@ -111,8 +94,7 @@ env = Environment(
|
|||||||
|
|
||||||
# Arch-specific flags and paths
|
# Arch-specific flags and paths
|
||||||
if arch == "larch64":
|
if arch == "larch64":
|
||||||
env["CC"] = "clang"
|
env.Append(CPPPATH=["#third_party/opencl/include"])
|
||||||
env["CXX"] = "clang++"
|
|
||||||
env.Append(LIBPATH=[
|
env.Append(LIBPATH=[
|
||||||
"/usr/local/lib",
|
"/usr/local/lib",
|
||||||
"/system/vendor/lib64",
|
"/system/vendor/lib64",
|
||||||
@@ -123,10 +105,17 @@ if arch == "larch64":
|
|||||||
env.Append(CXXFLAGS=arch_flags)
|
env.Append(CXXFLAGS=arch_flags)
|
||||||
elif arch == "Darwin":
|
elif arch == "Darwin":
|
||||||
env.Append(LIBPATH=[
|
env.Append(LIBPATH=[
|
||||||
|
f"{brew_prefix}/lib",
|
||||||
|
f"{brew_prefix}/opt/openssl@3.0/lib",
|
||||||
|
f"{brew_prefix}/opt/llvm/lib/c++",
|
||||||
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
"/System/Library/Frameworks/OpenGL.framework/Libraries",
|
||||||
])
|
])
|
||||||
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
|
||||||
|
env.Append(CPPPATH=[
|
||||||
|
f"{brew_prefix}/include",
|
||||||
|
f"{brew_prefix}/opt/openssl@3.0/include",
|
||||||
|
])
|
||||||
else:
|
else:
|
||||||
env.Append(LIBPATH=[
|
env.Append(LIBPATH=[
|
||||||
"/usr/lib",
|
"/usr/lib",
|
||||||
@@ -149,22 +138,6 @@ if _extra_cc:
|
|||||||
if arch != "Darwin":
|
if arch != "Darwin":
|
||||||
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
|
||||||
|
|
||||||
# Shorter build output: show brief descriptions instead of full commands.
|
|
||||||
# Full command lines are still printed on failure by scons.
|
|
||||||
if not GetOption('verbose'):
|
|
||||||
for action, short in (
|
|
||||||
("CC", "CC"),
|
|
||||||
("CXX", "CXX"),
|
|
||||||
("LINK", "LINK"),
|
|
||||||
("SHCC", "CC"),
|
|
||||||
("SHCXX", "CXX"),
|
|
||||||
("SHLINK", "LINK"),
|
|
||||||
("AR", "AR"),
|
|
||||||
("RANLIB", "RANLIB"),
|
|
||||||
("AS", "AS"),
|
|
||||||
):
|
|
||||||
env[f"{action}COMSTR"] = f" [{short}] $TARGET"
|
|
||||||
|
|
||||||
# progress output
|
# progress output
|
||||||
node_interval = 5
|
node_interval = 5
|
||||||
node_count = 0
|
node_count = 0
|
||||||
@@ -176,9 +149,10 @@ if os.environ.get('SCONS_PROGRESS'):
|
|||||||
Progress(progress_function, interval=node_interval)
|
Progress(progress_function, interval=node_interval)
|
||||||
|
|
||||||
# ********** Cython build environment **********
|
# ********** Cython build environment **********
|
||||||
|
py_include = sysconfig.get_paths()['include']
|
||||||
envCython = env.Clone()
|
envCython = env.Clone()
|
||||||
envCython["CPPPATH"] += [py_include, np.get_include()]
|
envCython["CPPPATH"] += [py_include, np.get_include()]
|
||||||
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-cpp", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
envCython["CCFLAGS"] += ["-Wno-#warnings", "-Wno-shadow", "-Wno-deprecated-declarations"]
|
||||||
envCython["CCFLAGS"].remove("-Werror")
|
envCython["CCFLAGS"].remove("-Werror")
|
||||||
|
|
||||||
envCython["LIBS"] = []
|
envCython["LIBS"] = []
|
||||||
@@ -241,8 +215,10 @@ SConscript(['selfdrive/SConscript'])
|
|||||||
|
|
||||||
SConscript(['sunnypilot/SConscript'])
|
SConscript(['sunnypilot/SConscript'])
|
||||||
|
|
||||||
if Dir('#tools/cabana/').exists() and arch != "larch64":
|
if Dir('#tools/cabana/').exists() and GetOption('extras'):
|
||||||
SConscript(['tools/cabana/SConscript'])
|
SConscript(['tools/replay/SConscript'])
|
||||||
|
if arch != "larch64":
|
||||||
|
SConscript(['tools/cabana/SConscript'])
|
||||||
|
|
||||||
|
|
||||||
env.CompilationDatabase('compile_commands.json')
|
env.CompilationDatabase('compile_commands.json')
|
||||||
|
|||||||
+3
-5
@@ -499,8 +499,7 @@ struct DeviceState @0xa4d8b5af2aa492eb {
|
|||||||
pmicTempC @39 :List(Float32);
|
pmicTempC @39 :List(Float32);
|
||||||
intakeTempC @46 :Float32;
|
intakeTempC @46 :Float32;
|
||||||
exhaustTempC @47 :Float32;
|
exhaustTempC @47 :Float32;
|
||||||
gnssTempC @48 :Float32;
|
caseTempC @48 :Float32;
|
||||||
bottomSocTempC @50 :Float32;
|
|
||||||
maxTempC @44 :Float32; # max of other temps, used to control fan
|
maxTempC @44 :Float32; # max of other temps, used to control fan
|
||||||
thermalZones @38 :List(ThermalZone);
|
thermalZones @38 :List(ThermalZone);
|
||||||
thermalStatus @14 :ThermalStatus;
|
thermalStatus @14 :ThermalStatus;
|
||||||
@@ -593,7 +592,6 @@ struct PandaState @0xa7649e2575e4591e {
|
|||||||
harnessStatus @21 :HarnessStatus;
|
harnessStatus @21 :HarnessStatus;
|
||||||
sbu1Voltage @35 :Float32;
|
sbu1Voltage @35 :Float32;
|
||||||
sbu2Voltage @36 :Float32;
|
sbu2Voltage @36 :Float32;
|
||||||
soundOutputLevel @37 :UInt16;
|
|
||||||
|
|
||||||
# can health
|
# can health
|
||||||
canState0 @29 :PandaCanState;
|
canState0 @29 :PandaCanState;
|
||||||
@@ -2234,9 +2232,9 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
|||||||
isActiveMode @16 :Bool;
|
isActiveMode @16 :Bool;
|
||||||
isRHD @4 :Bool;
|
isRHD @4 :Bool;
|
||||||
uncertainCount @19 :UInt32;
|
uncertainCount @19 :UInt32;
|
||||||
|
phoneProbOffset @20 :Float32;
|
||||||
|
phoneProbValidCount @21 :UInt32;
|
||||||
|
|
||||||
phoneProbOffsetDEPRECATED @20 :Float32;
|
|
||||||
phoneProbValidCountDEPRECATED @21 :UInt32;
|
|
||||||
isPreviewDEPRECATED @15 :Bool;
|
isPreviewDEPRECATED @15 :Bool;
|
||||||
rhdCheckedDEPRECATED @5 :Bool;
|
rhdCheckedDEPRECATED @5 :Bool;
|
||||||
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import numbers
|
|||||||
import random
|
import random
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cereal import log, car
|
from cereal import log, car
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
import cereal.services as services
|
import cereal.services as services
|
||||||
from cereal.services import SERVICE_LIST
|
from cereal.services import SERVICE_LIST
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ common_libs = [
|
|||||||
'swaglog.cc',
|
'swaglog.cc',
|
||||||
'util.cc',
|
'util.cc',
|
||||||
'ratekeeper.cc',
|
'ratekeeper.cc',
|
||||||
|
'clutil.cc',
|
||||||
]
|
]
|
||||||
|
|
||||||
_common = env.Library('common', common_libs, LIBS="json11")
|
_common = env.Library('common', common_libs, LIBS="json11")
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "common/clutil.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/swaglog.h"
|
||||||
|
|
||||||
|
namespace { // helper functions
|
||||||
|
|
||||||
|
template <typename Func, typename Id, typename Name>
|
||||||
|
std::string get_info(Func get_info_func, Id id, Name param_name) {
|
||||||
|
size_t size = 0;
|
||||||
|
CL_CHECK(get_info_func(id, param_name, 0, NULL, &size));
|
||||||
|
std::string info(size, '\0');
|
||||||
|
CL_CHECK(get_info_func(id, param_name, size, info.data(), NULL));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
inline std::string get_platform_info(cl_platform_id id, cl_platform_info name) { return get_info(&clGetPlatformInfo, id, name); }
|
||||||
|
inline std::string get_device_info(cl_device_id id, cl_device_info name) { return get_info(&clGetDeviceInfo, id, name); }
|
||||||
|
|
||||||
|
void cl_print_info(cl_platform_id platform, cl_device_id device) {
|
||||||
|
size_t work_group_size = 0;
|
||||||
|
cl_device_type device_type = 0;
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(work_group_size), &work_group_size, NULL);
|
||||||
|
clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(device_type), &device_type, NULL);
|
||||||
|
const char *type_str = "Other...";
|
||||||
|
switch (device_type) {
|
||||||
|
case CL_DEVICE_TYPE_CPU: type_str ="CL_DEVICE_TYPE_CPU"; break;
|
||||||
|
case CL_DEVICE_TYPE_GPU: type_str = "CL_DEVICE_TYPE_GPU"; break;
|
||||||
|
case CL_DEVICE_TYPE_ACCELERATOR: type_str = "CL_DEVICE_TYPE_ACCELERATOR"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("vendor: %s", get_platform_info(platform, CL_PLATFORM_VENDOR).c_str());
|
||||||
|
LOGD("platform version: %s", get_platform_info(platform, CL_PLATFORM_VERSION).c_str());
|
||||||
|
LOGD("profile: %s", get_platform_info(platform, CL_PLATFORM_PROFILE).c_str());
|
||||||
|
LOGD("extensions: %s", get_platform_info(platform, CL_PLATFORM_EXTENSIONS).c_str());
|
||||||
|
LOGD("name: %s", get_device_info(device, CL_DEVICE_NAME).c_str());
|
||||||
|
LOGD("device version: %s", get_device_info(device, CL_DEVICE_VERSION).c_str());
|
||||||
|
LOGD("max work group size: %zu", work_group_size);
|
||||||
|
LOGD("type = %d, %s", (int)device_type, type_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cl_print_build_errors(cl_program program, cl_device_id device) {
|
||||||
|
cl_build_status status;
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_STATUS, sizeof(status), &status, NULL);
|
||||||
|
size_t log_size;
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
|
||||||
|
std::string log(log_size, '\0');
|
||||||
|
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size, &log[0], NULL);
|
||||||
|
|
||||||
|
LOGE("build failed; status=%d, log: %s", status, log.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
cl_device_id cl_get_device_id(cl_device_type device_type) {
|
||||||
|
cl_uint num_platforms = 0;
|
||||||
|
CL_CHECK(clGetPlatformIDs(0, NULL, &num_platforms));
|
||||||
|
std::unique_ptr<cl_platform_id[]> platform_ids = std::make_unique<cl_platform_id[]>(num_platforms);
|
||||||
|
CL_CHECK(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_platforms; ++i) {
|
||||||
|
LOGD("platform[%zu] CL_PLATFORM_NAME: %s", i, get_platform_info(platform_ids[i], CL_PLATFORM_NAME).c_str());
|
||||||
|
|
||||||
|
// Get first device
|
||||||
|
if (cl_device_id device_id = NULL; clGetDeviceIDs(platform_ids[i], device_type, 1, &device_id, NULL) == 0 && device_id) {
|
||||||
|
cl_print_info(platform_ids[i], device_id);
|
||||||
|
return device_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGE("No valid openCL platform found");
|
||||||
|
assert(0);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_context cl_create_context(cl_device_id device_id) {
|
||||||
|
return CL_CHECK_ERR(clCreateContext(NULL, 1, &device_id, NULL, NULL, &err));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cl_release_context(cl_context context) {
|
||||||
|
clReleaseContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args) {
|
||||||
|
return cl_program_from_source(ctx, device_id, util::read_file(path), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args) {
|
||||||
|
const char *csrc = src.c_str();
|
||||||
|
cl_program prg = CL_CHECK_ERR(clCreateProgramWithSource(ctx, 1, &csrc, NULL, &err));
|
||||||
|
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
||||||
|
cl_print_build_errors(prg, device_id);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
return prg;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenCL/cl.h>
|
||||||
|
#else
|
||||||
|
#include <CL/cl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define CL_CHECK(_expr) \
|
||||||
|
do { \
|
||||||
|
assert(CL_SUCCESS == (_expr)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CL_CHECK_ERR(_expr) \
|
||||||
|
({ \
|
||||||
|
cl_int err = CL_INVALID_VALUE; \
|
||||||
|
__typeof__(_expr) _ret = _expr; \
|
||||||
|
assert(_ret&& err == CL_SUCCESS); \
|
||||||
|
_ret; \
|
||||||
|
})
|
||||||
|
|
||||||
|
cl_device_id cl_get_device_id(cl_device_type device_type);
|
||||||
|
cl_context cl_create_context(cl_device_id device_id);
|
||||||
|
void cl_release_context(cl_context context);
|
||||||
|
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
||||||
|
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import math
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
|
|
||||||
|
|
||||||
def get_chunk_name(name, idx, num_chunks):
|
|
||||||
return f"{name}.chunk{idx+1:02d}of{num_chunks:02d}"
|
|
||||||
|
|
||||||
def get_manifest_path(name):
|
|
||||||
return f"{name}.chunkmanifest"
|
|
||||||
|
|
||||||
def get_chunk_paths(path, file_size):
|
|
||||||
num_chunks = math.ceil(file_size / CHUNK_SIZE)
|
|
||||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
|
||||||
|
|
||||||
def chunk_file(path, targets):
|
|
||||||
manifest_path, *chunk_paths = targets
|
|
||||||
with open(path, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
actual_num_chunks = max(1, math.ceil(len(data) / CHUNK_SIZE))
|
|
||||||
assert len(chunk_paths) >= actual_num_chunks, f"Allowed {len(chunk_paths)} chunks but needs at least {actual_num_chunks}, for path {path}"
|
|
||||||
for i, chunk_path in enumerate(chunk_paths):
|
|
||||||
with open(chunk_path, 'wb') as f:
|
|
||||||
f.write(data[i * CHUNK_SIZE:(i + 1) * CHUNK_SIZE])
|
|
||||||
Path(manifest_path).write_text(str(len(chunk_paths)))
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
|
|
||||||
def read_file_chunked(path):
|
|
||||||
manifest_path = get_manifest_path(path)
|
|
||||||
if os.path.isfile(manifest_path):
|
|
||||||
num_chunks = int(Path(manifest_path).read_text().strip())
|
|
||||||
return b''.join(Path(get_chunk_name(path, i, num_chunks)).read_bytes() for i in range(num_chunks))
|
|
||||||
if os.path.isfile(path):
|
|
||||||
return Path(path).read_bytes()
|
|
||||||
raise FileNotFoundError(path)
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct vec3 {
|
||||||
|
float v[3];
|
||||||
|
} vec3;
|
||||||
|
|
||||||
|
typedef struct vec4 {
|
||||||
|
float v[4];
|
||||||
|
} vec4;
|
||||||
|
|
||||||
|
typedef struct mat3 {
|
||||||
|
float v[3*3];
|
||||||
|
} mat3;
|
||||||
|
|
||||||
|
typedef struct mat4 {
|
||||||
|
float v[4*4];
|
||||||
|
} mat4;
|
||||||
|
|
||||||
|
static inline mat3 matmul3(const mat3 &a, const mat3 &b) {
|
||||||
|
mat3 ret = {{0.0}};
|
||||||
|
for (int r=0; r<3; r++) {
|
||||||
|
for (int c=0; c<3; c++) {
|
||||||
|
float v = 0.0;
|
||||||
|
for (int k=0; k<3; k++) {
|
||||||
|
v += a.v[r*3+k] * b.v[k*3+c];
|
||||||
|
}
|
||||||
|
ret.v[r*3+c] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec3 matvecmul3(const mat3 &a, const vec3 &b) {
|
||||||
|
vec3 ret = {{0.0}};
|
||||||
|
for (int r=0; r<3; r++) {
|
||||||
|
for (int c=0; c<3; c++) {
|
||||||
|
ret.v[r] += a.v[r*3+c] * b.v[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline mat4 matmul(const mat4 &a, const mat4 &b) {
|
||||||
|
mat4 ret = {{0.0}};
|
||||||
|
for (int r=0; r<4; r++) {
|
||||||
|
for (int c=0; c<4; c++) {
|
||||||
|
float v = 0.0;
|
||||||
|
for (int k=0; k<4; k++) {
|
||||||
|
v += a.v[r*4+k] * b.v[k*4+c];
|
||||||
|
}
|
||||||
|
ret.v[r*4+c] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec4 matvecmul(const mat4 &a, const vec4 &b) {
|
||||||
|
vec4 ret = {{0.0}};
|
||||||
|
for (int r=0; r<4; r++) {
|
||||||
|
for (int c=0; c<4; c++) {
|
||||||
|
ret.v[r] += a.v[r*4+c] * b.v[c];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scales the input and output space of a transformation matrix
|
||||||
|
// that assumes pixel-center origin.
|
||||||
|
static inline mat3 transform_scale_buffer(const mat3 &in, float s) {
|
||||||
|
// in_pt = ( transform(out_pt/s + 0.5) - 0.5) * s
|
||||||
|
|
||||||
|
mat3 transform_out = {{
|
||||||
|
1.0f/s, 0.0f, 0.5f,
|
||||||
|
0.0f, 1.0f/s, 0.5f,
|
||||||
|
0.0f, 0.0f, 1.0f,
|
||||||
|
}};
|
||||||
|
|
||||||
|
mat3 transform_in = {{
|
||||||
|
s, 0.0f, -0.5f*s,
|
||||||
|
0.0f, s, -0.5f*s,
|
||||||
|
0.0f, 0.0f, 1.0f,
|
||||||
|
}};
|
||||||
|
|
||||||
|
return matmul3(transform_in, matmul3(in, transform_out));
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import sys
|
|
||||||
import pytest
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
|
|
||||||
class parameterized:
|
|
||||||
@staticmethod
|
|
||||||
def expand(cases):
|
|
||||||
cases = list(cases)
|
|
||||||
|
|
||||||
if not cases:
|
|
||||||
return lambda func: pytest.mark.skip("no parameterized cases")(func)
|
|
||||||
|
|
||||||
def decorator(func):
|
|
||||||
params = [p for p in inspect.signature(func).parameters if p != 'self']
|
|
||||||
normalized = [c if isinstance(c, tuple) else (c,) for c in cases]
|
|
||||||
# Infer arg count from first case so extra params (e.g. from @given) are left untouched
|
|
||||||
expand_params = params[: len(normalized[0])]
|
|
||||||
if len(expand_params) == 1:
|
|
||||||
return pytest.mark.parametrize(expand_params[0], [c[0] for c in normalized])(func)
|
|
||||||
return pytest.mark.parametrize(', '.join(expand_params), normalized)(func)
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def parameterized_class(attrs, input_list=None):
|
|
||||||
if isinstance(attrs, list) and (not attrs or isinstance(attrs[0], dict)):
|
|
||||||
params_list = attrs
|
|
||||||
else:
|
|
||||||
assert input_list is not None
|
|
||||||
attr_names = (attrs,) if isinstance(attrs, str) else tuple(attrs)
|
|
||||||
params_list = [dict(zip(attr_names, v if isinstance(v, (tuple, list)) else (v,), strict=False)) for v in input_list]
|
|
||||||
|
|
||||||
def decorator(cls):
|
|
||||||
globs = sys._getframe(1).f_globals
|
|
||||||
for i, params in enumerate(params_list):
|
|
||||||
name = f"{cls.__name__}_{i}"
|
|
||||||
new_cls = type(name, (cls,), dict(params))
|
|
||||||
new_cls.__module__ = cls.__module__
|
|
||||||
new_cls.__test__ = True # override inherited False so pytest collects this subclass
|
|
||||||
globs[name] = new_cls
|
|
||||||
# Don't collect the un-parametrised base, but return it so outer decorators
|
|
||||||
# (e.g. @pytest.mark.skip) land on it and propagate to subclasses via MRO.
|
|
||||||
cls.__test__ = False
|
|
||||||
return cls
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
@@ -170,7 +170,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
|
||||||
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
|
{"OnroadScreenOffBrightness", {PERSISTENT | BACKUP, INT, "0"}},
|
||||||
{"OnroadScreenOffBrightnessMigrated", {PERSISTENT | BACKUP, STRING, "0.0"}},
|
|
||||||
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
{"OnroadScreenOffTimer", {PERSISTENT | BACKUP, INT, "15"}},
|
||||||
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
{"OnroadUploads", {PERSISTENT | BACKUP, BOOL, "1"}},
|
||||||
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
@@ -191,7 +190,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
// Model Manager params
|
// Model Manager params
|
||||||
{"ModelManager_ActiveBundle", {PERSISTENT, JSON}},
|
{"ModelManager_ActiveBundle", {PERSISTENT, JSON}},
|
||||||
{"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}},
|
{"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||||
{"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT}},
|
{"ModelManager_DownloadIndex", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, INT, "0"}},
|
||||||
{"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}},
|
{"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}},
|
||||||
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
|
||||||
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
|
||||||
@@ -219,7 +218,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
|||||||
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"ToyotaStopAndGoHack", {PERSISTENT | BACKUP, BOOL, "0"}},
|
|
||||||
|
|
||||||
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"DynamicExperimentalControl", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
{"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}},
|
{"BlindSpot", {PERSISTENT | BACKUP, BOOL, "0"}},
|
||||||
|
|||||||
+4
-4
@@ -27,14 +27,14 @@ public:
|
|||||||
auto param_path = Params().getParamPath();
|
auto param_path = Params().getParamPath();
|
||||||
if (util::file_exists(param_path)) {
|
if (util::file_exists(param_path)) {
|
||||||
std::string real_path = util::readlink(param_path);
|
std::string real_path = util::readlink(param_path);
|
||||||
util::check_system(util::string_format("rm %s -rf", real_path.c_str()));
|
system(util::string_format("rm %s -rf", real_path.c_str()).c_str());
|
||||||
unlink(param_path.c_str());
|
unlink(param_path.c_str());
|
||||||
}
|
}
|
||||||
if (getenv("COMMA_CACHE") == nullptr) {
|
if (getenv("COMMA_CACHE") == nullptr) {
|
||||||
util::check_system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()));
|
system(util::string_format("rm %s -rf", Path::download_cache_root().c_str()).c_str());
|
||||||
}
|
}
|
||||||
util::check_system(util::string_format("rm %s -rf", Path::comma_home().c_str()));
|
system(util::string_format("rm %s -rf", Path::comma_home().c_str()).c_str());
|
||||||
util::check_system(util::string_format("rm %s -rf", msgq_path.c_str()));
|
system(util::string_format("rm %s -rf", msgq_path.c_str()).c_str());
|
||||||
unsetenv("OPENPILOT_PREFIX");
|
unsetenv("OPENPILOT_PREFIX");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
#include "common/timing.h"
|
#include "common/timing.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
|
||||||
RateKeeper::RateKeeper(const std::string &name_, float rate, float print_delay_threshold_)
|
RateKeeper::RateKeeper(const std::string &name, float rate, float print_delay_threshold)
|
||||||
: name(name_),
|
: name(name),
|
||||||
print_delay_threshold(std::max(0.f, print_delay_threshold_)) {
|
print_delay_threshold(std::max(0.f, print_delay_threshold)) {
|
||||||
interval = 1 / rate;
|
interval = 1 / rate;
|
||||||
last_monitor_time = seconds_since_boot();
|
last_monitor_time = seconds_since_boot();
|
||||||
next_frame_time = last_monitor_time + interval;
|
next_frame_time = last_monitor_time + interval;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ TEST_CASE("util::read_file") {
|
|||||||
REQUIRE(util::read_file(filename).empty());
|
REQUIRE(util::read_file(filename).empty());
|
||||||
|
|
||||||
std::string content = random_bytes(64 * 1024);
|
std::string content = random_bytes(64 * 1024);
|
||||||
REQUIRE(write(fd, content.c_str(), content.size()) == (ssize_t)content.size());
|
write(fd, content.c_str(), content.size());
|
||||||
std::string ret = util::read_file(filename);
|
std::string ret = util::read_file(filename);
|
||||||
bool equal = (ret == content);
|
bool equal = (ret == content);
|
||||||
REQUIRE(equal);
|
REQUIRE(equal);
|
||||||
@@ -114,12 +114,12 @@ TEST_CASE("util::safe_fwrite") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("util::create_directories") {
|
TEST_CASE("util::create_directories") {
|
||||||
REQUIRE(system("rm /tmp/test_create_directories -rf") == 0);
|
system("rm /tmp/test_create_directories -rf");
|
||||||
std::string dir = "/tmp/test_create_directories/a/b/c/d/e/f";
|
std::string dir = "/tmp/test_create_directories/a/b/c/d/e/f";
|
||||||
|
|
||||||
auto check_dir_permissions = [](const std::string &path, mode_t mode) -> bool {
|
auto check_dir_permissions = [](const std::string &dir, mode_t mode) -> bool {
|
||||||
struct stat st = {};
|
struct stat st = {};
|
||||||
return stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == mode;
|
return stat(dir.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("create_directories") {
|
SECTION("create_directories") {
|
||||||
@@ -132,7 +132,7 @@ TEST_CASE("util::create_directories") {
|
|||||||
}
|
}
|
||||||
SECTION("a file exists with the same name") {
|
SECTION("a file exists with the same name") {
|
||||||
REQUIRE(util::create_directories(dir, 0755));
|
REQUIRE(util::create_directories(dir, 0755));
|
||||||
int f = open((dir + "/file").c_str(), O_RDWR | O_CREAT, 0644);
|
int f = open((dir + "/file").c_str(), O_RDWR | O_CREAT);
|
||||||
REQUIRE(f != -1);
|
REQUIRE(f != -1);
|
||||||
close(f);
|
close(f);
|
||||||
REQUIRE(util::create_directories(dir + "/file", 0755) == false);
|
REQUIRE(util::create_directories(dir + "/file", 0755) == false);
|
||||||
|
|||||||
+4
-4
@@ -181,9 +181,9 @@ bool file_exists(const std::string& fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool createDirectory(std::string dir, mode_t mode) {
|
static bool createDirectory(std::string dir, mode_t mode) {
|
||||||
auto verify_dir = [](const std::string& path) -> bool {
|
auto verify_dir = [](const std::string& dir) -> bool {
|
||||||
struct stat st = {};
|
struct stat st = {};
|
||||||
return (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR);
|
return (stat(dir.c_str(), &st) == 0 && (st.st_mode & S_IFMT) == S_IFDIR);
|
||||||
};
|
};
|
||||||
// remove trailing /'s
|
// remove trailing /'s
|
||||||
while (dir.size() > 1 && dir.back() == '/') {
|
while (dir.size() > 1 && dir.back() == '/') {
|
||||||
@@ -288,7 +288,7 @@ std::string strip(const std::string &str) {
|
|||||||
std::string check_output(const std::string& command) {
|
std::string check_output(const std::string& command) {
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
std::string result;
|
std::string result;
|
||||||
std::unique_ptr<FILE, int(*)(FILE*)> pipe(popen(command.c_str(), "r"), pclose);
|
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
|
||||||
|
|
||||||
if (!pipe) {
|
if (!pipe) {
|
||||||
return "";
|
return "";
|
||||||
@@ -303,7 +303,7 @@ std::string check_output(const std::string& command) {
|
|||||||
|
|
||||||
bool system_time_valid() {
|
bool system_time_valid() {
|
||||||
// Default to August 26, 2024
|
// Default to August 26, 2024
|
||||||
tm min_tm = {.tm_mday = 26, .tm_mon = 7, .tm_year = 2024 - 1900};
|
tm min_tm = {.tm_year = 2024 - 1900, .tm_mon = 7, .tm_mday = 26};
|
||||||
time_t min_date = mktime(&min_tm);
|
time_t min_date = mktime(&min_tm);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|||||||
@@ -97,13 +97,6 @@ bool create_directories(const std::string &dir, mode_t mode);
|
|||||||
|
|
||||||
std::string check_output(const std::string& command);
|
std::string check_output(const std::string& command);
|
||||||
|
|
||||||
inline void check_system(const std::string& cmd) {
|
|
||||||
int ret = std::system(cmd.c_str());
|
|
||||||
if (ret != 0) {
|
|
||||||
fprintf(stderr, "system command failed (%d): %s\n", ret, cmd.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool system_time_valid();
|
bool system_time_valid();
|
||||||
|
|
||||||
inline void sleep_for(const int milliseconds) {
|
inline void sleep_for(const int milliseconds) {
|
||||||
|
|||||||
+2
-3
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||||
|
|
||||||
# 336 Supported Cars
|
# 335 Supported Cars
|
||||||
|
|
||||||
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br> |Video|Setup Video|
|
||||||
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||||
@@ -166,7 +166,6 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2022-23">Buy Here</a></sub></details>|||
|
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Forte 2022-23">Buy Here</a></sub></details>|||
|
||||||
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 2021-24">Buy Here</a></sub></details>|||
|
|Kia|K5 2021-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 2021-24">Buy Here</a></sub></details>|||
|
||||||
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K5 Hybrid 2020-22">Buy Here</a></sub></details>|||
|
||||||
|Kia|K7 2017|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K7 2017">Buy Here</a></sub></details>|||
|
|
||||||
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia K8 Hybrid (with HDA II) 2023">Buy Here</a></sub></details>|||
|
||||||
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Kia|Niro EV 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai H connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
|Kia|Niro EV 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai F connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Niro EV 2020">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=lT7zcG6ZpGo" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||||
@@ -346,7 +345,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
|||||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Volkswagen Touran 2016-23">Buy Here</a></sub></details>|||
|
||||||
|
|
||||||
### Footnotes
|
### Footnotes
|
||||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `nightly-dev`. <br />
|
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`. <br />
|
||||||
<sup>2</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
|
<sup>2</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
|
||||||
<sup>3</sup>See more setup details for <a href="https://github.com/commaai/openpilot/wiki/gm" target="_blank">GM</a>. <br />
|
<sup>3</sup>See more setup details for <a href="https://github.com/commaai/openpilot/wiki/gm" target="_blank">GM</a>. <br />
|
||||||
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ Development is coordinated through [Discord](https://discord.comma.ai) and GitHu
|
|||||||
## What contributions are we looking for?
|
## What contributions are we looking for?
|
||||||
|
|
||||||
**openpilot's priorities are [safety](SAFETY.md), stability, quality, and features, in that order.**
|
**openpilot's priorities are [safety](SAFETY.md), stability, quality, and features, in that order.**
|
||||||
openpilot is part of comma's mission to *solve self-driving cars while delivering shippable intermediaries*, and all development is towards that goal.
|
openpilot is part of comma's mission to *solve self-driving cars while delivering shippable intermediaries*, and all development is towards that goal.
|
||||||
|
|
||||||
### What gets merged?
|
### What gets merged?
|
||||||
|
|
||||||
The probability of a pull request being merged is a function of its value to the project and the effort it will take us to get it merged.
|
The probability of a pull request being merged is a function of its value to the project and the effort it will take us to get it merged.
|
||||||
If a PR offers *some* value but will take lots of time to get merged, it will be closed.
|
If a PR offers *some* value but will take lots of time to get merged, it will be closed.
|
||||||
Simple, well-tested bug fixes are the easiest to merge, and new features are the hardest to get merged.
|
Simple, well-tested bug fixes are the easiest to merge, and new features are the hardest to get merged.
|
||||||
|
|
||||||
All of these are examples of good PRs:
|
All of these are examples of good PRs:
|
||||||
* typo fix: https://github.com/commaai/openpilot/pull/30678
|
* typo fix: https://github.com/commaai/openpilot/pull/30678
|
||||||
@@ -29,17 +29,17 @@ All of these are examples of good PRs:
|
|||||||
|
|
||||||
### What doesn't get merged?
|
### What doesn't get merged?
|
||||||
|
|
||||||
* **style changes**: code is art, and it's up to the author to make it beautiful
|
* **style changes**: code is art, and it's up to the author to make it beautiful
|
||||||
* **500+ line PRs**: clean it up, break it up into smaller PRs, or both
|
* **500+ line PRs**: clean it up, break it up into smaller PRs, or both
|
||||||
* **PRs without a clear goal**: every PR must have a singular and clear goal
|
* **PRs without a clear goal**: every PR must have a singular and clear goal
|
||||||
* **UI design**: we do not have a good review process for this yet
|
* **UI design**: we do not have a good review process for this yet
|
||||||
* **New features**: We believe openpilot is mostly feature-complete, and the rest is a matter of refinement and fixing bugs. As a result of this, most feature PRs will be immediately closed, however the beauty of open source is that forks can and do offer features that upstream openpilot doesn't.
|
* **New features**: We believe openpilot is mostly feature-complete, and the rest is a matter of refinement and fixing bugs. As a result of this, most feature PRs will be immediately closed, however the beauty of open source is that forks can and do offer features that upstream openpilot doesn't.
|
||||||
* **Negative expected value**: This is a class of PRs that makes an improvement, but the risk or validation costs more than the improvement. The risk can be mitigated by first getting a failing test merged.
|
* **Negative expected value**: This a class of PRs that makes an improvement, but the risk or validation costs more than the improvement. The risk can be mitigated by first getting a failing test merged.
|
||||||
|
|
||||||
### First contribution
|
### First contribution
|
||||||
|
|
||||||
[Projects / openpilot bounties](https://github.com/orgs/commaai/projects/26/views/1?pane=info) is the best place to get started and goes in-depth on what's expected when working on a bounty.
|
[Projects / openpilot bounties](https://github.com/orgs/commaai/projects/26/views/1?pane=info) is the best place to get started and goes in-depth on what's expected when working on a bounty.
|
||||||
There are a lot of bounties that don't require a comma 3X or a car.
|
There's lot of bounties that don't require a comma 3X or a car.
|
||||||
|
|
||||||
## Pull Requests
|
## Pull Requests
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ NOTE: Those commands must be run in the root directory of openpilot, **not /docs
|
|||||||
|
|
||||||
**1. Install the docs dependencies**
|
**1. Install the docs dependencies**
|
||||||
``` bash
|
``` bash
|
||||||
uv pip install .[docs]
|
pip install .[docs]
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Build the new site**
|
**2. Build the new site**
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ Each car brand is supported by a standard interface structure in `opendbc/car/[b
|
|||||||
* `values.py`: Limits for actuation, general constants for cars, and supported car documentation
|
* `values.py`: Limits for actuation, general constants for cars, and supported car documentation
|
||||||
* `radar_interface.py`: Interface for parsing radar points from the car, if applicable
|
* `radar_interface.py`: Interface for parsing radar points from the car, if applicable
|
||||||
|
|
||||||
## safety
|
## panda
|
||||||
|
|
||||||
* `opendbc_repo/opendbc/safety/modes/[brand].h`: Brand-specific safety logic
|
* `board/safety/safety_[brand].h`: Brand-specific safety logic
|
||||||
* `opendbc_repo/opendbc/safety/tests/test_[brand].py`: Brand-specific safety CI tests
|
* `tests/safety/test_[brand].py`: Brand-specific safety CI tests
|
||||||
|
|
||||||
## openpilot
|
## openpilot
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule msgq_repo updated: ed2777747d...4c4e814ed5
+1
-1
Submodule opendbc_repo updated: 9918ec656f...a54fae3101
+1
-1
Submodule panda updated: f5f296c65c...a95e060e85
+18
-19
@@ -20,34 +20,26 @@ dependencies = [
|
|||||||
# core
|
# core
|
||||||
"cffi",
|
"cffi",
|
||||||
"scons",
|
"scons",
|
||||||
"pycapnp",
|
"pycapnp==2.1.0",
|
||||||
"Cython",
|
"Cython",
|
||||||
"setuptools",
|
"setuptools",
|
||||||
"numpy >=2.0",
|
"numpy >=2.0",
|
||||||
|
|
||||||
# vendored native dependencies
|
|
||||||
"bzip2 @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=bzip2",
|
|
||||||
"capnproto @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=capnproto",
|
|
||||||
"eigen @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=eigen",
|
|
||||||
"ffmpeg @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=ffmpeg",
|
|
||||||
"libjpeg @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=libjpeg",
|
|
||||||
"libyuv @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=libyuv",
|
|
||||||
"openssl3 @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=openssl3",
|
|
||||||
"python3-dev @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=python3-dev",
|
|
||||||
"zstd @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=zstd",
|
|
||||||
"ncurses @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=ncurses",
|
|
||||||
"zeromq @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=zeromq",
|
|
||||||
"git-lfs @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=git-lfs",
|
|
||||||
|
|
||||||
# body / webrtcd
|
# body / webrtcd
|
||||||
"av",
|
|
||||||
"aiohttp",
|
"aiohttp",
|
||||||
"aiortc",
|
"aiortc",
|
||||||
|
# aiortc does not put an upper bound on pyopenssl and is now incompatible
|
||||||
|
# with the latest release
|
||||||
|
"pyopenssl < 24.3.0",
|
||||||
|
"pyaudio",
|
||||||
|
|
||||||
# panda
|
# panda
|
||||||
"libusb1",
|
"libusb1",
|
||||||
"spidev; platform_system == 'Linux'",
|
"spidev; platform_system == 'Linux'",
|
||||||
|
|
||||||
|
# modeld
|
||||||
|
"onnx >= 1.14.0",
|
||||||
|
|
||||||
# logging
|
# logging
|
||||||
"pyzmq",
|
"pyzmq",
|
||||||
"sentry-sdk",
|
"sentry-sdk",
|
||||||
@@ -75,6 +67,7 @@ dependencies = [
|
|||||||
# ui
|
# ui
|
||||||
"raylib > 5.5.0.3",
|
"raylib > 5.5.0.3",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
|
"mapbox-earcut",
|
||||||
"jeepney",
|
"jeepney",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -93,6 +86,7 @@ testing = [
|
|||||||
"pytest-subtests",
|
"pytest-subtests",
|
||||||
# https://github.com/pytest-dev/pytest-xdist/pull/1229
|
# https://github.com/pytest-dev/pytest-xdist/pull/1229
|
||||||
"pytest-xdist @ git+https://github.com/sshane/pytest-xdist@2b4372bd62699fb412c4fe2f95bf9f01bd2018da",
|
"pytest-xdist @ git+https://github.com/sshane/pytest-xdist@2b4372bd62699fb412c4fe2f95bf9f01bd2018da",
|
||||||
|
"pytest-timeout",
|
||||||
"pytest-asyncio",
|
"pytest-asyncio",
|
||||||
"pytest-mock",
|
"pytest-mock",
|
||||||
"ruff",
|
"ruff",
|
||||||
@@ -101,13 +95,17 @@ testing = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
dev = [
|
dev = [
|
||||||
|
"av",
|
||||||
|
"dictdiffer",
|
||||||
"matplotlib",
|
"matplotlib",
|
||||||
"opencv-python-headless",
|
"opencv-python-headless",
|
||||||
"gcc-arm-none-eabi @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=gcc-arm-none-eabi",
|
"parameterized >=0.8, <0.9",
|
||||||
|
"pyautogui",
|
||||||
|
"pywinctl",
|
||||||
]
|
]
|
||||||
|
|
||||||
tools = [
|
tools = [
|
||||||
"metadrive-simulator @ git+https://github.com/commaai/metadrive.git@minimal ; (platform_machine != 'aarch64')",
|
"metadrive-simulator @ https://github.com/commaai/metadrive/releases/download/MetaDrive-minimal-0.4.2.4/metadrive_simulator-0.4.2.4-py3-none-any.whl ; (platform_machine != 'aarch64')",
|
||||||
"dearpygui>=2.1.0; (sys_platform != 'linux' or platform_machine != 'aarch64')", # not vended for linux aarch64
|
"dearpygui>=2.1.0; (sys_platform != 'linux' or platform_machine != 'aarch64')", # not vended for linux aarch64
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -131,6 +129,7 @@ cpp_files = "test_*"
|
|||||||
cpp_harness = "selfdrive/test/cpp_harness.py"
|
cpp_harness = "selfdrive/test/cpp_harness.py"
|
||||||
python_files = "test_*.py"
|
python_files = "test_*.py"
|
||||||
asyncio_default_fixture_loop_scope = "function"
|
asyncio_default_fixture_loop_scope = "function"
|
||||||
|
#timeout = "30" # you get this long by default
|
||||||
markers = [
|
markers = [
|
||||||
"slow: tests that take awhile to run and can be skipped with -m 'not slow'",
|
"slow: tests that take awhile to run and can be skipped with -m 'not slow'",
|
||||||
"tici: tests that are only meant to run on the C3/C3X",
|
"tici: tests that are only meant to run on the C3/C3X",
|
||||||
@@ -148,7 +147,7 @@ testpaths = [
|
|||||||
[tool.codespell]
|
[tool.codespell]
|
||||||
quiet-level = 3
|
quiet-level = 3
|
||||||
# if you've got a short variable name that's getting flagged, add it here
|
# if you've got a short variable name that's getting flagged, add it here
|
||||||
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite,ser"
|
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite"
|
||||||
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
|
||||||
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*, selfdrive/assets/offroad/mici_fcc.html"
|
||||||
|
|
||||||
|
|||||||
+1
-1
Submodule rednose_repo updated: 6ccb8d0556...7fddc8e6d4
@@ -72,8 +72,9 @@ find . -name '*.pyc' -delete
|
|||||||
find . -name 'moc_*' -delete
|
find . -name 'moc_*' -delete
|
||||||
find . -name '__pycache__' -delete
|
find . -name '__pycache__' -delete
|
||||||
rm -rf .sconsign.dblite Jenkinsfile release/
|
rm -rf .sconsign.dblite Jenkinsfile release/
|
||||||
rm -f selfdrive/modeld/models/*.onnx
|
rm selfdrive/modeld/models/driving_vision.onnx
|
||||||
rm -f sunnypilot/modeld*/models/*.onnx
|
rm selfdrive/modeld/models/driving_policy.onnx
|
||||||
|
rm sunnypilot/modeld*/models/supercombo.onnx
|
||||||
|
|
||||||
find third_party/ -name '*x86*' -exec rm -r {} +
|
find third_party/ -name '*x86*' -exec rm -r {} +
|
||||||
find third_party/ -name '*Darwin*' -exec rm -r {} +
|
find third_party/ -name '*Darwin*' -exec rm -r {} +
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# To build sim and docs, you can run the following to mount the scons cache to the same place as in CI:
|
||||||
|
# mkdir -p .ci_cache/scons_cache
|
||||||
|
# sudo mount --bind /tmp/scons_cache/ .ci_cache/scons_cache
|
||||||
|
|
||||||
SCRIPT_DIR=$(dirname "$0")
|
SCRIPT_DIR=$(dirname "$0")
|
||||||
OPENPILOT_DIR=$SCRIPT_DIR/../../
|
OPENPILOT_DIR=$SCRIPT_DIR/../../
|
||||||
|
|
||||||
DOCKER_IMAGE=sunnypilot
|
|
||||||
DOCKER_FILE=Dockerfile.openpilot
|
|
||||||
DOCKER_REGISTRY=ghcr.io/sunnypilot
|
|
||||||
COMMIT_SHA=$(git rev-parse HEAD)
|
|
||||||
|
|
||||||
if [ -n "$TARGET_ARCHITECTURE" ]; then
|
if [ -n "$TARGET_ARCHITECTURE" ]; then
|
||||||
PLATFORM="linux/$TARGET_ARCHITECTURE"
|
PLATFORM="linux/$TARGET_ARCHITECTURE"
|
||||||
TAG_SUFFIX="-$TARGET_ARCHITECTURE"
|
TAG_SUFFIX="-$TARGET_ARCHITECTURE"
|
||||||
@@ -17,11 +15,9 @@ else
|
|||||||
TAG_SUFFIX=""
|
TAG_SUFFIX=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LOCAL_TAG=$DOCKER_IMAGE$TAG_SUFFIX
|
source $SCRIPT_DIR/docker_common_sp.sh $1 "$TAG_SUFFIX"
|
||||||
REMOTE_TAG=$DOCKER_REGISTRY/$LOCAL_TAG
|
|
||||||
REMOTE_SHA_TAG=$DOCKER_REGISTRY/$LOCAL_TAG:$COMMIT_SHA
|
|
||||||
|
|
||||||
DOCKER_BUILDKIT=1 docker buildx build --provenance false --pull --platform $PLATFORM --load -t $DOCKER_IMAGE:latest -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR
|
DOCKER_BUILDKIT=1 docker buildx build --provenance false --pull --platform $PLATFORM --load --cache-to type=inline --cache-from type=registry,ref=$REMOTE_TAG -t $DOCKER_IMAGE:latest -t $REMOTE_TAG -t $LOCAL_TAG -f $OPENPILOT_DIR/$DOCKER_FILE $OPENPILOT_DIR
|
||||||
|
|
||||||
if [ -n "$PUSH_IMAGE" ]; then
|
if [ -n "$PUSH_IMAGE" ]; then
|
||||||
docker push $REMOTE_TAG
|
docker push $REMOTE_TAG
|
||||||
|
|||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
if [ "$1" = "base" ]; then
|
||||||
|
export DOCKER_IMAGE=sunnypilot-base
|
||||||
|
export DOCKER_FILE=Dockerfile.sunnypilot_base
|
||||||
|
elif [ "$1" = "prebuilt" ]; then
|
||||||
|
export DOCKER_IMAGE=sunnypilot-prebuilt
|
||||||
|
export DOCKER_FILE=Dockerfile.sunnypilot
|
||||||
|
else
|
||||||
|
echo "Invalid docker build image: '$1'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export DOCKER_REGISTRY=ghcr.io/sunnypilot
|
||||||
|
export COMMIT_SHA=$(git rev-parse HEAD)
|
||||||
|
|
||||||
|
TAG_SUFFIX=$2
|
||||||
|
LOCAL_TAG=$DOCKER_IMAGE$TAG_SUFFIX
|
||||||
|
REMOTE_TAG=$DOCKER_REGISTRY/$LOCAL_TAG
|
||||||
|
REMOTE_SHA_TAG=$DOCKER_REGISTRY/$LOCAL_TAG:$COMMIT_SHA
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import pickle
|
|
||||||
import sys
|
import sys
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
@@ -7,41 +6,6 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime, UTC
|
from datetime import datetime, UTC
|
||||||
|
|
||||||
REQUIRED_OUTPUT_KEYS = frozenset({
|
|
||||||
"plan",
|
|
||||||
"lane_lines",
|
|
||||||
"road_edges",
|
|
||||||
"lead",
|
|
||||||
"desire_state",
|
|
||||||
"desire_pred",
|
|
||||||
"meta",
|
|
||||||
"lead_prob",
|
|
||||||
"lane_lines_prob",
|
|
||||||
"pose",
|
|
||||||
"wide_from_device_euler",
|
|
||||||
"road_transform",
|
|
||||||
"hidden_state",
|
|
||||||
})
|
|
||||||
OPTIONAL_OUTPUT_KEYS = frozenset({
|
|
||||||
"planplus",
|
|
||||||
"sim_pose",
|
|
||||||
"desired_curvature",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
def validate_model_outputs(metadata_paths: list[Path]) -> None:
|
|
||||||
combined_keys: set[str] = set()
|
|
||||||
for path in metadata_paths:
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
metadata = pickle.load(f)
|
|
||||||
combined_keys.update(metadata.get("output_slices", {}).keys())
|
|
||||||
missing = REQUIRED_OUTPUT_KEYS - combined_keys
|
|
||||||
if missing:
|
|
||||||
raise ValueError(f"Combined model metadata is missing required output keys: {sorted(missing)}")
|
|
||||||
detected_optional = sorted(OPTIONAL_OUTPUT_KEYS & combined_keys)
|
|
||||||
if detected_optional:
|
|
||||||
print(f"Optional output keys detected: {detected_optional}")
|
|
||||||
|
|
||||||
|
|
||||||
def create_short_name(full_name):
|
def create_short_name(full_name):
|
||||||
# Remove parentheses and extract alphanumeric words
|
# Remove parentheses and extract alphanumeric words
|
||||||
@@ -160,19 +124,9 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("--output-dir", default="./output", help="Output directory for metadata")
|
parser.add_argument("--output-dir", default="./output", help="Output directory for metadata")
|
||||||
parser.add_argument("--custom-name", help="Custom display name for the model")
|
parser.add_argument("--custom-name", help="Custom display name for the model")
|
||||||
parser.add_argument("--is-20hz", action="store_true", help="Whether this is a 20Hz model")
|
parser.add_argument("--is-20hz", action="store_true", help="Whether this is a 20Hz model")
|
||||||
parser.add_argument("--validate-only", action="store_true")
|
|
||||||
parser.add_argument("--upstream-branch", default="unknown", help="Upstream branch name")
|
parser.add_argument("--upstream-branch", default="unknown", help="Upstream branch name")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.validate_only:
|
|
||||||
metadata_paths = glob.glob(os.path.join(args.model_dir, "*_metadata.pkl"))
|
|
||||||
if not metadata_paths:
|
|
||||||
print(f"No metadata files found in {args.model_dir}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
validate_model_outputs([Path(p) for p in metadata_paths])
|
|
||||||
print(f"Validated {len(metadata_paths)} metadata files successfully.")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# Find all ONNX files in the given directory
|
# Find all ONNX files in the given directory
|
||||||
model_paths = glob.glob(os.path.join(args.model_dir, "*.onnx"))
|
model_paths = glob.glob(os.path.join(args.model_dir, "*.onnx"))
|
||||||
if not model_paths:
|
if not model_paths:
|
||||||
|
|||||||
+8
-20
@@ -1,33 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
|
import onnx
|
||||||
from tinygrad.nn.onnx import OnnxPBParser
|
|
||||||
|
|
||||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||||
|
|
||||||
MASTER_PATH = os.getenv("MASTER_PATH", BASEDIR)
|
MASTER_PATH = os.getenv("MASTER_PATH", BASEDIR)
|
||||||
MODEL_PATH = "/selfdrive/modeld/models/"
|
MODEL_PATH = "/selfdrive/modeld/models/"
|
||||||
|
|
||||||
|
|
||||||
class MetadataOnnxPBParser(OnnxPBParser):
|
|
||||||
def _parse_ModelProto(self) -> dict:
|
|
||||||
obj = {"metadata_props": []}
|
|
||||||
for fid, wire_type in self._parse_message(self.reader.len):
|
|
||||||
match fid:
|
|
||||||
case 14:
|
|
||||||
obj["metadata_props"].append(self._parse_StringStringEntryProto())
|
|
||||||
case _:
|
|
||||||
self.reader.skip_field(wire_type)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def get_checkpoint(f):
|
def get_checkpoint(f):
|
||||||
model = MetadataOnnxPBParser(f).parse()
|
model = onnx.load(f)
|
||||||
metadata = {prop["key"]: prop["value"] for prop in model["metadata_props"]}
|
metadata = {prop.key: prop.value for prop in model.metadata_props}
|
||||||
return metadata['model_checkpoint'].split('/')[0]
|
return metadata['model_checkpoint'].split('/')[0]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("| | master | PR branch |")
|
print("| | master | PR branch |")
|
||||||
print("|-| ----- | --------- |")
|
print("|-| ----- | --------- |")
|
||||||
@@ -40,4 +24,8 @@ if __name__ == "__main__":
|
|||||||
fn = os.path.basename(f)
|
fn = os.path.basename(f)
|
||||||
master = get_checkpoint(MASTER_PATH + MODEL_PATH + fn)
|
master = get_checkpoint(MASTER_PATH + MODEL_PATH + fn)
|
||||||
pr = get_checkpoint(BASEDIR + MODEL_PATH + fn)
|
pr = get_checkpoint(BASEDIR + MODEL_PATH + fn)
|
||||||
print("|", fn, "|", f"[{master}](https://reporter.comma.life/experiment/{master})", "|", f"[{pr}](https://reporter.comma.life/experiment/{pr})", "|")
|
print(
|
||||||
|
"|", fn, "|",
|
||||||
|
f"[{master}](https://reporter.comma.life/experiment/{master})", "|",
|
||||||
|
f"[{pr}](https://reporter.comma.life/experiment/{pr})", "|"
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:20024203288f144633014422e16119278477099f24fba5c155a804a1864a26b4
|
||||||
|
size 7511
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:70d4236bcfd3aa8f100b81179c1e0f193c6ffbd84769c4a516be4381e62b270a
|
|
||||||
size 18666
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:279c1d8f95eb9f4a3058dff76b0f316ce9eef7bc8f4296936ad25fd08703ce13
|
||||||
|
size 10380
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:ed07f72339cf1c3926a2cb7314f9baa099bcdb3f8bc89a9084661b71334b0526
|
|
||||||
size 32599
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5dedb4139a7ddeafcdaf050144769e490643820db726201a15250e1042eb6d15
|
oid sha256:ffb293236f5f8f7da44b5a3c4c0b72e86c4e1fdb04f89c94507af008ff7de139
|
||||||
size 7982
|
size 8210
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d527dcff61fa66902681706b4916586244b8cf0520086ac980ff782ab2d99ce7
|
oid sha256:bda53863c9a46c50a1e2920a76c2d2f1fe4df8a94b8d2e26f5d83eef3a9c3bd3
|
||||||
size 4778
|
size 3627
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6b55e43c50e805ac5e8357e5943374ed02d756cefa3aaffb58c568a0b125c30b
|
||||||
|
size 7750
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c6b1b0f1270a596b5ac150dee8ade54794de55b2033a529d4a17176f688aa6f0
|
oid sha256:5528e9c041b824f005bf1ef6e49b2dbbc4ba10f994b0726d2a17a4fbf8c80f55
|
||||||
size 56738
|
size 21379
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:584cea202afff6dd20d67ae1a9cd6d2b8cc07598bccb91a8d1bac0142567308e
|
||||||
|
size 45489
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:7fae4872ab3c24d5e4c2be6150127a844f89bbdcadfccdff2dfed180e125d577
|
||||||
|
size 45699
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:04236fa0f2759a01c6e321ac7b1c86c7a039215a7953b1a23d250ecf2ef1fa87
|
||||||
|
size 8563
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4337098554af30c98ebd512e17ab08207db868ff34acca5f865fcbfc940286d3
|
||||||
|
size 21123
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:ffd37d5e5d5980efa98fee1cd0e8ebbf4139149b41c099e7dc3d5bd402cffb92
|
||||||
|
size 9072
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:1b1d58704f8808dcb5a7ce9d86bc4212477759e96ac2419475f16f9184ee6a42
|
||||||
|
size 21892
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:7488c1aa69b728387b2cf300a614cc64e3c2305d2b509c14cf44cad65d20d85c
|
oid sha256:782161f35b4925c7063c441b0c341331c814614cf241f21b4e70134280c630f0
|
||||||
size 2509
|
size 1182
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:160f67162e075436200d6719e614ddf96caaa2b7c0a3943f728c2afef10aa4ad
|
||||||
|
size 2489
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:163ac31cb990bdddfe552efef9a68870404caadb1c40fa8a5042b5ae956e6b4c
|
||||||
|
size 24687
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:6e4614adb2d3d0e44c64a855c221ec462a7aee22fff26132ad551035141c1a53
|
||||||
|
size 62056
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:d1dd642ae4708cc7a837e8ef8b4c75f578654d241f8c854249c2b1ade640ceca
|
|
||||||
size 14058
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5ba98ab2b75f0c1f8fdffb9eab0a742645b80ef4ca404c007f374a5e0fd48d8c
|
oid sha256:bcd08444c77b3e559876eeb88d17808f72496adc26e27c3c21c00ff410879447
|
||||||
size 10254
|
size 10966
|
||||||
|
|||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:4545fdbaf67402b28b18644a7353a0620250ece6416c1b0ce0e27c758817b042
|
|
||||||
size 26729
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:a804da77b268f0a625f93949642ae74cdfe5b5caa5baea1c52c4605ae25c80e4
|
|
||||||
size 12916
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:5e993247160edcbc9c3cba3efa93169028568d484bcfd0bf64f3e3a7ec7556c0
|
|
||||||
size 18608
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:1c4f1002ecde9a2b33779c2e784a39b492b4c8d76abc063e935ce0aa971925dd
|
|
||||||
size 65513
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:7b7e0194a8b9009e493cdce35cd15711596a54227c740e9d6419a3891c6c4037
|
||||||
|
size 912
|
||||||
@@ -48,14 +48,14 @@ class VCruiseHelper(VCruiseHelperSP):
|
|||||||
|
|
||||||
self.get_minimum_set_speed(is_metric)
|
self.get_minimum_set_speed(is_metric)
|
||||||
|
|
||||||
_enabled = self.update_enabled_state(CS, enabled)
|
|
||||||
|
|
||||||
if CS.cruiseState.available:
|
if CS.cruiseState.available:
|
||||||
|
_enabled = self.update_enabled_state(CS, enabled)
|
||||||
if not self.CP.pcmCruise or (not self.CP_SP.pcmCruiseSpeed and _enabled):
|
if not self.CP.pcmCruise or (not self.CP_SP.pcmCruiseSpeed and _enabled):
|
||||||
# if stock cruise is completely disabled, then we can use our own set speed logic
|
# if stock cruise is completely disabled, then we can use our own set speed logic
|
||||||
self._update_v_cruise_non_pcm(CS, _enabled, is_metric)
|
self._update_v_cruise_non_pcm(CS, _enabled, is_metric)
|
||||||
self.update_speed_limit_assist_v_cruise_non_pcm()
|
self.update_speed_limit_assist_v_cruise_non_pcm()
|
||||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||||
|
self.update_button_timers(CS, enabled)
|
||||||
else:
|
else:
|
||||||
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||||
self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH
|
self.v_cruise_cluster_kph = CS.cruiseState.speedCluster * CV.MS_TO_KPH
|
||||||
@@ -69,9 +69,6 @@ class VCruiseHelper(VCruiseHelperSP):
|
|||||||
self.v_cruise_kph = V_CRUISE_UNSET
|
self.v_cruise_kph = V_CRUISE_UNSET
|
||||||
self.v_cruise_cluster_kph = V_CRUISE_UNSET
|
self.v_cruise_cluster_kph = V_CRUISE_UNSET
|
||||||
|
|
||||||
if not self.CP.pcmCruise or not self.CP_SP.pcmCruiseSpeed:
|
|
||||||
self.update_button_timers(CS, enabled)
|
|
||||||
|
|
||||||
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric):
|
def _update_v_cruise_non_pcm(self, CS, enabled, is_metric):
|
||||||
# handle button presses. TODO: this should be in state_control, but a decelCruise press
|
# handle button presses. TODO: this should be in state_control, but a decelCruise press
|
||||||
# would have the effect of both enabling and changing speed is checked after the state transition
|
# would have the effect of both enabling and changing speed is checked after the state transition
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import hypothesis.strategies as st
|
import hypothesis.strategies as st
|
||||||
from hypothesis import Phase, given, settings
|
from hypothesis import Phase, given, settings
|
||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
from cereal import car, custom
|
from cereal import car, custom
|
||||||
from opendbc.car import DT_CTRL
|
from opendbc.car import DT_CTRL
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import pytest
|
|||||||
import itertools
|
import itertools
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from openpilot.common.parameterized import parameterized_class
|
from parameterized import parameterized_class
|
||||||
from cereal import log
|
from cereal import log
|
||||||
from openpilot.selfdrive.car.cruise import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_INITIAL, IMPERIAL_INCREMENT
|
from openpilot.selfdrive.car.cruise import VCruiseHelper, V_CRUISE_MIN, V_CRUISE_MAX, V_CRUISE_INITIAL, IMPERIAL_INCREMENT
|
||||||
from cereal import car, custom
|
from cereal import car, custom
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import unittest # noqa: TID251
|
|||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
import hypothesis.strategies as st
|
import hypothesis.strategies as st
|
||||||
from hypothesis import Phase, given, settings
|
from hypothesis import Phase, given, settings
|
||||||
from openpilot.common.parameterized import parameterized_class
|
from parameterized import parameterized_class
|
||||||
|
|
||||||
from opendbc.car import DT_CTRL, gen_empty_fingerprint, structs
|
from opendbc.car import DT_CTRL, gen_empty_fingerprint, structs
|
||||||
from opendbc.car.can_definitions import CanData
|
from opendbc.car.can_definitions import CanData
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ class Controls(ControlsExt):
|
|||||||
hudControl.leftLaneDepart = self.sm['driverAssistance'].leftLaneDeparture
|
hudControl.leftLaneDepart = self.sm['driverAssistance'].leftLaneDeparture
|
||||||
hudControl.rightLaneDepart = self.sm['driverAssistance'].rightLaneDeparture
|
hudControl.rightLaneDepart = self.sm['driverAssistance'].rightLaneDeparture
|
||||||
|
|
||||||
if self.get_lat_active(self.sm):
|
if self.sm['selfdriveState'].active:
|
||||||
CO = self.sm['carOutput']
|
CO = self.sm['carOutput']
|
||||||
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||||
self.steer_limited_by_safety = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
|
self.steer_limited_by_safety = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import itertools
|
import itertools
|
||||||
from openpilot.common.parameterized import parameterized_class
|
from parameterized import parameterized_class
|
||||||
|
|
||||||
from cereal import log
|
from cereal import log
|
||||||
|
|
||||||
@@ -42,5 +42,4 @@ class TestFollowingDistance:
|
|||||||
simulation_steady_state = run_following_distance_simulation(v_lead, e2e=self.e2e, personality=self.personality)
|
simulation_steady_state = run_following_distance_simulation(v_lead, e2e=self.e2e, personality=self.personality)
|
||||||
correct_steady_state = desired_follow_distance(v_lead, v_lead, get_T_FOLLOW(self.personality))
|
correct_steady_state = desired_follow_distance(v_lead, v_lead, get_T_FOLLOW(self.personality))
|
||||||
err_ratio = 0.2 if self.e2e else 0.1
|
err_ratio = 0.2 if self.e2e else 0.1
|
||||||
abs_err_margin = 0.5 if v_lead > 0.0 else 1.15
|
assert simulation_steady_state == pytest.approx(correct_steady_state, abs=err_ratio * correct_steady_state + .5)
|
||||||
assert simulation_steady_state == pytest.approx(correct_steady_state, abs=err_ratio * correct_steady_state + abs_err_margin)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
from opendbc.car.car_helpers import interfaces
|
from opendbc.car.car_helpers import interfaces
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from openpilot.common.parameterized import parameterized
|
from parameterized import parameterized
|
||||||
|
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
from opendbc.car.car_helpers import interfaces
|
from opendbc.car.car_helpers import interfaces
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import os
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from tabulate import tabulate
|
||||||
|
|
||||||
from openpilot.common.utils import tabulate
|
|
||||||
from openpilot.tools.lib.logreader import LogReader
|
from openpilot.tools.lib.logreader import LogReader
|
||||||
|
|
||||||
DEMO_ROUTE = "5beb9b58bd12b691/0000010a--a51155e496"
|
DEMO_ROUTE = "a2a0ccea32023010|2023-07-27--13-01-19"
|
||||||
MB = 1024 * 1024
|
MB = 1024 * 1024
|
||||||
TABULATE_OPTS = dict(tablefmt="simple_grid", stralign="center", numalign="center")
|
TABULATE_OPTS = dict(tablefmt="simple_grid", stralign="center", numalign="center")
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import time
|
|||||||
|
|
||||||
from cereal import car, log, messaging
|
from cereal import car, log, messaging
|
||||||
from openpilot.common.params import Params
|
from openpilot.common.params import Params
|
||||||
from openpilot.system.manager.process_config import managed_processes, is_tinygrad_model, is_stock_model
|
from openpilot.system.manager.process_config import managed_processes, is_snpe_model, is_tinygrad_model, is_stock_model
|
||||||
from openpilot.system.hardware import HARDWARE
|
from openpilot.system.hardware import HARDWARE
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -11,6 +11,8 @@ if __name__ == "__main__":
|
|||||||
params = Params()
|
params = Params()
|
||||||
params.put("CarParams", CP.to_bytes())
|
params.put("CarParams", CP.to_bytes())
|
||||||
|
|
||||||
|
if use_snpe_modeld := is_snpe_model(False, params, CP):
|
||||||
|
print("Using SNPE modeld")
|
||||||
if use_tinygrad_modeld := is_tinygrad_model(False, params, CP):
|
if use_tinygrad_modeld := is_tinygrad_model(False, params, CP):
|
||||||
print("Using TinyGrad modeld")
|
print("Using TinyGrad modeld")
|
||||||
if use_stock_modeld := is_stock_model(False, params, CP):
|
if use_stock_modeld := is_stock_model(False, params, CP):
|
||||||
@@ -19,7 +21,7 @@ if __name__ == "__main__":
|
|||||||
HARDWARE.set_power_save(False)
|
HARDWARE.set_power_save(False)
|
||||||
|
|
||||||
procs = ['camerad', 'ui', 'calibrationd', 'plannerd', 'dmonitoringmodeld', 'dmonitoringd']
|
procs = ['camerad', 'ui', 'calibrationd', 'plannerd', 'dmonitoringmodeld', 'dmonitoringd']
|
||||||
procs += ["modeld_tinygrad" if use_tinygrad_modeld else "modeld"]
|
procs += ["modeld_snpe" if use_snpe_modeld else "modeld_tinygrad" if use_tinygrad_modeld else "modeld"]
|
||||||
for p in procs:
|
for p in procs:
|
||||||
managed_processes[p].start()
|
managed_processes[p].start()
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ MIN_ABS_YAW_RATE = 0.0
|
|||||||
MAX_YAW_RATE_SANITY_CHECK = 1.0
|
MAX_YAW_RATE_SANITY_CHECK = 1.0
|
||||||
MIN_NCC = 0.95
|
MIN_NCC = 0.95
|
||||||
MAX_LAG = 1.0
|
MAX_LAG = 1.0
|
||||||
MIN_LAG = 0.15
|
|
||||||
MAX_LAG_STD = 0.1
|
MAX_LAG_STD = 0.1
|
||||||
MAX_LAT_ACCEL = 2.0
|
MAX_LAT_ACCEL = 2.0
|
||||||
MAX_LAT_ACCEL_DIFF = 0.6
|
MAX_LAT_ACCEL_DIFF = 0.6
|
||||||
@@ -217,7 +216,7 @@ class LateralLagEstimator:
|
|||||||
liveDelay.status = log.LiveDelayData.Status.unestimated
|
liveDelay.status = log.LiveDelayData.Status.unestimated
|
||||||
|
|
||||||
if liveDelay.status == log.LiveDelayData.Status.estimated:
|
if liveDelay.status == log.LiveDelayData.Status.estimated:
|
||||||
liveDelay.lateralDelay = min(MAX_LAG, max(MIN_LAG, valid_mean_lag))
|
liveDelay.lateralDelay = valid_mean_lag
|
||||||
else:
|
else:
|
||||||
liveDelay.lateralDelay = self.initial_lag
|
liveDelay.lateralDelay = self.initial_lag
|
||||||
|
|
||||||
@@ -300,7 +299,7 @@ class LateralLagEstimator:
|
|||||||
new_values_start_idx = next(-i for i, t in enumerate(reversed(times)) if t <= self.last_estimate_t)
|
new_values_start_idx = next(-i for i, t in enumerate(reversed(times)) if t <= self.last_estimate_t)
|
||||||
is_valid = is_valid and not (new_values_start_idx == 0 or not np.any(okay[new_values_start_idx:]))
|
is_valid = is_valid and not (new_values_start_idx == 0 or not np.any(okay[new_values_start_idx:]))
|
||||||
|
|
||||||
delay, corr, confidence = self.actuator_delay(desired, actual, okay, self.dt, MIN_LAG, MAX_LAG)
|
delay, corr, confidence = self.actuator_delay(desired, actual, okay, self.dt, MAX_LAG)
|
||||||
if corr < self.min_ncc or confidence < self.min_confidence or not is_valid:
|
if corr < self.min_ncc or confidence < self.min_confidence or not is_valid:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -308,23 +307,22 @@ class LateralLagEstimator:
|
|||||||
self.last_estimate_t = self.t
|
self.last_estimate_t = self.t
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def actuator_delay(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray,
|
def actuator_delay(expected_sig: np.ndarray, actual_sig: np.ndarray, mask: np.ndarray, dt: float, max_lag: float) -> tuple[float, float, float]:
|
||||||
dt: float, min_lag: float, max_lag: float) -> tuple[float, float, float]:
|
|
||||||
assert len(expected_sig) == len(actual_sig)
|
assert len(expected_sig) == len(actual_sig)
|
||||||
min_lag_samples, max_lag_samples = int(round(min_lag / dt)), int(round(max_lag / dt))
|
max_lag_samples = int(max_lag / dt)
|
||||||
padded_size = fft_next_good_size(len(expected_sig) + max_lag_samples)
|
padded_size = fft_next_good_size(len(expected_sig) + max_lag_samples)
|
||||||
|
|
||||||
ncc = masked_normalized_cross_correlation(expected_sig, actual_sig, mask, padded_size)
|
ncc = masked_normalized_cross_correlation(expected_sig, actual_sig, mask, padded_size)
|
||||||
|
|
||||||
# only consider lags from min_lag to max_lag
|
# only consider lags from 0 to max_lag
|
||||||
roi = np.s_[len(expected_sig) - 1 + min_lag_samples: len(expected_sig) - 1 + max_lag_samples]
|
roi = np.s_[len(expected_sig) - 1: len(expected_sig) - 1 + max_lag_samples]
|
||||||
extended_roi = np.s_[roi.start - CORR_BORDER_OFFSET: roi.stop + CORR_BORDER_OFFSET]
|
extended_roi = np.s_[roi.start - CORR_BORDER_OFFSET: roi.stop + CORR_BORDER_OFFSET]
|
||||||
roi_ncc = ncc[roi]
|
roi_ncc = ncc[roi]
|
||||||
extended_roi_ncc = ncc[extended_roi]
|
extended_roi_ncc = ncc[extended_roi]
|
||||||
|
|
||||||
max_corr_index = np.argmax(roi_ncc)
|
max_corr_index = np.argmax(roi_ncc)
|
||||||
corr = roi_ncc[max_corr_index]
|
corr = roi_ncc[max_corr_index]
|
||||||
lag = parabolic_peak_interp(roi_ncc, max_corr_index) * dt + min_lag
|
lag = parabolic_peak_interp(roi_ncc, max_corr_index) * dt
|
||||||
|
|
||||||
# to estimate lag confidence, gather all high-correlation candidates and see how spread they are
|
# to estimate lag confidence, gather all high-correlation candidates and see how spread they are
|
||||||
# if e.g. 0.8 and 0.4 are both viable, this is an ambiguous case
|
# if e.g. 0.8 and 0.4 are both viable, this is an ambiguous case
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ class TestLagd:
|
|||||||
assert msg.liveDelay.calPerc == 0
|
assert msg.liveDelay.calPerc == 0
|
||||||
|
|
||||||
def test_estimator_basics(self, subtests):
|
def test_estimator_basics(self, subtests):
|
||||||
for lag_frames in range(3, 10):
|
for lag_frames in range(5):
|
||||||
with subtests.test(msg=f"lag_frames={lag_frames}"):
|
with subtests.test(msg=f"lag_frames={lag_frames}"):
|
||||||
mocked_CP = car.CarParams(steerActuatorDelay=0.8)
|
mocked_CP = car.CarParams(steerActuatorDelay=0.8)
|
||||||
estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0)
|
estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0)
|
||||||
@@ -111,7 +111,7 @@ class TestLagd:
|
|||||||
assert msg.liveDelay.calPerc == 100
|
assert msg.liveDelay.calPerc == 100
|
||||||
|
|
||||||
def test_estimator_masking(self):
|
def test_estimator_masking(self):
|
||||||
mocked_CP, lag_frames = car.CarParams(steerActuatorDelay=0.8), random.randint(3, 19)
|
mocked_CP, lag_frames = car.CarParams(steerActuatorDelay=0.8), random.randint(1, 19)
|
||||||
estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, min_valid_block_count=1)
|
estimator = LateralLagEstimator(mocked_CP, DT, min_recovery_buffer_sec=0.0, min_yr=0.0, min_valid_block_count=1)
|
||||||
process_messages(estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4)
|
process_messages(estimator, lag_frames, (int(MIN_OKAY_WINDOW_SEC / DT) + BLOCK_SIZE) * 2, rejection_threshold=0.4)
|
||||||
msg = estimator.get_msg(True)
|
msg = estimator.get_msg(True)
|
||||||
@@ -120,6 +120,7 @@ class TestLagd:
|
|||||||
assert msg.liveDelay.calPerc == 100
|
assert msg.liveDelay.calPerc == 100
|
||||||
|
|
||||||
@pytest.mark.skipif(PC, reason="only on device")
|
@pytest.mark.skipif(PC, reason="only on device")
|
||||||
|
@pytest.mark.timeout(60)
|
||||||
def test_estimator_performance(self):
|
def test_estimator_performance(self):
|
||||||
mocked_CP = car.CarParams(steerActuatorDelay=0.8)
|
mocked_CP = car.CarParams(steerActuatorDelay=0.8)
|
||||||
estimator = LateralLagEstimator(mocked_CP, DT)
|
estimator = LateralLagEstimator(mocked_CP, DT)
|
||||||
|
|||||||
+34
-37
@@ -1,62 +1,59 @@
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
from openpilot.common.file_chunker import chunk_file, get_chunk_paths
|
|
||||||
|
|
||||||
Import('env', 'arch')
|
Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'visionipc')
|
||||||
chunker_file = File("#common/file_chunker.py")
|
|
||||||
lenv = env.Clone()
|
lenv = env.Clone()
|
||||||
|
lenvCython = envCython.Clone()
|
||||||
|
|
||||||
tinygrad_root = env.Dir("#").abspath
|
libs = [cereal, messaging, visionipc, common, 'capnp', 'kj', 'pthread']
|
||||||
tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=tinygrad_root)
|
frameworks = []
|
||||||
if 'pycache' not in x and os.path.isfile(os.path.join(tinygrad_root, x))]
|
|
||||||
|
|
||||||
def estimate_pickle_max_size(onnx_size):
|
common_src = [
|
||||||
return 1.2 * onnx_size + 10 * 1024 * 1024 # 20% + 10MB is plenty
|
"models/commonmodel.cc",
|
||||||
|
"transforms/loadyuv.cc",
|
||||||
|
"transforms/transform.cc",
|
||||||
|
]
|
||||||
|
|
||||||
# compile warp
|
# OpenCL is a framework on Mac
|
||||||
# THREADS=0 is need to prevent bug: https://github.com/tinygrad/tinygrad/issues/14689
|
if arch == "Darwin":
|
||||||
tg_flags = {
|
frameworks += ['OpenCL']
|
||||||
'larch64': 'DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0',
|
else:
|
||||||
'Darwin': f'DEV=CPU THREADS=0 HOME={os.path.expanduser("~")} IMAGE=0', # tinygrad calls brew which needs a $HOME in the env
|
libs += ['OpenCL']
|
||||||
}.get(arch, 'DEV=CPU CPU_LLVM=1 THREADS=0 IMAGE=0')
|
|
||||||
|
# Set path definitions
|
||||||
|
for pathdef, fn in {'TRANSFORM': 'transforms/transform.cl', 'LOADYUV': 'transforms/loadyuv.cl'}.items():
|
||||||
|
for xenv in (lenv, lenvCython):
|
||||||
|
xenv['CXXFLAGS'].append(f'-D{pathdef}_PATH=\\"{File(fn).abspath}\\"')
|
||||||
|
|
||||||
|
# Compile cython
|
||||||
|
cython_libs = envCython["LIBS"] + libs
|
||||||
|
commonmodel_lib = lenv.Library('commonmodel', common_src)
|
||||||
|
lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
|
||||||
|
tinygrad_files = sorted(["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x])
|
||||||
|
|
||||||
# Get model metadata
|
# Get model metadata
|
||||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||||
fn = File(f"models/{model_name}").abspath
|
fn = File(f"models/{model_name}").abspath
|
||||||
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
||||||
cmd = f'{tg_flags} python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||||
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
||||||
|
|
||||||
image_flag = {
|
|
||||||
'larch64': 'IMAGE=2',
|
|
||||||
}.get(arch, 'IMAGE=0')
|
|
||||||
script_files = [File(Dir("#selfdrive/modeld").File("compile_warp.py").abspath)]
|
|
||||||
compile_warp_cmd = f'{tg_flags} python3 {Dir("#selfdrive/modeld").abspath}/compile_warp.py '
|
|
||||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
|
||||||
warp_targets = []
|
|
||||||
for cam in [_ar_ox_fisheye, _os_fisheye]:
|
|
||||||
w, h = cam.width, cam.height
|
|
||||||
warp_targets += [File(f"models/warp_{w}x{h}_tinygrad.pkl").abspath, File(f"models/dm_warp_{w}x{h}_tinygrad.pkl").abspath]
|
|
||||||
lenv.Command(warp_targets, tinygrad_files + script_files, compile_warp_cmd)
|
|
||||||
|
|
||||||
def tg_compile(flags, model_name):
|
def tg_compile(flags, model_name):
|
||||||
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
||||||
fn = File(f"models/{model_name}").abspath
|
fn = File(f"models/{model_name}").abspath
|
||||||
pkl = fn + "_tinygrad.pkl"
|
|
||||||
onnx_path = fn + ".onnx"
|
|
||||||
chunk_targets = get_chunk_paths(pkl, estimate_pickle_max_size(os.path.getsize(onnx_path)))
|
|
||||||
def do_chunk(target, source, env):
|
|
||||||
chunk_file(pkl, chunk_targets)
|
|
||||||
return lenv.Command(
|
return lenv.Command(
|
||||||
chunk_targets,
|
fn + "_tinygrad.pkl",
|
||||||
[onnx_path] + tinygrad_files + [chunker_file],
|
[fn + ".onnx"] + tinygrad_files,
|
||||||
[f'{pythonpath_string} {flags} {image_flag} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {pkl}',
|
f'{pythonpath_string} {flags} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl'
|
||||||
do_chunk]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Compile small models
|
# Compile small models
|
||||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||||
tg_compile(tg_flags, model_name)
|
flags = {
|
||||||
|
'larch64': 'DEV=QCOM FLOAT16=1 NOLOCALS=1 IMAGE=2 JIT_BATCH_SIZE=0',
|
||||||
|
'Darwin': f'DEV=CPU HOME={os.path.expanduser("~")} IMAGE=0', # tinygrad calls brew which needs a $HOME in the env
|
||||||
|
}.get(arch, 'DEV=CPU CPU_LLVM=1 IMAGE=0')
|
||||||
|
tg_compile(flags, model_name)
|
||||||
|
|
||||||
# Compile BIG model if USB GPU is available
|
# Compile BIG model if USB GPU is available
|
||||||
if "USBGPU" in os.environ:
|
if "USBGPU" in os.environ:
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import time
|
|
||||||
import pickle
|
|
||||||
import numpy as np
|
|
||||||
from pathlib import Path
|
|
||||||
from tinygrad.tensor import Tensor
|
|
||||||
from tinygrad.helpers import Context
|
|
||||||
from tinygrad.device import Device
|
|
||||||
from tinygrad.engine.jit import TinyJit
|
|
||||||
|
|
||||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
|
||||||
from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE, DM_INPUT_SIZE
|
|
||||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
|
||||||
|
|
||||||
MODELS_DIR = Path(__file__).parent / 'models'
|
|
||||||
|
|
||||||
CAMERA_CONFIGS = [
|
|
||||||
(_ar_ox_fisheye.width, _ar_ox_fisheye.height), # tici: 1928x1208
|
|
||||||
(_os_fisheye.width, _os_fisheye.height), # mici: 1344x760
|
|
||||||
]
|
|
||||||
|
|
||||||
UV_SCALE_MATRIX = np.array([[0.5, 0, 0], [0, 0.5, 0], [0, 0, 1]], dtype=np.float32)
|
|
||||||
UV_SCALE_MATRIX_INV = np.linalg.inv(UV_SCALE_MATRIX)
|
|
||||||
|
|
||||||
IMG_BUFFER_SHAPE = (30, MEDMODEL_INPUT_SIZE[1] // 2, MEDMODEL_INPUT_SIZE[0] // 2)
|
|
||||||
|
|
||||||
|
|
||||||
def warp_pkl_path(w, h):
|
|
||||||
return MODELS_DIR / f'warp_{w}x{h}_tinygrad.pkl'
|
|
||||||
|
|
||||||
|
|
||||||
def dm_warp_pkl_path(w, h):
|
|
||||||
return MODELS_DIR / f'dm_warp_{w}x{h}_tinygrad.pkl'
|
|
||||||
|
|
||||||
|
|
||||||
def warp_perspective_tinygrad(src_flat, M_inv, dst_shape, src_shape, stride_pad):
|
|
||||||
w_dst, h_dst = dst_shape
|
|
||||||
h_src, w_src = src_shape
|
|
||||||
|
|
||||||
x = Tensor.arange(w_dst).reshape(1, w_dst).expand(h_dst, w_dst).reshape(-1)
|
|
||||||
y = Tensor.arange(h_dst).reshape(h_dst, 1).expand(h_dst, w_dst).reshape(-1)
|
|
||||||
|
|
||||||
# inline 3x3 matmul as elementwise to avoid reduce op (enables fusion with gather)
|
|
||||||
src_x = M_inv[0, 0] * x + M_inv[0, 1] * y + M_inv[0, 2]
|
|
||||||
src_y = M_inv[1, 0] * x + M_inv[1, 1] * y + M_inv[1, 2]
|
|
||||||
src_w = M_inv[2, 0] * x + M_inv[2, 1] * y + M_inv[2, 2]
|
|
||||||
|
|
||||||
src_x = src_x / src_w
|
|
||||||
src_y = src_y / src_w
|
|
||||||
|
|
||||||
x_nn_clipped = Tensor.round(src_x).clip(0, w_src - 1).cast('int')
|
|
||||||
y_nn_clipped = Tensor.round(src_y).clip(0, h_src - 1).cast('int')
|
|
||||||
idx = y_nn_clipped * (w_src + stride_pad) + x_nn_clipped
|
|
||||||
|
|
||||||
return src_flat[idx]
|
|
||||||
|
|
||||||
|
|
||||||
def frames_to_tensor(frames, model_w, model_h):
|
|
||||||
H = (frames.shape[0] * 2) // 3
|
|
||||||
W = frames.shape[1]
|
|
||||||
in_img1 = Tensor.cat(frames[0:H:2, 0::2],
|
|
||||||
frames[1:H:2, 0::2],
|
|
||||||
frames[0:H:2, 1::2],
|
|
||||||
frames[1:H:2, 1::2],
|
|
||||||
frames[H:H+H//4].reshape((H//2, W//2)),
|
|
||||||
frames[H+H//4:H+H//2].reshape((H//2, W//2)), dim=0).reshape((6, H//2, W//2))
|
|
||||||
return in_img1
|
|
||||||
|
|
||||||
|
|
||||||
def make_frame_prepare(cam_w, cam_h, model_w, model_h):
|
|
||||||
stride, y_height, uv_height, _ = get_nv12_info(cam_w, cam_h)
|
|
||||||
uv_offset = stride * y_height
|
|
||||||
stride_pad = stride - cam_w
|
|
||||||
|
|
||||||
def frame_prepare_tinygrad(input_frame, M_inv):
|
|
||||||
# UV_SCALE @ M_inv @ UV_SCALE_INV simplifies to elementwise scaling
|
|
||||||
M_inv_uv = M_inv * Tensor([[1.0, 1.0, 0.5], [1.0, 1.0, 0.5], [2.0, 2.0, 1.0]])
|
|
||||||
# deinterleave NV12 UV plane (UVUV... -> separate U, V)
|
|
||||||
uv = input_frame[uv_offset:uv_offset + uv_height * stride].reshape(uv_height, stride)
|
|
||||||
with Context(SPLIT_REDUCEOP=0):
|
|
||||||
y = warp_perspective_tinygrad(input_frame[:cam_h*stride],
|
|
||||||
M_inv, (model_w, model_h),
|
|
||||||
(cam_h, cam_w), stride_pad).realize()
|
|
||||||
u = warp_perspective_tinygrad(uv[:cam_h//2, :cam_w:2].flatten(),
|
|
||||||
M_inv_uv, (model_w//2, model_h//2),
|
|
||||||
(cam_h//2, cam_w//2), 0).realize()
|
|
||||||
v = warp_perspective_tinygrad(uv[:cam_h//2, 1:cam_w:2].flatten(),
|
|
||||||
M_inv_uv, (model_w//2, model_h//2),
|
|
||||||
(cam_h//2, cam_w//2), 0).realize()
|
|
||||||
yuv = y.cat(u).cat(v).reshape((model_h * 3 // 2, model_w))
|
|
||||||
tensor = frames_to_tensor(yuv, model_w, model_h)
|
|
||||||
return tensor
|
|
||||||
return frame_prepare_tinygrad
|
|
||||||
|
|
||||||
|
|
||||||
def make_update_img_input(frame_prepare, model_w, model_h):
|
|
||||||
def update_img_input_tinygrad(tensor, frame, M_inv):
|
|
||||||
M_inv = M_inv.to(Device.DEFAULT)
|
|
||||||
new_img = frame_prepare(frame, M_inv)
|
|
||||||
full_buffer = tensor[6:].cat(new_img, dim=0).contiguous()
|
|
||||||
return full_buffer, Tensor.cat(full_buffer[:6], full_buffer[-6:], dim=0).contiguous().reshape(1, 12, model_h//2, model_w//2)
|
|
||||||
return update_img_input_tinygrad
|
|
||||||
|
|
||||||
|
|
||||||
def make_update_both_imgs(frame_prepare, model_w, model_h):
|
|
||||||
update_img = make_update_img_input(frame_prepare, model_w, model_h)
|
|
||||||
|
|
||||||
def update_both_imgs_tinygrad(calib_img_buffer, new_img, M_inv,
|
|
||||||
calib_big_img_buffer, new_big_img, M_inv_big):
|
|
||||||
calib_img_buffer, calib_img_pair = update_img(calib_img_buffer, new_img, M_inv)
|
|
||||||
calib_big_img_buffer, calib_big_img_pair = update_img(calib_big_img_buffer, new_big_img, M_inv_big)
|
|
||||||
return calib_img_buffer, calib_img_pair, calib_big_img_buffer, calib_big_img_pair
|
|
||||||
return update_both_imgs_tinygrad
|
|
||||||
|
|
||||||
|
|
||||||
def make_warp_dm(cam_w, cam_h, dm_w, dm_h):
|
|
||||||
stride, y_height, _, _ = get_nv12_info(cam_w, cam_h)
|
|
||||||
stride_pad = stride - cam_w
|
|
||||||
|
|
||||||
def warp_dm(input_frame, M_inv):
|
|
||||||
M_inv = M_inv.to(Device.DEFAULT)
|
|
||||||
result = warp_perspective_tinygrad(input_frame[:cam_h*stride], M_inv, (dm_w, dm_h), (cam_h, cam_w), stride_pad).reshape(-1, dm_h * dm_w)
|
|
||||||
return result
|
|
||||||
return warp_dm
|
|
||||||
|
|
||||||
|
|
||||||
def compile_modeld_warp(cam_w, cam_h):
|
|
||||||
model_w, model_h = MEDMODEL_INPUT_SIZE
|
|
||||||
_, _, _, yuv_size = get_nv12_info(cam_w, cam_h)
|
|
||||||
|
|
||||||
print(f"Compiling modeld warp for {cam_w}x{cam_h}...")
|
|
||||||
|
|
||||||
frame_prepare = make_frame_prepare(cam_w, cam_h, model_w, model_h)
|
|
||||||
update_both_imgs = make_update_both_imgs(frame_prepare, model_w, model_h)
|
|
||||||
update_img_jit = TinyJit(update_both_imgs, prune=True)
|
|
||||||
|
|
||||||
full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
|
||||||
big_full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
|
||||||
full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
|
||||||
big_full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
|
||||||
|
|
||||||
for i in range(10):
|
|
||||||
new_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
|
||||||
img_inputs = [full_buffer,
|
|
||||||
Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
|
||||||
new_big_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
|
||||||
big_img_inputs = [big_full_buffer,
|
|
||||||
Tensor.from_blob(new_big_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
|
||||||
inputs = img_inputs + big_img_inputs
|
|
||||||
Device.default.synchronize()
|
|
||||||
|
|
||||||
inputs_np = [x.numpy() for x in inputs]
|
|
||||||
inputs_np[0] = full_buffer_np
|
|
||||||
inputs_np[3] = big_full_buffer_np
|
|
||||||
|
|
||||||
st = time.perf_counter()
|
|
||||||
out = update_img_jit(*inputs)
|
|
||||||
full_buffer = out[0].contiguous().realize().clone()
|
|
||||||
big_full_buffer = out[2].contiguous().realize().clone()
|
|
||||||
mt = time.perf_counter()
|
|
||||||
Device.default.synchronize()
|
|
||||||
et = time.perf_counter()
|
|
||||||
print(f" [{i+1}/10] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms")
|
|
||||||
|
|
||||||
pkl_path = warp_pkl_path(cam_w, cam_h)
|
|
||||||
with open(pkl_path, "wb") as f:
|
|
||||||
pickle.dump(update_img_jit, f)
|
|
||||||
print(f" Saved to {pkl_path}")
|
|
||||||
|
|
||||||
jit = pickle.load(open(pkl_path, "rb"))
|
|
||||||
jit(*inputs)
|
|
||||||
|
|
||||||
|
|
||||||
def compile_dm_warp(cam_w, cam_h):
|
|
||||||
dm_w, dm_h = DM_INPUT_SIZE
|
|
||||||
_, _, _, yuv_size = get_nv12_info(cam_w, cam_h)
|
|
||||||
|
|
||||||
print(f"Compiling DM warp for {cam_w}x{cam_h}...")
|
|
||||||
|
|
||||||
warp_dm = make_warp_dm(cam_w, cam_h, dm_w, dm_h)
|
|
||||||
warp_dm_jit = TinyJit(warp_dm, prune=True)
|
|
||||||
|
|
||||||
for i in range(10):
|
|
||||||
inputs = [Tensor.from_blob((32 * Tensor.randn(yuv_size,) + 128).cast(dtype='uint8').realize().numpy().ctypes.data, (yuv_size,), dtype='uint8'),
|
|
||||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
|
||||||
Device.default.synchronize()
|
|
||||||
st = time.perf_counter()
|
|
||||||
warp_dm_jit(*inputs)
|
|
||||||
mt = time.perf_counter()
|
|
||||||
Device.default.synchronize()
|
|
||||||
et = time.perf_counter()
|
|
||||||
print(f" [{i+1}/10] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms")
|
|
||||||
|
|
||||||
pkl_path = dm_warp_pkl_path(cam_w, cam_h)
|
|
||||||
with open(pkl_path, "wb") as f:
|
|
||||||
pickle.dump(warp_dm_jit, f)
|
|
||||||
print(f" Saved to {pkl_path}")
|
|
||||||
|
|
||||||
|
|
||||||
def run_and_save_pickle():
|
|
||||||
for cam_w, cam_h in CAMERA_CONFIGS:
|
|
||||||
compile_modeld_warp(cam_w, cam_h)
|
|
||||||
compile_dm_warp(cam_w, cam_h)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run_and_save_pickle()
|
|
||||||
@@ -3,6 +3,7 @@ import os
|
|||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||||
from tinygrad.tensor import Tensor
|
from tinygrad.tensor import Tensor
|
||||||
|
from tinygrad.dtype import dtypes
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -15,57 +16,50 @@ from openpilot.common.swaglog import cloudlog
|
|||||||
from openpilot.common.realtime import config_realtime_process
|
from openpilot.common.realtime import config_realtime_process
|
||||||
from openpilot.common.transformations.model import dmonitoringmodel_intrinsics
|
from openpilot.common.transformations.model import dmonitoringmodel_intrinsics
|
||||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
||||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
from openpilot.selfdrive.modeld.models.commonmodel_pyx import CLContext, MonitoringModelFrame
|
||||||
from openpilot.common.file_chunker import read_file_chunked
|
|
||||||
from openpilot.selfdrive.modeld.parse_model_outputs import sigmoid, safe_exp
|
from openpilot.selfdrive.modeld.parse_model_outputs import sigmoid, safe_exp
|
||||||
|
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||||
|
|
||||||
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
||||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||||
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
||||||
METADATA_PATH = Path(__file__).parent / 'models/dmonitoring_model_metadata.pkl'
|
METADATA_PATH = Path(__file__).parent / 'models/dmonitoring_model_metadata.pkl'
|
||||||
MODELS_DIR = Path(__file__).parent / 'models'
|
|
||||||
|
|
||||||
class ModelState:
|
class ModelState:
|
||||||
inputs: dict[str, np.ndarray]
|
inputs: dict[str, np.ndarray]
|
||||||
output: np.ndarray
|
output: np.ndarray
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, cl_ctx):
|
||||||
with open(METADATA_PATH, 'rb') as f:
|
with open(METADATA_PATH, 'rb') as f:
|
||||||
model_metadata = pickle.load(f)
|
model_metadata = pickle.load(f)
|
||||||
self.input_shapes = model_metadata['input_shapes']
|
self.input_shapes = model_metadata['input_shapes']
|
||||||
self.output_slices = model_metadata['output_slices']
|
self.output_slices = model_metadata['output_slices']
|
||||||
|
|
||||||
|
self.frame = MonitoringModelFrame(cl_ctx)
|
||||||
self.numpy_inputs = {
|
self.numpy_inputs = {
|
||||||
'calib': np.zeros(self.input_shapes['calib'], dtype=np.float32),
|
'calib': np.zeros(self.input_shapes['calib'], dtype=np.float32),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.warp_inputs_np = {'transform': np.zeros((3,3), dtype=np.float32)}
|
|
||||||
self.warp_inputs = {k: Tensor(v, device='NPY') for k,v in self.warp_inputs_np.items()}
|
|
||||||
self.frame_buf_params = None
|
|
||||||
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||||
self._blob_cache : dict[int, Tensor] = {}
|
with open(MODEL_PKL_PATH, "rb") as f:
|
||||||
self.image_warp = None
|
self.model_run = pickle.load(f)
|
||||||
self.model_run = pickle.loads(read_file_chunked(str(MODEL_PKL_PATH)))
|
|
||||||
|
|
||||||
def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]:
|
def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]:
|
||||||
self.numpy_inputs['calib'][0,:] = calib
|
self.numpy_inputs['calib'][0,:] = calib
|
||||||
|
|
||||||
t1 = time.perf_counter()
|
t1 = time.perf_counter()
|
||||||
|
|
||||||
if self.image_warp is None:
|
input_img_cl = self.frame.prepare(buf, transform.flatten())
|
||||||
self.frame_buf_params = get_nv12_info(buf.width, buf.height)
|
if TICI:
|
||||||
warp_path = MODELS_DIR / f'dm_warp_{buf.width}x{buf.height}_tinygrad.pkl'
|
# The imgs tensors are backed by opencl memory, only need init once
|
||||||
with open(warp_path, "rb") as f:
|
if 'input_img' not in self.tensor_inputs:
|
||||||
self.image_warp = pickle.load(f)
|
self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, self.input_shapes['input_img'], dtype=dtypes.uint8)
|
||||||
ptr = buf.data.ctypes.data
|
else:
|
||||||
# There is a ringbuffer of imgs, just cache tensors pointing to all of them
|
self.tensor_inputs['input_img'] = Tensor(self.frame.buffer_from_cl(input_img_cl).reshape(self.input_shapes['input_img']), dtype=dtypes.uint8).realize()
|
||||||
if ptr not in self._blob_cache:
|
|
||||||
self._blob_cache[ptr] = Tensor.from_blob(ptr, (self.frame_buf_params[3],), dtype='uint8')
|
|
||||||
|
|
||||||
self.warp_inputs_np['transform'][:] = transform[:]
|
|
||||||
self.tensor_inputs['input_img'] = self.image_warp(self._blob_cache[ptr], self.warp_inputs['transform']).realize()
|
|
||||||
|
|
||||||
output = self.model_run(**self.tensor_inputs).contiguous().realize().uop.base.buffer.numpy().flatten()
|
output = self.model_run(**self.tensor_inputs).numpy().flatten()
|
||||||
|
|
||||||
t2 = time.perf_counter()
|
t2 = time.perf_counter()
|
||||||
return output, t2 - t1
|
return output, t2 - t1
|
||||||
@@ -113,11 +107,12 @@ def get_driverstate_packet(model_output, frame_id: int, location_ts: int, exec_t
|
|||||||
def main():
|
def main():
|
||||||
config_realtime_process(7, 5)
|
config_realtime_process(7, 5)
|
||||||
|
|
||||||
model = ModelState()
|
cl_context = CLContext()
|
||||||
|
model = ModelState(cl_context)
|
||||||
cloudlog.warning("models loaded, dmonitoringmodeld starting")
|
cloudlog.warning("models loaded, dmonitoringmodeld starting")
|
||||||
|
|
||||||
cloudlog.warning("connecting to driver stream")
|
cloudlog.warning("connecting to driver stream")
|
||||||
vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True)
|
vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True, cl_context)
|
||||||
while not vipc_client.connect(False):
|
while not vipc_client.connect(False):
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
assert vipc_client.is_connected()
|
assert vipc_client.is_connected()
|
||||||
|
|||||||
@@ -1,51 +1,33 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sys
|
import sys
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import onnx
|
||||||
import codecs
|
import codecs
|
||||||
import pickle
|
import pickle
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from tinygrad.nn.onnx import OnnxPBParser
|
def get_name_and_shape(value_info:onnx.ValueInfoProto) -> tuple[str, tuple[int,...]]:
|
||||||
|
shape = tuple([int(dim.dim_value) for dim in value_info.type.tensor_type.shape.dim])
|
||||||
|
name = value_info.name
|
||||||
class MetadataOnnxPBParser(OnnxPBParser):
|
|
||||||
def _parse_ModelProto(self) -> dict:
|
|
||||||
obj: dict[str, Any] = {"graph": {"input": [], "output": []}, "metadata_props": []}
|
|
||||||
for fid, wire_type in self._parse_message(self.reader.len):
|
|
||||||
match fid:
|
|
||||||
case 7:
|
|
||||||
obj["graph"] = self._parse_GraphProto()
|
|
||||||
case 14:
|
|
||||||
obj["metadata_props"].append(self._parse_StringStringEntryProto())
|
|
||||||
case _:
|
|
||||||
self.reader.skip_field(wire_type)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def get_name_and_shape(value_info: dict[str, Any]) -> tuple[str, tuple[int, ...]]:
|
|
||||||
shape = tuple(int(dim) if isinstance(dim, int) else 0 for dim in value_info["parsed_type"].shape)
|
|
||||||
name = value_info["name"]
|
|
||||||
return name, shape
|
return name, shape
|
||||||
|
|
||||||
|
def get_metadata_value_by_name(model:onnx.ModelProto, name:str) -> str | Any:
|
||||||
def get_metadata_value_by_name(model: dict[str, Any], name: str) -> str | Any:
|
for prop in model.metadata_props:
|
||||||
for prop in model["metadata_props"]:
|
if prop.key == name:
|
||||||
if prop["key"] == name:
|
return prop.value
|
||||||
return prop["value"]
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
model_path = pathlib.Path(sys.argv[1])
|
model_path = pathlib.Path(sys.argv[1])
|
||||||
model = MetadataOnnxPBParser(model_path).parse()
|
model = onnx.load(str(model_path))
|
||||||
output_slices = get_metadata_value_by_name(model, 'output_slices')
|
output_slices = get_metadata_value_by_name(model, 'output_slices')
|
||||||
assert output_slices is not None, 'output_slices not found in metadata'
|
assert output_slices is not None, 'output_slices not found in metadata'
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'),
|
'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'),
|
||||||
'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")),
|
'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")),
|
||||||
'input_shapes': dict(get_name_and_shape(x) for x in model["graph"]["input"]),
|
'input_shapes': dict([get_name_and_shape(x) for x in model.graph.input]),
|
||||||
'output_shapes': dict(get_name_and_shape(x) for x in model["graph"]["output"]),
|
'output_shapes': dict([get_name_and_shape(x) for x in model.graph.output])
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl')
|
metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl')
|
||||||
|
|||||||
+34
-45
@@ -7,6 +7,7 @@ if USBGPU:
|
|||||||
os.environ['DEV'] = 'AMD'
|
os.environ['DEV'] = 'AMD'
|
||||||
os.environ['AMD_IFACE'] = 'USB'
|
os.environ['AMD_IFACE'] = 'USB'
|
||||||
from tinygrad.tensor import Tensor
|
from tinygrad.tensor import Tensor
|
||||||
|
from tinygrad.dtype import dtypes
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -21,17 +22,17 @@ from openpilot.common.params import Params
|
|||||||
from openpilot.common.filter_simple import FirstOrderFilter
|
from openpilot.common.filter_simple import FirstOrderFilter
|
||||||
from openpilot.common.realtime import config_realtime_process, DT_MDL
|
from openpilot.common.realtime import config_realtime_process, DT_MDL
|
||||||
from openpilot.common.transformations.camera import DEVICE_CAMERAS
|
from openpilot.common.transformations.camera import DEVICE_CAMERAS
|
||||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
|
||||||
from openpilot.common.transformations.model import get_warp_matrix
|
from openpilot.common.transformations.model import get_warp_matrix
|
||||||
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
|
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
|
||||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan
|
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plan
|
||||||
from openpilot.selfdrive.modeld.parse_model_outputs import Parser
|
from openpilot.selfdrive.modeld.parse_model_outputs import Parser
|
||||||
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
|
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
|
||||||
from openpilot.common.file_chunker import read_file_chunked
|
|
||||||
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
||||||
|
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
|
||||||
|
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||||
|
|
||||||
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
|
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
|
||||||
from openpilot.sunnypilot.modeld_v2.modeld_base import ModelStateBase
|
from openpilot.sunnypilot.modeld.modeld_base import ModelStateBase
|
||||||
|
|
||||||
|
|
||||||
PROCESS_NAME = "selfdrive.modeld.modeld"
|
PROCESS_NAME = "selfdrive.modeld.modeld"
|
||||||
@@ -41,15 +42,11 @@ VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
|
|||||||
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
||||||
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
||||||
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
||||||
MODELS_DIR = Path(__file__).parent / 'models'
|
|
||||||
|
|
||||||
LAT_SMOOTH_SECONDS = 0.0
|
LAT_SMOOTH_SECONDS = 0.0
|
||||||
LONG_SMOOTH_SECONDS = 0.3
|
LONG_SMOOTH_SECONDS = 0.3
|
||||||
MIN_LAT_CONTROL_SPEED = 0.3
|
MIN_LAT_CONTROL_SPEED = 0.3
|
||||||
|
|
||||||
IMG_QUEUE_SHAPE = (6*(ModelConstants.MODEL_RUN_FREQ//ModelConstants.MODEL_CONTEXT_FREQ + 1), 128, 256)
|
|
||||||
assert IMG_QUEUE_SHAPE[0] == 30
|
|
||||||
|
|
||||||
|
|
||||||
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
|
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
|
||||||
lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action:
|
lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action:
|
||||||
@@ -142,11 +139,12 @@ class InputQueues:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
class ModelState(ModelStateBase):
|
class ModelState(ModelStateBase):
|
||||||
|
frames: dict[str, DrivingModelFrame]
|
||||||
inputs: dict[str, np.ndarray]
|
inputs: dict[str, np.ndarray]
|
||||||
output: np.ndarray
|
output: np.ndarray
|
||||||
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, context: CLContext):
|
||||||
ModelStateBase.__init__(self)
|
ModelStateBase.__init__(self)
|
||||||
self.LAT_SMOOTH_SECONDS = LAT_SMOOTH_SECONDS
|
self.LAT_SMOOTH_SECONDS = LAT_SMOOTH_SECONDS
|
||||||
with open(VISION_METADATA_PATH, 'rb') as f:
|
with open(VISION_METADATA_PATH, 'rb') as f:
|
||||||
@@ -162,6 +160,7 @@ class ModelState(ModelStateBase):
|
|||||||
self.policy_output_slices = policy_metadata['output_slices']
|
self.policy_output_slices = policy_metadata['output_slices']
|
||||||
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
|
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
|
||||||
|
|
||||||
|
self.frames = {name: DrivingModelFrame(context, ModelConstants.MODEL_RUN_FREQ//ModelConstants.MODEL_CONTEXT_FREQ) for name in self.vision_input_names}
|
||||||
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
|
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
|
||||||
|
|
||||||
# policy inputs
|
# policy inputs
|
||||||
@@ -171,20 +170,18 @@ class ModelState(ModelStateBase):
|
|||||||
self.full_input_queues.update_dtypes_and_shapes({k: self.numpy_inputs[k].dtype}, {k: self.numpy_inputs[k].shape})
|
self.full_input_queues.update_dtypes_and_shapes({k: self.numpy_inputs[k].dtype}, {k: self.numpy_inputs[k].shape})
|
||||||
self.full_input_queues.reset()
|
self.full_input_queues.reset()
|
||||||
|
|
||||||
self.img_queues = {'img': Tensor.zeros(IMG_QUEUE_SHAPE, dtype='uint8').contiguous().realize(),
|
# img buffers are managed in openCL transform code
|
||||||
'big_img': Tensor.zeros(IMG_QUEUE_SHAPE, dtype='uint8').contiguous().realize()}
|
self.vision_inputs: dict[str, Tensor] = {}
|
||||||
self.full_frames : dict[str, Tensor] = {}
|
|
||||||
self._blob_cache : dict[int, Tensor] = {}
|
|
||||||
self.transforms_np = {k: np.zeros((3,3), dtype=np.float32) for k in self.img_queues}
|
|
||||||
self.transforms = {k: Tensor(v, device='NPY').realize() for k, v in self.transforms_np.items()}
|
|
||||||
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
|
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
|
||||||
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||||
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
||||||
self.parser = Parser()
|
self.parser = Parser()
|
||||||
self.frame_buf_params : dict[str, tuple[int, int, int, int]] = {}
|
|
||||||
self.update_imgs = None
|
with open(VISION_PKL_PATH, "rb") as f:
|
||||||
self.vision_run = pickle.loads(read_file_chunked(str(VISION_PKL_PATH)))
|
self.vision_run = pickle.load(f)
|
||||||
self.policy_run = pickle.loads(read_file_chunked(str(POLICY_PKL_PATH)))
|
|
||||||
|
with open(POLICY_PKL_PATH, "rb") as f:
|
||||||
|
self.policy_run = pickle.load(f)
|
||||||
|
|
||||||
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
|
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
|
||||||
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
|
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
|
||||||
@@ -196,34 +193,23 @@ class ModelState(ModelStateBase):
|
|||||||
inputs['desire_pulse'][0] = 0
|
inputs['desire_pulse'][0] = 0
|
||||||
new_desire = np.where(inputs['desire_pulse'] - self.prev_desire > .99, inputs['desire_pulse'], 0)
|
new_desire = np.where(inputs['desire_pulse'] - self.prev_desire > .99, inputs['desire_pulse'], 0)
|
||||||
self.prev_desire[:] = inputs['desire_pulse']
|
self.prev_desire[:] = inputs['desire_pulse']
|
||||||
if self.update_imgs is None:
|
|
||||||
for key in bufs.keys():
|
|
||||||
w, h = bufs[key].width, bufs[key].height
|
|
||||||
self.frame_buf_params[key] = get_nv12_info(w, h)
|
|
||||||
warp_path = MODELS_DIR / f'warp_{w}x{h}_tinygrad.pkl'
|
|
||||||
with open(warp_path, "rb") as f:
|
|
||||||
self.update_imgs = pickle.load(f)
|
|
||||||
|
|
||||||
for key in bufs.keys():
|
imgs_cl = {name: self.frames[name].prepare(bufs[name], transforms[name].flatten()) for name in self.vision_input_names}
|
||||||
ptr = bufs[key].data.ctypes.data
|
|
||||||
yuv_size = self.frame_buf_params[key][3]
|
|
||||||
# There is a ringbuffer of imgs, just cache tensors pointing to all of them
|
|
||||||
cache_key = (key, ptr)
|
|
||||||
if cache_key not in self._blob_cache:
|
|
||||||
self._blob_cache[cache_key] = Tensor.from_blob(ptr, (yuv_size,), dtype='uint8')
|
|
||||||
self.full_frames[key] = self._blob_cache[cache_key]
|
|
||||||
for key in bufs.keys():
|
|
||||||
self.transforms_np[key][:,:] = transforms[key][:,:]
|
|
||||||
|
|
||||||
out = self.update_imgs(self.img_queues['img'], self.full_frames['img'], self.transforms['img'],
|
if TICI and not USBGPU:
|
||||||
self.img_queues['big_img'], self.full_frames['big_img'], self.transforms['big_img'])
|
# The imgs tensors are backed by opencl memory, only need init once
|
||||||
self.img_queues['img'], self.img_queues['big_img'] = out[0].realize(), out[2].realize()
|
for key in imgs_cl:
|
||||||
vision_inputs = {'img': out[1], 'big_img': out[3]}
|
if key not in self.vision_inputs:
|
||||||
|
self.vision_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.vision_input_shapes[key], dtype=dtypes.uint8)
|
||||||
|
else:
|
||||||
|
for key in imgs_cl:
|
||||||
|
frame_input = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.vision_input_shapes[key])
|
||||||
|
self.vision_inputs[key] = Tensor(frame_input, dtype=dtypes.uint8).realize()
|
||||||
|
|
||||||
if prepare_only:
|
if prepare_only:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.vision_output = self.vision_run(**vision_inputs).contiguous().realize().uop.base.buffer.numpy().flatten()
|
self.vision_output = self.vision_run(**self.vision_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||||
vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices))
|
vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices))
|
||||||
|
|
||||||
self.full_input_queues.enqueue({'features_buffer': vision_outputs_dict['hidden_state'], 'desire_pulse': new_desire})
|
self.full_input_queues.enqueue({'features_buffer': vision_outputs_dict['hidden_state'], 'desire_pulse': new_desire})
|
||||||
@@ -231,8 +217,9 @@ class ModelState(ModelStateBase):
|
|||||||
self.numpy_inputs[k][:] = self.full_input_queues.get(k)[k]
|
self.numpy_inputs[k][:] = self.full_input_queues.get(k)[k]
|
||||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||||
|
|
||||||
self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy().flatten()
|
self.policy_output = self.policy_run(**self.policy_inputs).numpy().flatten()
|
||||||
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
|
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
|
||||||
|
|
||||||
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
|
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
|
||||||
if SEND_RAW_PRED:
|
if SEND_RAW_PRED:
|
||||||
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
|
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
|
||||||
@@ -249,8 +236,10 @@ def main(demo=False):
|
|||||||
config_realtime_process(7, 54)
|
config_realtime_process(7, 54)
|
||||||
|
|
||||||
st = time.monotonic()
|
st = time.monotonic()
|
||||||
cloudlog.warning("loading model")
|
cloudlog.warning("setting up CL context")
|
||||||
model = ModelState()
|
cl_context = CLContext()
|
||||||
|
cloudlog.warning("CL context ready; loading model")
|
||||||
|
model = ModelState(cl_context)
|
||||||
cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting")
|
cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting")
|
||||||
|
|
||||||
# visionipc clients
|
# visionipc clients
|
||||||
@@ -263,8 +252,8 @@ def main(demo=False):
|
|||||||
time.sleep(.1)
|
time.sleep(.1)
|
||||||
|
|
||||||
vipc_client_main_stream = VisionStreamType.VISION_STREAM_WIDE_ROAD if main_wide_camera else VisionStreamType.VISION_STREAM_ROAD
|
vipc_client_main_stream = VisionStreamType.VISION_STREAM_WIDE_ROAD if main_wide_camera else VisionStreamType.VISION_STREAM_ROAD
|
||||||
vipc_client_main = VisionIpcClient("camerad", vipc_client_main_stream, True)
|
vipc_client_main = VisionIpcClient("camerad", vipc_client_main_stream, True, cl_context)
|
||||||
vipc_client_extra = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD, False)
|
vipc_client_extra = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD, False, cl_context)
|
||||||
cloudlog.warning(f"vision stream set up, main_wide_camera: {main_wide_camera}, use_extra_client: {use_extra_client}")
|
cloudlog.warning(f"vision stream set up, main_wide_camera: {main_wide_camera}, use_extra_client: {use_extra_client}")
|
||||||
|
|
||||||
while not vipc_client_main.connect(False):
|
while not vipc_client_main.connect(False):
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#include "selfdrive/modeld/models/commonmodel.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "common/clutil.h"
|
||||||
|
|
||||||
|
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context, int _temporal_skip) : ModelFrame(device_id, context) {
|
||||||
|
input_frames = std::make_unique<uint8_t[]>(buf_size);
|
||||||
|
temporal_skip = _temporal_skip;
|
||||||
|
input_frames_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size, NULL, &err));
|
||||||
|
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, (temporal_skip+1)*frame_size_bytes, NULL, &err));
|
||||||
|
region.origin = temporal_skip * frame_size_bytes;
|
||||||
|
region.size = frame_size_bytes;
|
||||||
|
last_img_cl = CL_CHECK_ERR(clCreateSubBuffer(img_buffer_20hz_cl, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err));
|
||||||
|
|
||||||
|
loadyuv_init(&loadyuv, context, device_id, MODEL_WIDTH, MODEL_HEIGHT);
|
||||||
|
init_transform(device_id, context, MODEL_WIDTH, MODEL_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_mem* DrivingModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
|
||||||
|
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
|
||||||
|
|
||||||
|
for (int i = 0; i < temporal_skip; i++) {
|
||||||
|
CL_CHECK(clEnqueueCopyBuffer(q, img_buffer_20hz_cl, img_buffer_20hz_cl, (i+1)*frame_size_bytes, i*frame_size_bytes, frame_size_bytes, 0, nullptr, nullptr));
|
||||||
|
}
|
||||||
|
loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, last_img_cl);
|
||||||
|
|
||||||
|
copy_queue(&loadyuv, q, img_buffer_20hz_cl, input_frames_cl, 0, 0, frame_size_bytes);
|
||||||
|
copy_queue(&loadyuv, q, last_img_cl, input_frames_cl, 0, frame_size_bytes, frame_size_bytes);
|
||||||
|
|
||||||
|
// NOTE: Since thneed is using a different command queue, this clFinish is needed to ensure the image is ready.
|
||||||
|
clFinish(q);
|
||||||
|
return &input_frames_cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrivingModelFrame::~DrivingModelFrame() {
|
||||||
|
deinit_transform();
|
||||||
|
loadyuv_destroy(&loadyuv);
|
||||||
|
CL_CHECK(clReleaseMemObject(input_frames_cl));
|
||||||
|
CL_CHECK(clReleaseMemObject(img_buffer_20hz_cl));
|
||||||
|
CL_CHECK(clReleaseMemObject(last_img_cl));
|
||||||
|
CL_CHECK(clReleaseCommandQueue(q));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MonitoringModelFrame::MonitoringModelFrame(cl_device_id device_id, cl_context context) : ModelFrame(device_id, context) {
|
||||||
|
input_frames = std::make_unique<uint8_t[]>(buf_size);
|
||||||
|
input_frame_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size, NULL, &err));
|
||||||
|
|
||||||
|
init_transform(device_id, context, MODEL_WIDTH, MODEL_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
cl_mem* MonitoringModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
|
||||||
|
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
|
||||||
|
clFinish(q);
|
||||||
|
return &y_cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitoringModelFrame::~MonitoringModelFrame() {
|
||||||
|
deinit_transform();
|
||||||
|
CL_CHECK(clReleaseMemObject(input_frame_cl));
|
||||||
|
CL_CHECK(clReleaseCommandQueue(q));
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenCL/cl.h>
|
||||||
|
#else
|
||||||
|
#include <CL/cl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common/mat.h"
|
||||||
|
#include "selfdrive/modeld/transforms/loadyuv.h"
|
||||||
|
#include "selfdrive/modeld/transforms/transform.h"
|
||||||
|
|
||||||
|
class ModelFrame {
|
||||||
|
public:
|
||||||
|
ModelFrame(cl_device_id device_id, cl_context context) {
|
||||||
|
q = CL_CHECK_ERR(clCreateCommandQueue(context, device_id, 0, &err));
|
||||||
|
}
|
||||||
|
virtual ~ModelFrame() {}
|
||||||
|
virtual cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) { return NULL; }
|
||||||
|
uint8_t* buffer_from_cl(cl_mem *in_frames, int buffer_size) {
|
||||||
|
CL_CHECK(clEnqueueReadBuffer(q, *in_frames, CL_TRUE, 0, buffer_size, input_frames.get(), 0, nullptr, nullptr));
|
||||||
|
clFinish(q);
|
||||||
|
return &input_frames[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int MODEL_WIDTH;
|
||||||
|
int MODEL_HEIGHT;
|
||||||
|
int MODEL_FRAME_SIZE;
|
||||||
|
int buf_size;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
cl_mem y_cl, u_cl, v_cl;
|
||||||
|
Transform transform;
|
||||||
|
cl_command_queue q;
|
||||||
|
std::unique_ptr<uint8_t[]> input_frames;
|
||||||
|
|
||||||
|
void init_transform(cl_device_id device_id, cl_context context, int model_width, int model_height) {
|
||||||
|
y_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, model_width * model_height, NULL, &err));
|
||||||
|
u_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, (model_width / 2) * (model_height / 2), NULL, &err));
|
||||||
|
v_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, (model_width / 2) * (model_height / 2), NULL, &err));
|
||||||
|
transform_init(&transform, context, device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_transform() {
|
||||||
|
transform_destroy(&transform);
|
||||||
|
CL_CHECK(clReleaseMemObject(v_cl));
|
||||||
|
CL_CHECK(clReleaseMemObject(u_cl));
|
||||||
|
CL_CHECK(clReleaseMemObject(y_cl));
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_transform(cl_mem yuv_cl, int model_width, int model_height, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
|
||||||
|
transform_queue(&transform, q,
|
||||||
|
yuv_cl, frame_width, frame_height, frame_stride, frame_uv_offset,
|
||||||
|
y_cl, u_cl, v_cl, model_width, model_height, projection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DrivingModelFrame : public ModelFrame {
|
||||||
|
public:
|
||||||
|
DrivingModelFrame(cl_device_id device_id, cl_context context, int _temporal_skip);
|
||||||
|
~DrivingModelFrame();
|
||||||
|
cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection);
|
||||||
|
|
||||||
|
const int MODEL_WIDTH = 512;
|
||||||
|
const int MODEL_HEIGHT = 256;
|
||||||
|
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
|
||||||
|
const int buf_size = MODEL_FRAME_SIZE * 2; // 2 frames are temporal_skip frames apart
|
||||||
|
const size_t frame_size_bytes = MODEL_FRAME_SIZE * sizeof(uint8_t);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LoadYUVState loadyuv;
|
||||||
|
cl_mem img_buffer_20hz_cl, last_img_cl, input_frames_cl;
|
||||||
|
cl_buffer_region region;
|
||||||
|
int temporal_skip;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MonitoringModelFrame : public ModelFrame {
|
||||||
|
public:
|
||||||
|
MonitoringModelFrame(cl_device_id device_id, cl_context context);
|
||||||
|
~MonitoringModelFrame();
|
||||||
|
cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection);
|
||||||
|
|
||||||
|
const int MODEL_WIDTH = 1440;
|
||||||
|
const int MODEL_HEIGHT = 960;
|
||||||
|
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT;
|
||||||
|
const int buf_size = MODEL_FRAME_SIZE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
cl_mem input_frame_cl;
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user