mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-23 17:33:50 +08:00
Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 75df5c1716 | |||
| 8d1a848545 | |||
| 75b4e61913 | |||
| c38b82fa9c | |||
| 72d2d5381d | |||
| 5ad091ad55 | |||
| 02c072b0a1 | |||
| a697daa669 | |||
| 13c8234c96 | |||
| 126ea769f8 | |||
| a4e8a4b67d | |||
| 70eed928e3 | |||
| 2f454eb107 | |||
| 6f009df11a | |||
| 73485b3b0f | |||
| f43fa96a8d | |||
| 9715151cc9 | |||
| b244f5d0ae | |||
| 68bf6d8162 | |||
| 8b472291d0 | |||
| 797c8d4293 | |||
| 7568528507 | |||
| 6665c3acf6 | |||
| 3b13eb4d0d | |||
| f7eb4c2561 | |||
| be9255358c | |||
| f48ccb5d7a | |||
| 99559d6749 | |||
| 4b6f0ffb46 | |||
| 55415382ce | |||
| 9f30901eb6 | |||
| d2045d24fb | |||
| 6488a11e4d | |||
| 7b22b65313 | |||
| b59a50101e | |||
| 3e30962bd3 | |||
| 3274043063 | |||
| c624550d2e | |||
| 6759038671 | |||
| aa636e75c8 | |||
| 5ff3c3bd8d | |||
| 1e11b52290 | |||
| 76e0204025 | |||
| cc88f2cbd6 | |||
| ee0ac199a4 | |||
| d5ed828eaa | |||
| 9fca585f2a | |||
| bb1259303e | |||
| e135051ca8 | |||
| 12bef55d8a | |||
| 2f7a45e6c8 | |||
| 936ebfc12b | |||
| aa0c9dc0eb | |||
| 7476a866e7 | |||
| 610d857e33 | |||
| d2f47407d0 | |||
| db75ec76ea | |||
| 24066465d7 | |||
| a7abbd6e25 | |||
| 878982447c | |||
| 576527a36b | |||
| ef8c35da24 | |||
| 85688b1040 | |||
| 0fb2199130 | |||
| d48d756c1d | |||
| 2ed298a0c9 | |||
| d68f038949 | |||
| 7231571e57 | |||
| b37f1419d3 | |||
| cd85a66790 | |||
| 305ea87daf | |||
| 4bbfc793e0 | |||
| d5d983676e | |||
| de8a96a398 | |||
| 0cbf45f699 | |||
| 0d68a3a2ab | |||
| 9e85a85059 | |||
| 0373c327c0 | |||
| efe9e5c200 | |||
| 8a249a45dc | |||
| bdbefe67f6 | |||
| 675bb166ad | |||
| 1b717a7e88 | |||
| 86f55a8ba9 | |||
| 629392d2f7 | |||
| bc414bdc8b | |||
| 7ca5649f2c | |||
| 641ee8fa87 | |||
| 56c276158c | |||
| c65308a8bd | |||
| 994e526460 | |||
| 1defae36b7 | |||
| 8f029fd0ef | |||
| ddb46284dc | |||
| 9effc754d9 | |||
| e49ffc2a2d | |||
| 2cacd0b3e5 | |||
| c4b8859dff | |||
| 8fb0953205 | |||
| 63d1c8835f | |||
| 17a185606d | |||
| da10131392 | |||
| 7107c2ba14 | |||
| 95b6e877ac | |||
| eb02c6570e | |||
| 1be8ae31c4 | |||
| 04dcd38856 | |||
| 22ccf0d72f | |||
| 3c969bb627 | |||
| 20f8011feb | |||
| 9cf17e74a1 | |||
| 2c4efdf557 | |||
| 4cd3d3c16c | |||
| 637f3ae9c8 | |||
| 464ee80f71 | |||
| 2743a04613 | |||
| 7f9978d001 | |||
| 4b83961c67 | |||
| c00eaf428a | |||
| 0a9993e8d4 | |||
| 0af214a985 | |||
| af43385e3a | |||
| 0ab2b8c590 | |||
| 67ab18a0de | |||
| e87dc15b30 | |||
| 192d08516c | |||
| 3cf001c59c | |||
| f2ccd021da | |||
| c9fc900f64 | |||
| 3c37c5ce5d | |||
| 7c45889e4e | |||
| 2aabb7aee8 | |||
| 3859e9962f | |||
| 810efbab72 | |||
| ec27bec326 | |||
| 250d553157 | |||
| cea54a0ca8 | |||
| 8e72d783bd | |||
| 1b0dc103dc | |||
| 6c364d292b | |||
| bcdec2ce84 | |||
| 3deaeb3759 | |||
| c669f0984a | |||
| 46dd946740 | |||
| 9da4b3653e | |||
| 4e21ae7c50 | |||
| bb91e92237 | |||
| 14b4c4f85b | |||
| 0660b542c3 | |||
| 2b893b90c9 | |||
| f5139178ed | |||
| fb43b755f2 | |||
| 07f5b967d8 | |||
| ea19c7d3bb | |||
| e461842cbb | |||
| a73c9659d5 | |||
| cb796fbc76 | |||
| 6bf75fc557 | |||
| 9a1fc28819 | |||
| 0741d05e92 | |||
| 1ad008107d | |||
| feebd9df93 | |||
| c2e5ced3e5 | |||
| 15e5d2efb9 | |||
| a3929d0b54 | |||
| 794f8f9991 | |||
| 68fa5e3f21 | |||
| 86c6cc1f48 | |||
| eb7ffbf093 | |||
| 3919095752 | |||
| 74d63be1c3 | |||
| 8894486a1a | |||
| 810599315d | |||
| 6f3ab810c8 | |||
| 230f78b8d3 | |||
| f1affec088 | |||
| 97d8ef242c | |||
| a63fff9b45 | |||
| cb3893daaa | |||
| 29f60df74b | |||
| c6c072e1f4 | |||
| d101cbb83e | |||
| 1536d59633 | |||
| dc99b865ae | |||
| e59bc027ff | |||
| cf7e5efaca | |||
| 4b44f2eb31 | |||
| 107d2ab400 | |||
| 5432d9062c | |||
| f533f6c843 | |||
| 58e9ac763c | |||
| cb50d54169 | |||
| bd5de4ed0a | |||
| 0d4073fadb | |||
| ebc70dcb52 | |||
| 4d0426999e | |||
| 286da42573 | |||
| 8a836710a9 | |||
| 5d515bcf33 | |||
| 1c7f6d5133 | |||
| 05d57c7aeb | |||
| e4b0eaf352 | |||
| a710276472 | |||
| af086db671 | |||
| 0d9eb0e25e | |||
| 0616caed6d | |||
| 095337b3c1 | |||
| 1edec2d22c | |||
| affabb9ee0 | |||
| dc27e8711c | |||
| cf7329a264 | |||
| 5ee5ecd820 | |||
| b064f730dd |
@@ -211,8 +211,6 @@ jobs:
|
||||
needs: [ prepare_strategy ]
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ needs.prepare_strategy.outputs.include_big_model == 'true' }}
|
||||
outputs:
|
||||
onnx_sha256: ${{ steps.resolve.outputs.onnx_sha256 }}
|
||||
env:
|
||||
HF_REPO: sunnypilot/sunnypilot_models_v1
|
||||
HF_DEFAULTS_PATH: models/defaults/big
|
||||
@@ -228,7 +226,6 @@ jobs:
|
||||
run: |
|
||||
ACTUAL_ONNX_HASH=$(sha256sum "openpilot/selfdrive/modeld/models/big_driving_supercombo.onnx" | cut -d' ' -f1)
|
||||
echo "Repo ONNX hash: $ACTUAL_ONNX_HASH"
|
||||
echo "onnx_sha256=$ACTUAL_ONNX_HASH" >> $GITHUB_OUTPUT
|
||||
|
||||
JSON_URL="https://huggingface.co/datasets/${HF_REPO}/resolve/main/${HF_DEFAULTS_PATH}/default_models.json"
|
||||
|
||||
@@ -270,6 +267,36 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Download big model chunks
|
||||
run: |
|
||||
ACTUAL_ONNX_HASH=$(sha256sum "openpilot/selfdrive/modeld/models/big_driving_supercombo.onnx" | cut -d' ' -f1)
|
||||
JSON_URL="https://huggingface.co/datasets/${HF_REPO}/resolve/main/${HF_DEFAULTS_PATH}/default_models.json"
|
||||
DEFAULTS=$(curl -fsSL "$JSON_URL")
|
||||
BUNDLE=$(echo "$DEFAULTS" | jq --arg hash "$ACTUAL_ONNX_HASH" '.bundles[] | select(.onnx_sha256 == $hash)')
|
||||
|
||||
mkdir -p big_model_chunks
|
||||
ARTIFACT=$(echo "$BUNDLE" | jq -r '.models[0].artifact')
|
||||
BASE_URL=$(echo "$ARTIFACT" | jq -r '.download_uri.url' | sed 's|/[^/]*$||')
|
||||
NUM_CHUNKS=$(echo "$ARTIFACT" | jq -r '.chunks | length')
|
||||
|
||||
CANONICAL="big_driving_tinygrad.pkl"
|
||||
echo "$ARTIFACT" | jq -r '.chunks[].file_name' | while read CHUNK_NAME; do
|
||||
CHUNK_IDX=$(echo "$CHUNK_NAME" | grep -oP 'chunk\K[0-9]+of[0-9]+')
|
||||
CANONICAL_CHUNK="${CANONICAL}.chunk${CHUNK_IDX}"
|
||||
ENCODED_URL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BASE_URL}/${CHUNK_NAME}', safe=':/'))")
|
||||
echo "Downloading $CHUNK_NAME -> $CANONICAL_CHUNK"
|
||||
curl -fsSL -o "big_model_chunks/${CANONICAL_CHUNK}" "$ENCODED_URL"
|
||||
done
|
||||
|
||||
echo "$NUM_CHUNKS" > "big_model_chunks/${CANONICAL}.chunkmanifest"
|
||||
|
||||
- name: Upload big model chunks
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: big-model-chunks
|
||||
path: big_model_chunks/
|
||||
compression-level: 0
|
||||
|
||||
- name: Cancel run on failure
|
||||
if: failure()
|
||||
run: gh run cancel ${{ github.run_id }}
|
||||
@@ -312,32 +339,12 @@ jobs:
|
||||
mkdir -p "${{ github.workspace }}/chestnut_output"
|
||||
tar xzf prebuilt.tar.gz -C "${{ github.workspace }}/chestnut_output"
|
||||
|
||||
- name: Download big model chunks from HF
|
||||
- name: Download big model chunks
|
||||
if: ${{ needs.prepare_chestnut.result == 'success' }}
|
||||
env:
|
||||
HF_REPO: sunnypilot/sunnypilot_models_v1
|
||||
HF_DEFAULTS_PATH: models/defaults/big
|
||||
run: |
|
||||
ONNX_HASH="${{ needs.prepare_chestnut.outputs.onnx_sha256 }}"
|
||||
JSON_URL="https://huggingface.co/datasets/${HF_REPO}/resolve/main/${HF_DEFAULTS_PATH}/default_models.json"
|
||||
DEFAULTS=$(curl -fsSL "$JSON_URL")
|
||||
BUNDLE=$(echo "$DEFAULTS" | jq --arg hash "$ONNX_HASH" '.bundles[] | select(.onnx_sha256 == $hash)')
|
||||
|
||||
mkdir -p big_model_chunks
|
||||
ARTIFACT=$(echo "$BUNDLE" | jq -r '.models[0].artifact')
|
||||
BASE_URL=$(echo "$ARTIFACT" | jq -r '.download_uri.url' | sed 's|/[^/]*$||')
|
||||
NUM_CHUNKS=$(echo "$ARTIFACT" | jq -r '.chunks | length')
|
||||
|
||||
CANONICAL="big_driving_tinygrad.pkl"
|
||||
echo "$ARTIFACT" | jq -r '.chunks[].file_name' | while read CHUNK_NAME; do
|
||||
CHUNK_IDX=$(echo "$CHUNK_NAME" | grep -oP 'chunk\K[0-9]+of[0-9]+')
|
||||
CANONICAL_CHUNK="${CANONICAL}.chunk${CHUNK_IDX}"
|
||||
ENCODED_URL=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BASE_URL}/${CHUNK_NAME}', safe=':/'))")
|
||||
echo "Downloading $CHUNK_NAME -> $CANONICAL_CHUNK"
|
||||
curl -fsSL -o "big_model_chunks/${CANONICAL_CHUNK}" "$ENCODED_URL"
|
||||
done
|
||||
|
||||
echo "$NUM_CHUNKS" > "big_model_chunks/${CANONICAL}.chunkmanifest"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: big-model-chunks
|
||||
path: big_model_chunks
|
||||
|
||||
- name: Inject big model into chestnut
|
||||
if: ${{ needs.prepare_chestnut.result == 'success' }}
|
||||
|
||||
+1
-1
Submodule opendbc_repo updated: 06743dfb39...0819b0e8e0
@@ -132,6 +132,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"UsbGpuLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"UsbGpuLoadProgress", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, INT, "0"}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
|
||||
// --- sunnypilot params --- //
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
from openpilot.common.file_chunker import open_file_chunked, get_existing_chunks
|
||||
from openpilot.common.params import Params
|
||||
|
||||
PARAM = "UsbGpuLoadProgress"
|
||||
|
||||
|
||||
class ProgressReader:
|
||||
def __init__(self, inner, total):
|
||||
self._inner = inner
|
||||
self._total = total
|
||||
self._params = Params()
|
||||
self._read = 0
|
||||
self._pct = -1
|
||||
self._step = max(64 * 1024, total // 100)
|
||||
|
||||
def _bump(self, n):
|
||||
self._read += n
|
||||
if self._total:
|
||||
pct = min(100, self._read * 100 // self._total)
|
||||
if pct != self._pct:
|
||||
self._pct = pct
|
||||
self._params.put(PARAM, pct)
|
||||
|
||||
def read(self, size=-1):
|
||||
data = self._inner.read(size)
|
||||
self._bump(len(data))
|
||||
return data
|
||||
|
||||
def readinto(self, b):
|
||||
view = memoryview(b)
|
||||
done = 0
|
||||
while done < len(view):
|
||||
n = self._inner.readinto(view[done:done + self._step])
|
||||
if not n:
|
||||
break
|
||||
done += n
|
||||
self._bump(n)
|
||||
return done
|
||||
|
||||
|
||||
def open_with_progress(pkl_path):
|
||||
total = sum(os.path.getsize(p) for p in get_existing_chunks(pkl_path))
|
||||
return ProgressReader(open_file_chunked(pkl_path), total)
|
||||
@@ -29,6 +29,7 @@ from openpilot.selfdrive.modeld.parse_model_outputs import Parser
|
||||
from openpilot.selfdrive.modeld.compile_modeld import make_input_queues, WARP_INPUTS, POLICY_INPUTS
|
||||
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_driving_model_data, fill_pose_msg, PublishState
|
||||
from openpilot.common.file_chunker import open_file_chunked
|
||||
from openpilot.selfdrive.modeld.load_progress import open_with_progress
|
||||
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
||||
from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled, modeld_pkl_path, get_tg_input_devices, load_oob
|
||||
|
||||
@@ -145,7 +146,7 @@ class ModelState(ModelStateBase):
|
||||
ModelStateBase.__init__(self)
|
||||
input_devices = get_tg_input_devices(PROCESS_NAME, usbgpu)
|
||||
self.WARP_DEV, self.QUEUE_DEV = input_devices['WARP_DEV'], input_devices['QUEUE_DEV']
|
||||
jits = load_oob(open_file_chunked(modeld_pkl_path(usbgpu)))
|
||||
jits = load_oob(open_with_progress(modeld_pkl_path(usbgpu)) if usbgpu else open_file_chunked(modeld_pkl_path(usbgpu)))
|
||||
metadata = jits['metadata']
|
||||
self.input_shapes = metadata['input_shapes']
|
||||
self.vision_input_names = [k for k in self.input_shapes if 'img' in k]
|
||||
|
||||
@@ -223,7 +223,7 @@ class HudRenderer(Widget):
|
||||
if icon is not self._egpu_icon:
|
||||
self._egpu_fade_time = rl.get_time()
|
||||
self._egpu_icon = icon
|
||||
alpha = self._egpu_alpha_filter.update(loading or 0 < rl.get_time() - self._egpu_fade_time < SET_SPEED_PERSISTENCE)
|
||||
alpha = self._egpu_alpha_filter.update(True)
|
||||
if alpha < 1e-2:
|
||||
return
|
||||
|
||||
@@ -231,6 +231,18 @@ class HudRenderer(Widget):
|
||||
rect.y + rect.height - 14 - (self._txt_wheel.height + icon.height) / 2)
|
||||
rl.draw_texture_ex(icon, pos, 0.0, 1.0, rl.Color(255, 255, 255, int(255 * opacity * alpha)))
|
||||
|
||||
if loading:
|
||||
pct_text = f"{ui_state.usbgpu_load_progress}%"
|
||||
size = FONT_SIZES.max_speed
|
||||
cell = measure_text_cached(self._font_bold, "0", size)
|
||||
widths = [cell.x if c.isdigit() else measure_text_cached(self._font_bold, c, size).x for c in pct_text]
|
||||
x = pos.x - 8 - sum(widths)
|
||||
y = pos.y + (icon.height - cell.y) / 2
|
||||
for c, w in zip(pct_text, widths):
|
||||
glyph = measure_text_cached(self._font_bold, c, size).x
|
||||
rl.draw_text_ex(self._font_bold, c, rl.Vector2(x + (w - glyph) / 2, y), size, 0, rl.WHITE)
|
||||
x += w
|
||||
|
||||
def _draw_steering_wheel(self, rect: rl.Rectangle) -> None:
|
||||
wheel_txt = self._txt_wheel_critical if self._show_wheel_critical else self._txt_wheel
|
||||
|
||||
|
||||
@@ -178,14 +178,27 @@ class ModelsLayout(Widget):
|
||||
# circled_slash is authored grey; tinting it again only darkens it
|
||||
return {"name": name, "text_color": rl.GRAY, "icon": "icons/circled_slash.png", "icon_color": rl.WHITE}
|
||||
|
||||
@staticmethod
|
||||
def _show_reset_params_dialog():
|
||||
def _callback(response):
|
||||
if response == DialogResult.CONFIRM:
|
||||
ui_state.params.remove("CalibrationParams")
|
||||
ui_state.params.remove("LiveTorqueParameters")
|
||||
msg = tr("Model download has started in the background. We suggest resetting calibration. Would you like to do that now?")
|
||||
dialog = ConfirmDialog(msg, tr("Reset Calibration"), callback=_callback)
|
||||
gui_app.push_widget(dialog)
|
||||
|
||||
def _on_model_selected(self, result):
|
||||
if result != DialogResult.CONFIRM:
|
||||
return
|
||||
selected_ref = self.model_dialog.selection_ref
|
||||
if selected_ref == "Default":
|
||||
ui_state.params.remove("ModelManager_ActiveBundle")
|
||||
self._show_reset_params_dialog()
|
||||
elif selected_bundle := next((bundle for bundle in self.model_manager.availableBundles if bundle.ref == selected_ref), None):
|
||||
ui_state.params.put("ModelManager_DownloadIndex", selected_bundle.index)
|
||||
if self.model_manager.activeBundle and selected_bundle.generation != self.model_manager.activeBundle.generation:
|
||||
self._show_reset_params_dialog()
|
||||
self.model_dialog = None
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -8,7 +8,6 @@ from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.chevron_metrics import ChevronMetrics
|
||||
from openpilot.selfdrive.ui.sunnypilot.onroad.rainbow_path import RainbowPath
|
||||
from openpilot.selfdrive.ui.sunnypilot.ui_state import MADSState
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
|
||||
|
||||
@@ -20,11 +19,6 @@ class ModelRendererSP:
|
||||
|
||||
@property
|
||||
def _lateral_active(self) -> bool:
|
||||
sm = ui_state.sm
|
||||
if sm.valid["selfdriveStateSP"]:
|
||||
mads = sm["selfdriveStateSP"].mads
|
||||
if mads.available:
|
||||
return mads.enabled and mads.state != MADSState.paused
|
||||
return ui_state.status in (UIStatus.ENGAGED, UIStatus.LAT_ONLY)
|
||||
|
||||
def _get_path_half_width(self) -> float:
|
||||
|
||||
@@ -86,6 +86,7 @@ class UIState(UIStateSP):
|
||||
self.usbgpu_compiled: bool = usbgpu_compiled()
|
||||
self.usbgpu_active: bool | None = self.params.get("UsbGpuActive")
|
||||
self.usbgpu_loading: bool = self.params.get_bool("UsbGpuLoading")
|
||||
self.usbgpu_load_progress: int = self.params.get("UsbGpuLoadProgress", return_default=True)
|
||||
self.started: bool = False
|
||||
self.ignition: bool = False
|
||||
self.recording_audio: bool = False
|
||||
@@ -164,6 +165,9 @@ class UIState(UIStateSP):
|
||||
# Update started state
|
||||
self.started = self.sm["deviceState"].started and self.ignition
|
||||
|
||||
if self.usbgpu_loading:
|
||||
self.usbgpu_load_progress = self.params.get("UsbGpuLoadProgress", return_default=True)
|
||||
|
||||
# Update body state
|
||||
if self.CP is not None and self.is_body != self.CP.notCar:
|
||||
self.is_body = self.CP.notCar
|
||||
|
||||
@@ -7,7 +7,6 @@ See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
import tempfile
|
||||
import time
|
||||
@@ -67,15 +66,14 @@ def get_policy_npy_shapes(input_shapes: dict, is_supercombo: bool = False) -> tu
|
||||
if desire_key:
|
||||
shapes['desire'] = (input_shapes[desire_key][2],)
|
||||
|
||||
if is_supercombo and 'features_buffer' in input_shapes:
|
||||
fb = input_shapes['features_buffer']
|
||||
shapes['prev_feat'] = (fb[0], fb[2])
|
||||
|
||||
for key, shape in input_shapes.items():
|
||||
if key not in (desire_key, 'features_buffer') and 'img' not in key:
|
||||
shapes[key] = tuple(shape)
|
||||
|
||||
if is_supercombo and 'features_buffer' in input_shapes:
|
||||
fb = input_shapes['features_buffer']
|
||||
feat_dim = math.prod(fb[2:])
|
||||
shapes['prev_feat'] = (fb[0], feat_dim)
|
||||
|
||||
sizes = [int(np.prod(size)) for size in shapes.values()]
|
||||
return shapes, sizes
|
||||
|
||||
@@ -119,9 +117,8 @@ def generate_queues_and_npy(input_shapes: dict, frame_skip: int, device: str = D
|
||||
}
|
||||
|
||||
if features_buffer:
|
||||
feat_dim = math.prod(features_buffer[2:])
|
||||
feat_q_len = frame_skip * features_buffer[1] if is_supercombo else frame_skip * (features_buffer[1] - 1) + 1
|
||||
queues['feat_q'] = Tensor(np.zeros((feat_q_len, features_buffer[0], feat_dim),
|
||||
queues['feat_q'] = Tensor(np.zeros((feat_q_len, features_buffer[0], features_buffer[2]),
|
||||
dtype=np.float32), device=device).contiguous().realize()
|
||||
|
||||
queues.update({key: Tensor(value, device='NPY').realize() for key, value in npy_arrays.items() if key in ('tfm', 'big_tfm')})
|
||||
@@ -186,14 +183,14 @@ def make_run_policy(vision_runner, policy_runners: list, features_slice: slice,
|
||||
warped_dev = warped.to(Device.DEFAULT)
|
||||
Tensor.realize(packed_npy_inputs_dev, warped_dev)
|
||||
|
||||
img = shift_and_sample(img_q, warped_dev[0:1], sample_skip_fn)
|
||||
big_img = shift_and_sample(big_img_q, warped_dev[1:2], sample_skip_fn)
|
||||
img = shift_and_sample(img_q, warped_dev[0:1], sample_skip_fn).realize()
|
||||
big_img = shift_and_sample(big_img_q, warped_dev[1:2], sample_skip_fn).realize()
|
||||
|
||||
unpacked_tensors = [tensor.reshape(shape) for tensor, shape in zip(packed_npy_inputs_dev.split(npy_sizes), npy_shapes.values(), strict=True)]
|
||||
unpacked_dict = dict(zip(npy_shapes.keys(), unpacked_tensors, strict=True))
|
||||
|
||||
desire_dev = unpacked_dict['desire']
|
||||
desire_buf = shift_and_sample(desire_q, desire_dev.reshape(1, 1, -1), sample_desire_fn)
|
||||
desire_buf = shift_and_sample(desire_q, desire_dev.reshape(1, 1, -1), sample_desire_fn).realize()
|
||||
|
||||
inputs = {desire_key: desire_buf}
|
||||
for key, tensor_val in unpacked_dict.items():
|
||||
@@ -202,22 +199,19 @@ def make_run_policy(vision_runner, policy_runners: list, features_slice: slice,
|
||||
|
||||
if 'prev_feat' in unpacked_dict:
|
||||
prev_feat_dev = unpacked_dict['prev_feat']
|
||||
feat_buf = shift_and_sample(feat_q, prev_feat_dev.reshape(1, 1, -1), sample_skip_fn)
|
||||
inputs['features_buffer'] = feat_buf if len(fb := input_shapes['features_buffer']) <= 3 else feat_buf.reshape(fb)
|
||||
inputs['features_buffer'] = shift_and_sample(feat_q, prev_feat_dev.reshape(1, 1, -1), sample_skip_fn).realize()
|
||||
|
||||
if vision_runner:
|
||||
vision_out_cast = next(iter(vision_runner({road_key: img, wide_key: big_img}).values())).cast('float32').realize()
|
||||
if 'features_buffer' not in inputs:
|
||||
new_feat = vision_out_cast[:, features_slice].reshape(1, -1).unsqueeze(0)
|
||||
feat_buf = shift_and_sample(feat_q, new_feat, sample_skip_fn).realize()
|
||||
inputs['features_buffer'] = feat_buf if len(fb := input_shapes['features_buffer']) <= 3 else feat_buf.reshape(fb)
|
||||
inputs['features_buffer'] = shift_and_sample(feat_q, new_feat, sample_skip_fn).realize()
|
||||
policy_outs = [next(iter(pol_runner(inputs).values())).cast('float32').realize() for pol_runner in policy_runners]
|
||||
return (vision_out_cast, *policy_outs) if len(policy_outs) > 1 else (vision_out_cast, policy_outs[0])
|
||||
|
||||
inputs.update({road_key: img, wide_key: big_img})
|
||||
if 'features_buffer' not in inputs:
|
||||
feat_buf = sample_skip_fn(feat_q)
|
||||
inputs['features_buffer'] = feat_buf if len(fb := input_shapes['features_buffer']) <= 3 else feat_buf.reshape(fb)
|
||||
inputs['features_buffer'] = sample_skip_fn(feat_q)
|
||||
|
||||
policy_out = next(iter(policy_runners[0](inputs).values())).cast('float32').realize()
|
||||
if 'features_buffer' not in inputs and features_slice is not None:
|
||||
|
||||
@@ -25,6 +25,7 @@ from opendbc.car.car_helpers import get_demo_car_params
|
||||
from tinygrad.tensor import Tensor
|
||||
|
||||
from openpilot.common.file_chunker import open_file_chunked
|
||||
from openpilot.selfdrive.modeld.load_progress import open_with_progress
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
@@ -107,7 +108,7 @@ class ModelState(ModelStateBase):
|
||||
|
||||
def _init_combined(self, pkl_path, cam_w, cam_h, bundle):
|
||||
cloudlog.warning(f"loading combined pkl: {pkl_path}")
|
||||
jits = load_oob(open_file_chunked(pkl_path))
|
||||
jits = load_oob(open_with_progress(pkl_path) if self.usbgpu else open_file_chunked(pkl_path))
|
||||
|
||||
self.WARP_DEV = 'QCOM' if COMMA_HARDWARE else 'CPU'
|
||||
self.DEV = 'AMD' if self.usbgpu else self.WARP_DEV
|
||||
|
||||
@@ -195,85 +195,3 @@ class TestReadFileChunkedToDisk(OpenpilotTestCase):
|
||||
|
||||
assert out.parent == Path(d)
|
||||
assert out.read_bytes() == payload
|
||||
|
||||
|
||||
class Test4DFeaturesBuffer(OpenpilotTestCase):
|
||||
def test_get_policy_npy_shapes_4d(self):
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import get_policy_npy_shapes
|
||||
input_shapes = {
|
||||
'desire_pulse': (1, 25, 8),
|
||||
'features_buffer': (1, 24, 32, 512), # compare 4d to 3d for regression
|
||||
'traffic_convention': (1, 2),
|
||||
'action_t': (1, 2)
|
||||
}
|
||||
shapes, sizes = get_policy_npy_shapes(input_shapes, is_supercombo=True)
|
||||
assert shapes['prev_feat'] == (1, 16384)
|
||||
assert sizes == [8, 2, 2, 16384]
|
||||
|
||||
def test_get_policy_npy_shapes_3d(self):
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import get_policy_npy_shapes
|
||||
input_shapes = {
|
||||
'desire_pulse': (1, 25, 8),
|
||||
'features_buffer': (1, 24, 512),
|
||||
'traffic_convention': (1, 2),
|
||||
'action_t': (1, 2)
|
||||
}
|
||||
shapes, sizes = get_policy_npy_shapes(input_shapes, is_supercombo=True)
|
||||
assert shapes['prev_feat'] == (1, 512)
|
||||
assert sizes == [8, 2, 2, 512]
|
||||
|
||||
|
||||
class TestStockCompileModeldEquivalence(OpenpilotTestCase):
|
||||
def test_get_policy_npy_shapes_matches_stock(self):
|
||||
from openpilot.selfdrive.modeld.compile_modeld import get_policy_npy_shapes as stock_get_policy_npy_shapes
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import get_policy_npy_shapes as sunny_get_policy_npy_shapes
|
||||
|
||||
stock_input_shapes = {
|
||||
'desire_pulse': (1, 25, 8),
|
||||
'features_buffer': (1, 24, 512), # see below comment
|
||||
'traffic_convention': (1, 2),
|
||||
'action_t': (1, 2),
|
||||
}
|
||||
|
||||
stock_shapes, stock_sizes = stock_get_policy_npy_shapes(stock_input_shapes)
|
||||
sunny_shapes, sunny_sizes = sunny_get_policy_npy_shapes(stock_input_shapes, is_supercombo=True)
|
||||
|
||||
assert sunny_shapes == stock_shapes
|
||||
assert sunny_sizes == stock_sizes
|
||||
assert sunny_shapes['prev_feat'] == (1, 512)
|
||||
|
||||
def test_make_input_queues_full_stock_equivalence(self):
|
||||
from openpilot.selfdrive.modeld.compile_modeld import make_input_queues as stock_make_input_queues
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import make_supercombo_input_queues as sunny_make_supercombo_input_queues
|
||||
input_shapes = {
|
||||
'img': (1, 12, 128, 256),
|
||||
'desire_pulse': (1, 25, 8),
|
||||
'features_buffer': (1, 24, 512), # when https://github.com/commaai/openpilot/pull/38681 merges, update to 1,24,32,512
|
||||
'traffic_convention': (1, 2),
|
||||
'action_t': (1, 2),
|
||||
}
|
||||
frame_skip = 4
|
||||
|
||||
stock_queues, stock_npy = stock_make_input_queues(input_shapes, frame_skip, device='NPY')
|
||||
sunny_queues, sunny_npy = sunny_make_supercombo_input_queues(input_shapes, frame_skip, device='NPY')
|
||||
assert set(sunny_queues.keys()) == set(stock_queues.keys())
|
||||
for key in stock_queues:
|
||||
assert sunny_queues[key].shape == stock_queues[key].shape, \
|
||||
f"Queue shape mismatch for {key}: sunny {sunny_queues[key].shape} != stock {stock_queues[key].shape}"
|
||||
assert set(sunny_npy.keys()) == set(stock_npy.keys())
|
||||
for key in stock_npy:
|
||||
assert sunny_npy[key].shape == stock_npy[key].shape, \
|
||||
f"Numpy array shape mismatch for {key}: sunny {sunny_npy[key].shape} != stock {stock_npy[key].shape}"
|
||||
|
||||
def test_make_warp_queues_stock_equivalence(self):
|
||||
from openpilot.selfdrive.modeld.compile_modeld import make_warp_input_queues as stock_make_warp_queues
|
||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import make_warp_queues as sunny_make_warp_queues
|
||||
stock_vision_shapes = {'img': (1, 12, 128, 256)} # for now?
|
||||
stock_queues, stock_npy = stock_make_warp_queues(stock_vision_shapes, frame_skip=4, device='NPY')
|
||||
sunny_queues, sunny_npy = sunny_make_warp_queues(device='NPY')
|
||||
|
||||
assert set(sunny_npy.keys()) == set(stock_npy.keys()) == {'tfm', 'big_tfm'}
|
||||
for key in sunny_npy:
|
||||
assert sunny_npy[key].shape == stock_npy[key].shape == (3, 3)
|
||||
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class ModelCache:
|
||||
class ModelFetcher:
|
||||
"""Handles fetching and caching of model data from remote source"""
|
||||
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v20.json"
|
||||
MODEL_URL_USBGPU = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_usbgpu_v22.json"
|
||||
MODEL_URL_USBGPU = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_usbgpu_v21.json"
|
||||
|
||||
def __init__(self, params: Params):
|
||||
self.params = params
|
||||
|
||||
@@ -143,17 +143,13 @@ class ModelManagerSP:
|
||||
is_cached = False
|
||||
if len(artifact.chunks) > 0:
|
||||
from openpilot.common.file_chunker import get_chunk_name
|
||||
num_chunks = len(artifact.chunks)
|
||||
chunks_valid = True
|
||||
for i, chunk in enumerate(artifact.chunks):
|
||||
chunk_path = get_chunk_name(full_path, i, num_chunks)
|
||||
chunk_path = get_chunk_name(full_path, i, len(artifact.chunks))
|
||||
if not await verify_file(chunk_path, chunk.sha256):
|
||||
chunks_valid = False
|
||||
break
|
||||
artifact.downloadProgress.progress = ((i + 1) / num_chunks) * 100
|
||||
self._sync_artifact_progress(artifact)
|
||||
self._report_status()
|
||||
if chunks_valid and num_chunks > 0:
|
||||
if chunks_valid and len(artifact.chunks) > 0:
|
||||
is_cached = True
|
||||
else:
|
||||
if await verify_file(full_path, expected_hash):
|
||||
@@ -220,9 +216,6 @@ class ModelManagerSP:
|
||||
"""Downloads all models in a bundle"""
|
||||
self.selected_bundle = model_bundle
|
||||
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.downloading
|
||||
for model in self.selected_bundle.models:
|
||||
model.artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloading
|
||||
self._report_status()
|
||||
os.makedirs(destination_path, exist_ok=True)
|
||||
|
||||
try:
|
||||
@@ -267,9 +260,7 @@ class ModelManagerSP:
|
||||
self.active_bundle = get_active_bundle(self.params)
|
||||
|
||||
if (index_to_download := self.params.get("ModelManager_DownloadIndex")) is not None:
|
||||
if self.active_bundle and self.active_bundle.index == index_to_download:
|
||||
self.params.remove("ModelManager_DownloadIndex")
|
||||
elif model_to_download := next((model for model in self.available_models if model.index == index_to_download), None):
|
||||
if model_to_download := next((model for model in self.available_models if model.index == index_to_download), None):
|
||||
try:
|
||||
self.download(model_to_download, Paths.model_root())
|
||||
except Exception as e:
|
||||
|
||||
@@ -83,14 +83,9 @@ class TestLocationdProc(OpenpilotTestCase):
|
||||
self.pm.send(msg.which(), msg)
|
||||
if msg.which() == "cameraOdometry":
|
||||
self.pm.wait_for_readers_to_update(msg.which(), timeout=1, dt=0.005)
|
||||
for _ in range(50):
|
||||
val = self.params.get('LastGPSPositionLLK')
|
||||
if val is not None:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
time.sleep(1) # wait for async params write
|
||||
|
||||
self.assertIsNotNone(val, "LastGPSPositionLLK not written within 5s")
|
||||
lastGPS = json.loads(val)
|
||||
lastGPS = json.loads(self.params.get('LastGPSPositionLLK'))
|
||||
self.assertAlmostEqual(lastGPS['latitude'], self.lat, delta=0.001)
|
||||
self.assertAlmostEqual(lastGPS['longitude'], self.lon, delta=0.001)
|
||||
self.assertAlmostEqual(lastGPS['altitude'], self.alt, delta=0.001)
|
||||
|
||||
@@ -28,8 +28,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
|
||||
create_connection, WebSocketConnectionClosedException)
|
||||
|
||||
import openpilot.cereal.messaging as messaging
|
||||
from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled
|
||||
from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MODEL
|
||||
from openpilot.sunnypilot.models.default_model import get_default_model
|
||||
from openpilot.sunnypilot.selfdrive.car.sync_sunnylink_params import update_car_list_param
|
||||
from openpilot.sunnypilot.sunnylink.api import SunnylinkApi
|
||||
from openpilot.sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
|
||||
@@ -182,10 +181,7 @@ def getParamsMetadata() -> str:
|
||||
schema = generate_schema()
|
||||
schema["capabilities"] = generate_capabilities()
|
||||
schema["capability_labels"] = CAPABILITY_LABELS
|
||||
# mirrors get_default_model() — ui_state unavailable in sunnylinkd process
|
||||
show_big = (usbgpu_present() and usbgpu_compiled()
|
||||
and (params.get_bool("UsbGpuActive") or params.get_bool("UsbGpuLoading") or params.get_bool("IsOffroad")))
|
||||
schema["default_model"] = DEFAULT_BIG_MODEL if show_big else DEFAULT_MODEL
|
||||
schema["default_model"] = get_default_model()
|
||||
raw = json.dumps(schema, separators=(",", ":")).encode("utf-8")
|
||||
return base64.b64encode(gzip.compress(raw)).decode("utf-8")
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user