diff --git a/.github/workflows/build-all-tinygrad-models.yaml b/.github/workflows/build-all-tinygrad-models.yaml index 1ba407b3cf..7bddeab2bc 100644 --- a/.github/workflows/build-all-tinygrad-models.yaml +++ b/.github/workflows/build-all-tinygrad-models.yaml @@ -8,13 +8,13 @@ on: required: true type: string target_hardware: - description: 'Hardware target to compile for (qcom or usbgpu)' + description: 'Hardware target to compile for (qcom or chestnut)' required: true type: choice default: 'qcom' options: - qcom - - usbgpu + - chestnut hf_repo: description: 'Hugging Face dataset repository' required: false @@ -59,7 +59,7 @@ jobs: id: get-json run: | cd docs/docs - PREFIX="driving_models_${{ inputs.target_hardware == 'usbgpu' && 'usbgpu_' || '' }}v" + PREFIX="driving_models_${{ inputs.target_hardware == 'chestnut' && 'chestnut_' || '' }}v" latest=$(ls ${PREFIX}*.json | sed -E "s/${PREFIX}([0-9]+)\.json/\1/" | sort -n | tail -1) next=$((latest+1)) json_file="${PREFIX}${next}.json" diff --git a/.github/workflows/build-default-models.yaml b/.github/workflows/build-default-models.yaml index bf00845e27..8030c7133d 100644 --- a/.github/workflows/build-default-models.yaml +++ b/.github/workflows/build-default-models.yaml @@ -176,7 +176,7 @@ jobs: build_big_model: needs: resolve if: ${{ inputs.target == 'big' }} - runs-on: [self-hosted, usbgpu] + runs-on: [self-hosted, chestnut] env: BIG_ONNX: openpilot/selfdrive/modeld/models/big_driving_supercombo.onnx BIG_PKL: openpilot/selfdrive/modeld/models/big_driving_tinygrad.pkl diff --git a/.github/workflows/build-single-tinygrad-model.yaml b/.github/workflows/build-single-tinygrad-model.yaml index 1ad06a54e4..c9d495c15f 100644 --- a/.github/workflows/build-single-tinygrad-model.yaml +++ b/.github/workflows/build-single-tinygrad-model.yaml @@ -30,7 +30,7 @@ on: type: boolean default: true target_hardware: - description: 'Hardware target to compile for (qcom or usbgpu)' + description: 'Hardware target to compile for (qcom or chestnut)' required: false type: string default: 'qcom' @@ -101,7 +101,7 @@ on: default: 'qcom' options: - qcom - - usbgpu + - chestnut hf_repo: description: 'Hugging Face dataset repository' required: false @@ -109,7 +109,7 @@ on: default: 'sunnypilot/sunnypilot_models_v1' env: RECOMPILED_DIR: recompiled${{ inputs.recompiled_dir }} - JSON_FILE: docs/docs/driving_models_${{ inputs.target_hardware == 'usbgpu' && 'usbgpu_v' || 'v' }}${{ inputs.json_version }}.json + JSON_FILE: docs/docs/driving_models_${{ inputs.target_hardware == 'chestnut' && 'chestnut_v' || 'v' }}${{ inputs.json_version }}.json jobs: build_model: diff --git a/.github/workflows/sunnypilot-build-model.yaml b/.github/workflows/sunnypilot-build-model.yaml index 5c2e3bf204..459fa74595 100644 --- a/.github/workflows/sunnypilot-build-model.yaml +++ b/.github/workflows/sunnypilot-build-model.yaml @@ -31,7 +31,7 @@ on: type: string default: '' target_hardware: - description: 'Hardware target to compile for (qcom or usbgpu)' + description: 'Hardware target to compile for (qcom or chestnut)' required: false type: string default: 'qcom' @@ -57,7 +57,7 @@ on: type: choice options: - qcom - - usbgpu + - chestnut default: 'qcom' @@ -102,7 +102,7 @@ jobs: cat $GITHUB_OUTPUT - run: | cd ${{ github.workspace }}/openpilot/openpilot - if [ "${{ inputs.target_hardware }}" != "usbgpu" ]; then + if [ "${{ inputs.target_hardware }}" != "chestnut" ]; then git lfs pull -X "**/selfdrive/modeld/models/big_*.onnx,**/selfdrive/modeld/models/dmonitoring_*.onnx" rm -f selfdrive/modeld/models/big_*.onnx selfdrive/modeld/models/dmonitoring_*.onnx else @@ -121,7 +121,7 @@ jobs: if-no-files-found: error build_model: - runs-on: [self-hosted, usbgpu] + runs-on: [self-hosted, chestnut] needs: get_model env: MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }}) @@ -185,9 +185,9 @@ jobs: CAMERA_RES=$(python3 -c "from openpilot.common.transformations.camera import _ar_ox_fisheye as a, _os_fisheye as o; print(f'{a.width}x{a.height} {o.width}x{o.height}')") TG_FLAGS_QCOM="DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1" - if [ "${{ inputs.target_hardware }}" == "usbgpu" ]; then - echo "USBGPU build" - export USBGPU=1 + if [ "${{ inputs.target_hardware }}" == "chestnut" ]; then + echo "CHESTNUT build" + export CHESTNUT=1 TG_FLAGS="DEBUG=1 DEV=USB+AMD:LLVM WARP_DEV=QCOM FLOAT16=1 JIT_BATCH_SIZE=0 GMMU=0 TC_OPT=2" OUTPUT_PKL="${{ env.MODELS_DIR }}/big_driving_tinygrad.pkl" else diff --git a/openpilot/common/params_keys.h b/openpilot/common/params_keys.h index 5461424c13..3c5f30ab3d 100644 --- a/openpilot/common/params_keys.h +++ b/openpilot/common/params_keys.h @@ -130,8 +130,8 @@ inline static std::unordered_map keys = { {"UpdaterLastFetchTime", {PERSISTENT, TIME}}, {"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}}, {"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}}, + {"ChestnutActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, + {"ChestnutLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, {"Version", {PERSISTENT, STRING}}, // --- sunnypilot params --- // @@ -195,15 +195,16 @@ inline static std::unordered_map keys = { // Model Manager params {"ModelManager_ActiveBundle", {PERSISTENT, JSON}}, - {"ModelManager_ActiveBundleUSBGPU", {PERSISTENT, JSON}}, + {"ModelManager_ActiveBundleUSBGPU", {PERSISTENT, JSON}}, //TODO-SP: kept for migration, remove on next sync? + {"ModelManager_ActiveBundleChestnut", {PERSISTENT, JSON}}, {"ModelManager_ActiveJson", {CLEAR_ON_MANAGER_START, JSON}}, {"ModelManager_ClearCache", {CLEAR_ON_MANAGER_START, BOOL}}, {"ModelManager_DownloadRef", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, STRING}}, {"ModelManager_Favs", {PERSISTENT | BACKUP, STRING}}, {"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}}, - {"ModelManager_LastSyncTime_USBGPU", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}}, + {"ModelManager_LastSyncTime_Chestnut", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}}, {"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}}, - {"ModelManager_ModelsCache_USBGPU", {PERSISTENT | BACKUP, JSON}}, + {"ModelManager_ModelsCache_Chestnut", {PERSISTENT | BACKUP, JSON}}, // Neural Network Lateral Control {"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}}, diff --git a/openpilot/selfdrive/assets/icons_mici/egpu.png b/openpilot/selfdrive/assets/icons_mici/chestnut.png similarity index 100% rename from openpilot/selfdrive/assets/icons_mici/egpu.png rename to openpilot/selfdrive/assets/icons_mici/chestnut.png diff --git a/openpilot/selfdrive/assets/icons_mici/egpu_green.png b/openpilot/selfdrive/assets/icons_mici/chestnut_green.png similarity index 100% rename from openpilot/selfdrive/assets/icons_mici/egpu_green.png rename to openpilot/selfdrive/assets/icons_mici/chestnut_green.png diff --git a/openpilot/selfdrive/assets/icons_mici/chestnut_orange.png b/openpilot/selfdrive/assets/icons_mici/chestnut_orange.png new file mode 100644 index 0000000000..d3982abf87 --- /dev/null +++ b/openpilot/selfdrive/assets/icons_mici/chestnut_orange.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:845c40ff0d37612e8f2f482a36845744b5ae91ce2fcfc8117990d7d278b59820 +size 13079 diff --git a/openpilot/selfdrive/assets/icons_mici/egpu_crossed.png b/openpilot/selfdrive/assets/icons_mici/egpu_crossed.png deleted file mode 100644 index 4fc5decb59..0000000000 --- a/openpilot/selfdrive/assets/icons_mici/egpu_crossed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a8c5fece2a1c7587feb41cbe04c6aee08e768ecd9b5d00da6af9832a4ccc842 -size 2034 diff --git a/openpilot/selfdrive/assets/icons_mici/egpu_gray.png b/openpilot/selfdrive/assets/icons_mici/egpu_gray.png deleted file mode 100644 index a6aeb84689..0000000000 --- a/openpilot/selfdrive/assets/icons_mici/egpu_gray.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7409c53d7c72681c24982fd83b56ce70f80797c9c0f936d9296a5c18557ac472 -size 7279 diff --git a/openpilot/selfdrive/assets/icons_mici/egpu_orange.png b/openpilot/selfdrive/assets/icons_mici/egpu_orange.png deleted file mode 100644 index c2fc06e34e..0000000000 --- a/openpilot/selfdrive/assets/icons_mici/egpu_orange.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:58bd6155433f623b1f75d134bd8ca4745d9aa71f6767eb807cdbcf7deb3089a1 -size 10876 diff --git a/openpilot/selfdrive/modeld/SConscript b/openpilot/selfdrive/modeld/SConscript index 19ce7d5e00..8769ef69a2 100644 --- a/openpilot/selfdrive/modeld/SConscript +++ b/openpilot/selfdrive/modeld/SConscript @@ -7,7 +7,7 @@ from openpilot.common.file_chunker import chunk_file, get_chunk_targets, get_exi from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE, DM_INPUT_SIZE from openpilot.selfdrive.modeld.constants import ModelConstants -from openpilot.selfdrive.modeld.helpers import TG_INPUT_DEVICES_PATH, usbgpu_present, modeld_pkl_path +from openpilot.selfdrive.modeld.helpers import TG_INPUT_DEVICES_PATH, chestnut_present, modeld_pkl_path CAMERA_CONFIGS = [ @@ -36,18 +36,18 @@ else: tg_devices = { # which device to put jit inputs to at runtime 'openpilot.selfdrive.modeld.modeld': { 'default': {'WARP_DEV': tg_backend, 'QUEUE_DEV': tg_backend}, - 'usbgpu': {'WARP_DEV': tg_backend, 'QUEUE_DEV': 'AMD'} + 'chestnut': {'WARP_DEV': tg_backend, 'QUEUE_DEV': 'AMD'} }, 'openpilot.selfdrive.modeld.dmonitoringmodeld': { 'default': {'DEV': tg_backend} }, } -USBGPU = usbgpu_present() -if USBGPU: - usbgpu_tg_flags = f'DEBUG=2 DEV=USB+AMD:LLVM WARP_DEV={tg_backend} FLOAT16=1 JIT_BATCH_SIZE=0 GMMU=0 TC_OPT=2' +CHESTNUT = chestnut_present() +if CHESTNUT: + chestnut_tg_flags = f'DEBUG=2 DEV=USB+AMD:LLVM WARP_DEV={tg_backend} FLOAT16=1 JIT_BATCH_SIZE=0 GMMU=0 TC_OPT=2' # the USB+AMD GPU takes an exclusive flock; serialize all targets that touch it - usbgpu_lock = File("models/.usb_gpu.lock").abspath + chestnut_lock = File("models/.chestnut.lock").abspath def write_tg_devices(target, source, env): with open(str(target[0]), "w") as f: @@ -74,10 +74,10 @@ model_w, model_h = MEDMODEL_INPUT_SIZE frame_skip = ModelConstants.MODEL_RUN_FREQ // ModelConstants.MODEL_CONTEXT_FREQ if not os.getenv('SKIP_TINYGRAD_COMPILE'): - for usbgpu in [False, True] if USBGPU else [False]: - target_pkl_path = File(modeld_pkl_path(usbgpu)).abspath - # BIG_INTO_SMALL=1 builds the default target from the big model, e.g. to test it without a USB GPU - file_prefix, cmd_flags = ('big_', usbgpu_tg_flags) if usbgpu else ('big_' if os.getenv('BIG_INTO_SMALL') else '', tg_flags) + for chestnut in [False, True] if CHESTNUT else [False]: + target_pkl_path = File(modeld_pkl_path(chestnut)).abspath + # BIG_INTO_SMALL=1 builds the default target from the big model, e.g. to test it without a chestnut + file_prefix, cmd_flags = ('big_', chestnut_tg_flags) if chestnut else ('big_' if os.getenv('BIG_INTO_SMALL') else '', tg_flags) driving_onnx_deps = get_existing_chunks(File(f"models/{file_prefix}driving_supercombo.onnx").abspath) camera_res_args = ' '.join(f'{cw}x{ch}' for cw, ch in CAMERA_CONFIGS) # CPU 7 is isolated with isolcpus on AGNOS, so explicitly pin the compiler to it. @@ -104,14 +104,14 @@ if not os.getenv('SKIP_TINYGRAD_COMPILE'): chunk_file(pkl, chunks) def do_chunk(target, source, env, pkl=target_pkl_path, chunks=chunk_targets): chunk_file(pkl, chunks) - actions = Action(do_compile, " [USBGPU] $TARGET") if usbgpu else [cmd, Action(do_chunk, " [CHUNK] $TARGET")] + actions = Action(do_compile, " [CHESTNUT] $TARGET") if chestnut else [cmd, Action(do_chunk, " [CHUNK] $TARGET")] node = lenv.Command( chunk_targets, tinygrad_files + compile_modeld_script + driving_onnx_deps + [Value(chunk_targets), chunker_file], actions, ) - if usbgpu: - lenv.SideEffect(usbgpu_lock, node) + if chestnut: + lenv.SideEffect(chestnut_lock, node) # get model metadata fn = File(f"models/dmonitoring_model").abspath diff --git a/openpilot/selfdrive/modeld/dmonitoringmodeld.py b/openpilot/selfdrive/modeld/dmonitoringmodeld.py index 67161c35bd..4010725b89 100755 --- a/openpilot/selfdrive/modeld/dmonitoringmodeld.py +++ b/openpilot/selfdrive/modeld/dmonitoringmodeld.py @@ -29,7 +29,7 @@ class ModelState: output: np.ndarray def __init__(self, cam_w: int, cam_h: int): - self.DEV = get_tg_input_devices(PROCESS_NAME, usbgpu=False)['DEV'] + self.DEV = get_tg_input_devices(PROCESS_NAME, chestnut=False)['DEV'] with open(METADATA_PATH, 'rb') as f: model_metadata = pickle.load(f) self.input_shapes = model_metadata['input_shapes'] diff --git a/openpilot/selfdrive/modeld/helpers.py b/openpilot/selfdrive/modeld/helpers.py index 37ab0b26d7..84236f3fd0 100644 --- a/openpilot/selfdrive/modeld/helpers.py +++ b/openpilot/selfdrive/modeld/helpers.py @@ -13,12 +13,12 @@ MODELS_DIR = Path(__file__).resolve().parent / 'models' TG_INPUT_DEVICES_PATH = MODELS_DIR / 'tg_input_devices.json' -def get_tg_input_devices(process_name: str, usbgpu: bool): +def get_tg_input_devices(process_name: str, chestnut: bool): with open(TG_INPUT_DEVICES_PATH) as f: - return json.load(f)[process_name]['default' if not usbgpu else 'usbgpu'] + return json.load(f)[process_name]['default' if not chestnut else 'chestnut'] -def modeld_pkl_path(usbgpu: bool): - prefix = 'big_' if usbgpu else '' +def modeld_pkl_path(chestnut: bool): + prefix = 'big_' if chestnut else '' return MODELS_DIR / f'{prefix}driving_tinygrad.pkl' def dump_oob(obj, f): @@ -45,7 +45,7 @@ def load_oob(f): yield pb return pickle.load(io.BytesIO(opcodes), buffers=buffers()) -def usbgpu_present() -> bool: +def chestnut_present() -> bool: for d in USB_DEVICES_PATH.glob("*"): try: usb_id = (int((d / "idVendor").read_text(), 16), int((d / "idProduct").read_text(), 16)) @@ -56,5 +56,5 @@ def usbgpu_present() -> bool: pass return False -def usbgpu_compiled() -> bool: - return Path(get_manifest_path(modeld_pkl_path(usbgpu=True))).is_file() +def chestnut_compiled() -> bool: + return Path(get_manifest_path(modeld_pkl_path(chestnut=True))).is_file() diff --git a/openpilot/selfdrive/modeld/modeld.py b/openpilot/selfdrive/modeld/modeld.py index 790ae7b167..f3f639d6d1 100755 --- a/openpilot/selfdrive/modeld/modeld.py +++ b/openpilot/selfdrive/modeld/modeld.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 +from collections.abc import Callable +import ctypes from functools import cached_property import os -os.environ['GMMU'] = '0' # for usbgpu fast loading, noop for qcom +os.environ['GMMU'] = '0' # for chestnut fast loading, noop for qcom from tinygrad.tensor import Tensor from tinygrad.device import Device import struct @@ -30,7 +32,7 @@ from openpilot.selfdrive.modeld.compile_modeld import make_input_queues, WARP_IN 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.constants import ModelConstants, Plan -from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled, modeld_pkl_path, get_tg_input_devices, load_oob +from openpilot.selfdrive.modeld.helpers import chestnut_present, chestnut_compiled, modeld_pkl_path, get_tg_input_devices, load_oob from openpilot.sunnypilot.livedelay.helpers import get_lat_delay from openpilot.sunnypilot.modeld_v2.modeld_base import ModelStateBase @@ -94,8 +96,10 @@ class ChestnutState: if self.big and "AMD" in Device._opened_devices and self.sends % 100 == 1: try: smu = Device["AMD"].iface.dev_impl.smu + metrics_t = smu.smu_mod.SmuMetricsExternal_t smu._send_msg(smu.smu_mod.PPSMC_MSG_TransferTableSmu2Dram, smu.smu_mod.TABLE_SMU_METRICS, timeout=100) - metrics = smu.read_table(smu.smu_mod.SmuMetricsExternal_t, smu.smu_mod.TABLE_SMU_METRICS).SmuMetrics + metrics_buf = bytearray(smu.adev.vram.view(smu.driver_table_paddr, ctypes.sizeof(metrics_t))[:]) + metrics = metrics_t.from_buffer(metrics_buf).SmuMetrics self.metrics = {'tempC': metrics.AvgTemperature[smu.smu_mod.TEMP_HOTSPOT], 'memoryTempC': metrics.AvgTemperature[smu.smu_mod.TEMP_MEM], 'powerDrawW': metrics.AverageSocketPower, @@ -141,18 +145,18 @@ class FrameMeta: class ModelState(ModelStateBase): prev_desire: np.ndarray # for tracking the rising edge of the pulse - def __init__(self, cam_w: int, cam_h: int, usbgpu: bool): + def __init__(self, cam_w: int, cam_h: int, chestnut: bool): ModelStateBase.__init__(self) - input_devices = get_tg_input_devices(PROCESS_NAME, usbgpu) + input_devices = get_tg_input_devices(PROCESS_NAME, chestnut) 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_file_chunked(modeld_pkl_path(chestnut))) metadata = jits['metadata'] self.input_shapes = metadata['input_shapes'] self.vision_input_names = [k for k in self.input_shapes if 'img' in k] self.output_slices = metadata['output_slices'] self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) - self.usbgpu = usbgpu + self.chestnut = chestnut self.frame_skip = ModelConstants.MODEL_RUN_FREQ // ModelConstants.MODEL_CONTEXT_FREQ self.input_queues, self.npy = make_input_queues(self.input_shapes, self.frame_skip, device=self.QUEUE_DEV) @@ -168,7 +172,7 @@ class ModelState(ModelStateBase): return parsed_model_outputs def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray], - inputs: dict[str, np.ndarray]) -> dict[str, np.ndarray] | None: + inputs: dict[str, np.ndarray], after_enqueue: Callable[[], None] | None = None) -> dict[str, np.ndarray]: for key in bufs.keys(): ptr = np.frombuffer(bufs[key].data, dtype=np.uint8).ctypes.data yuv_size = self.frame_buf_params[key][3] @@ -192,11 +196,11 @@ class ModelState(ModelStateBase): outs, = self.run_policy( **{k: self.input_queues[k] for k in POLICY_INPUTS if k in self.input_queues}, warped=warped ) + if after_enqueue is not None: + after_enqueue() model_output = outs.numpy()[0] - if self.usbgpu and not np.all(np.isfinite(model_output)): - # TODO remove with prev_feat - cloudlog.error("model output not finite, dropping frame") - return None + if self.chestnut and not np.all(np.isfinite(model_output)): + raise RuntimeError("model output not finite") outputs_dict = self.parser.parse_outputs(self.slice_outputs(model_output, self.output_slices)) self.npy['prev_feat'][:] = model_output[self.output_slices['hidden_state']] @@ -218,12 +222,12 @@ class ModelState(ModelStateBase): def main(demo=False): cloudlog.warning("modeld init") - USBGPU = usbgpu_present() and usbgpu_compiled() - if USBGPU: + CHESTNUT = chestnut_present() and chestnut_compiled() + if CHESTNUT: os.environ['HCQDEV_WAIT_TIMEOUT_MS'] = '3000' params = Params() - params.put_bool("UsbGpuLoading", USBGPU) - params.remove("UsbGpuActive") + params.put_bool("ChestnutLoading", CHESTNUT) + params.remove("ChestnutActive") config_realtime_process(7, 54) @@ -253,7 +257,7 @@ def main(demo=False): st = time.monotonic() cloudlog.warning("loading model") model = None - if USBGPU: + if CHESTNUT: big_model = None def load_big(): nonlocal big_model @@ -267,23 +271,23 @@ def main(demo=False): loader.start() loader.join(BIG_MODEL_TIMEOUT) model = big_model - params.put_bool("UsbGpuActive", model is not None) + params.put_bool("ChestnutActive", model is not None) - small_model = ModelState(vipc_client_main.width, vipc_client_main.height, False) if model is None or USBGPU else None + small_model = ModelState(vipc_client_main.width, vipc_client_main.height, False) if model is None or CHESTNUT else None if model is None: model = small_model - params.put_bool("UsbGpuLoading", False) + params.put_bool("ChestnutLoading", False) assert model is not None cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting") # messaging - pub_socks = ["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"] + (["chestnutState"] if USBGPU else []) + pub_socks = ["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"] + (["chestnutState"] if CHESTNUT else []) pm = PubMaster(pub_socks) sm = SubMaster(["deviceState", "carState", "narrowRoadCameraState", "extrinsicsCalibration", "driverMonitoringState", "carControl", "lateralDelay"]) publish_state = PublishState() params = Params() - chestnut_state = ChestnutState(pm, model.usbgpu) if USBGPU else None + chestnut_state = ChestnutState(pm, model.chestnut) if CHESTNUT else None # setup filter to track dropped frames frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_RUN_FREQ) @@ -393,13 +397,15 @@ def main(demo=False): mt1 = time.perf_counter() try: - model_output = model.run(bufs, transforms, inputs) + send_chestnut = (chestnut_state is not None and + run_count % round(ModelConstants.MODEL_RUN_FREQ / SERVICE_LIST['chestnutState'].frequency) == 0) + model_output = model.run(bufs, transforms, inputs, chestnut_state.send if send_chestnut else None) except Exception: - if not params.get_bool("UsbGpuActive"): + if not params.get_bool("ChestnutActive"): raise # fallback to small model cloudlog.exception("big model failed, fall back to small") - params.put_bool("UsbGpuActive", False) + params.put_bool("ChestnutActive", False) assert small_model is not None model = small_model if chestnut_state is not None: @@ -419,7 +425,7 @@ def main(demo=False): fill_model_msg(modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, extrinsics_calibration_seen) - modelv2_send.modelV2.big = model.usbgpu + modelv2_send.modelV2.big = model.chestnut desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] @@ -441,10 +447,6 @@ def main(demo=False): pm.send('modelDataV2SP', mdv2sp_send) last_vipc_frame_id = meta_main.frame_id - if chestnut_state is not None and run_count % round(ModelConstants.MODEL_RUN_FREQ / SERVICE_LIST['chestnutState'].frequency) == 0: - chestnut_state.send() - - if __name__ == "__main__": try: import argparse diff --git a/openpilot/selfdrive/selfdrived/selfdrived.py b/openpilot/selfdrive/selfdrived/selfdrived.py index d1bc57c856..180b58c916 100755 --- a/openpilot/selfdrive/selfdrived/selfdrived.py +++ b/openpilot/selfdrive/selfdrived/selfdrived.py @@ -195,17 +195,17 @@ class SelfdriveD(CruiseHelper): self.events.add(EventName.joystickDebug) self.startup_event = None - loading = self.params.get_bool("UsbGpuLoading") + loading = self.params.get_bool("ChestnutLoading") if self.big_model_loading and not loading: self.big_model_ready_t = time.monotonic() self.big_model_loading = loading if self.big_model_loading: self.events.add(EventName.bigModelLoading) - big_active = self.params.get("UsbGpuActive") - usbgpu_present = self.sm['deviceState'].chestnutPresent + big_active = self.params.get("ChestnutActive") + chestnut_present = self.sm['deviceState'].chestnutPresent model_unavailable = big_active is True and self.sm.seen['modelV2'] and not self.sm.alive['modelV2'] - big_failed = big_active is False or model_unavailable or (self.big_model_active and not usbgpu_present) + big_failed = big_active is False or model_unavailable or (self.big_model_active and not chestnut_present) if big_failed and not self.big_model_failed: self.events.add(EventName.bigModelFailed) self.big_model_failed = big_failed diff --git a/openpilot/selfdrive/ui/mici/layouts/home.py b/openpilot/selfdrive/ui/mici/layouts/home.py index fd74979404..fce3db4605 100644 --- a/openpilot/selfdrive/ui/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/mici/layouts/home.py @@ -9,7 +9,7 @@ from openpilot.system.ui.widgets.layouts import HBoxLayout from openpilot.system.ui.widgets.icon_widget import IconWidget from openpilot.system.ui.widgets.label import UnifiedLabel, gui_label from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos -from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state, ChestnutState from openpilot.common.version import RELEASE_BRANCHES HEAD_BUTTON_FONT_SIZE = 40 @@ -139,8 +139,8 @@ class MiciHomeLayout(Widget): self._version_text = self._get_version_text() self._experimental_icon = IconWidget("icons_mici/experimental_mode.png", (48, 48)) - self._egpu_icon = IconWidget("icons_mici/egpu_green.png", (50, 37)) - self._egpu_icon_gray = IconWidget("icons_mici/egpu_gray.png", (50, 37)) + self._chestnut_icon = IconWidget("icons_mici/chestnut_green.png", (68, 40)) + self._chestnut_failed_icon = IconWidget("icons_mici/chestnut_orange.png", (68, 40)) self._mic_icon = IconWidget("icons_mici/microphone.png", (32, 46)) self._body_icon = IconWidget("icons_mici/body.png", (54, 37)) @@ -150,8 +150,8 @@ class MiciHomeLayout(Widget): IconWidget("icons_mici/settings.png", (48, 48), opacity=0.9), NetworkIcon(), self._experimental_icon, - self._egpu_icon, - self._egpu_icon_gray, + self._chestnut_icon, + self._chestnut_failed_icon, self._body_icon, self._mic_icon, ], spacing=18) @@ -249,10 +249,10 @@ class MiciHomeLayout(Widget): # ***** Center-aligned bottom section icons ***** self._experimental_icon.set_visible(ui_state.experimental_mode) if gui_app.sunnypilot_ui(): - self._set_egpu_visibility() + self._set_chestnut_visibility() else: - self._egpu_icon.set_visible(ui_state.sm["deviceState"].chestnutPresent and ui_state.usbgpu_compiled) - self._egpu_icon_gray.set_visible(ui_state.sm["deviceState"].chestnutPresent and not ui_state.usbgpu_compiled) + self._chestnut_icon.set_visible(ui_state.chestnut_state in (ChestnutState.READY, ChestnutState.LOADING, ChestnutState.ACTIVE)) + self._chestnut_failed_icon.set_visible(ui_state.chestnut_state in (ChestnutState.UNCOMPILED, ChestnutState.FAILED)) self._mic_icon.set_visible(ui_state.recording_audio) self._body_icon.set_visible(bool(ui_state.is_body)) diff --git a/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py b/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py index 2d1f954122..05abae82eb 100644 --- a/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py +++ b/openpilot/selfdrive/ui/mici/onroad/hud_renderer.py @@ -3,7 +3,7 @@ import pyray as rl from dataclasses import dataclass from openpilot.common.constants import CV from openpilot.selfdrive.ui.mici.onroad.torque_bar import TorqueBar -from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, ChestnutState from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.multilang import tr from openpilot.system.ui.lib.text_measure import measure_text_cached @@ -107,8 +107,7 @@ class HudRenderer(Widget): self.speed: float = 0.0 self.v_ego_cluster_seen: bool = False self._engaged: bool = False - self._small_model_engaged: bool = False - self._egpu_fade_time: float = 0 + self._chestnut_fade_time: float = 0 self._can_draw_top_icons = True self._show_wheel_critical = False @@ -124,17 +123,15 @@ class HudRenderer(Widget): self._txt_wheel: rl.Texture = gui_app.texture('icons_mici/wheel.png', 50, 50) self._txt_wheel_critical: rl.Texture = gui_app.texture('icons_mici/wheel_critical.png', 50, 50) self._txt_exclamation_point: rl.Texture = gui_app.texture('icons_mici/exclamation_point.png', 9, 44) - self._txt_egpu: rl.Texture = gui_app.texture('icons_mici/egpu.png', 60, 44) - self._txt_egpu_green: rl.Texture = gui_app.texture('icons_mici/egpu_green.png', 60, 44) - self._txt_egpu_orange: rl.Texture = gui_app.texture('icons_mici/egpu_orange.png', 60, 44) - self._txt_egpu_crossed: rl.Texture = gui_app.texture('icons_mici/egpu_crossed.png', 60, 52) - self._egpu_icon: rl.Texture | None = None - + self._txt_chestnut: rl.Texture = gui_app.texture('icons_mici/chestnut.png', 60, 44) + self._txt_chestnut_green: rl.Texture = gui_app.texture('icons_mici/chestnut_green.png', 60, 44) + self._txt_chestnut_orange: rl.Texture = gui_app.texture('icons_mici/chestnut_orange.png', 75, 44) + self._chestnut_icon: rl.Texture | None = None self._wheel_alpha_filter = FirstOrderFilter(0, 0.05, 1 / gui_app.target_fps) self._wheel_y_filter = FirstOrderFilter(0, 0.1, 1 / gui_app.target_fps) self._set_speed_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps) - self._egpu_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps) + self._chestnut_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps) def set_wheel_critical_icon(self, critical: bool): """Set the wheel icon to critical or normal state.""" @@ -165,13 +162,10 @@ class HudRenderer(Widget): controls_state.deprecated.vCruise if v_cruise_cluster == 0.0 else v_cruise_cluster ) engaged = sm['selfdriveState'].enabled - if (engaged and not self._engaged and not ui_state.usbgpu_loading and ui_state.usbgpu_active is not True and - ui_state.sm.recv_frame['modelV2'] > ui_state.started_frame): - self._small_model_engaged = True - if engaged != self._engaged: - self._egpu_fade_time = rl.get_time() if engaged else 0 if (set_speed != self.set_speed and engaged) or (engaged and not self._engaged): self._set_speed_changed_time = rl.get_time() + if engaged != self._engaged: + self._chestnut_fade_time = rl.get_time() if engaged else 0 self._engaged = engaged self.set_speed = set_speed self.is_cruise_set = 0 < self.set_speed < SET_SPEED_NA @@ -191,8 +185,7 @@ class HudRenderer(Widget): if self.is_cruise_set: self._draw_set_speed(rect) - if ui_state.usbgpu and ui_state.usbgpu_compiled: - self._draw_model_source(rect) + self._draw_model_source(rect) self._draw_steering_wheel(rect) @@ -200,30 +193,24 @@ class HudRenderer(Widget): if ui_state.sm.recv_frame['selfdriveState'] < ui_state.started_frame: return - big_failed = (ui_state.usbgpu_active is False or not ui_state.sm['deviceState'].chestnutPresent or - (ui_state.usbgpu_active is True and ui_state.sm.recv_frame['modelV2'] > ui_state.started_frame and - not ui_state.sm.alive['modelV2']) or - (ui_state.usbgpu_active is None and ui_state.sm.recv_frame['modelV2'] > ui_state.started_frame)) - self._small_model_engaged &= big_failed - loading = ui_state.usbgpu_loading or (ui_state.usbgpu_active is None and not big_failed) + loading = ui_state.chestnut_state == ChestnutState.LOADING if loading: - pulse = 0.5 - 0.5 * math.cos(rl.get_time() * 6.0) - icon = self._txt_egpu - opacity = 0.35 + 0.65 * pulse - elif self._small_model_engaged: - icon = self._txt_egpu_crossed - opacity = 0.65 - elif big_failed: - icon = self._txt_egpu_orange + icon = self._txt_chestnut + opacity = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0)) + elif ui_state.chestnut_state in (ChestnutState.UNCOMPILED, ChestnutState.FAILED): + icon = self._txt_chestnut_orange + opacity = 1.0 + elif ui_state.chestnut_state == ChestnutState.ACTIVE: + icon = self._txt_chestnut_green opacity = 1.0 else: - icon = self._txt_egpu_green - opacity = 1.0 + return - 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) + if icon is not self._chestnut_icon: + self._chestnut_fade_time = rl.get_time() + self._chestnut_icon = icon + visible = loading or rl.get_time() - self._chestnut_fade_time < SET_SPEED_PERSISTENCE + alpha = self._chestnut_alpha_filter.update(visible) if alpha < 1e-2: return diff --git a/openpilot/selfdrive/ui/sunnypilot/layouts/settings/models.py b/openpilot/selfdrive/ui/sunnypilot/layouts/settings/models.py index 3aa115139f..d1abfa22a0 100644 --- a/openpilot/selfdrive/ui/sunnypilot/layouts/settings/models.py +++ b/openpilot/selfdrive/ui/sunnypilot/layouts/settings/models.py @@ -62,14 +62,14 @@ class ModelsLayout(Widget): self.big_model_item = ListItemSP( title=tr("Big Model"), action_item=ScrollingButtonAction(tr("SELECT")), - callback=lambda: self._open_source_dialog("usbgpu") + callback=lambda: self._open_source_dialog("chestnut") ) self.download_item = download_status_item(lambda: tr("Download") if self._downloading else tr("Model Status")) self.refresh_item = button_item(tr("Refresh Model List"), tr("REFRESH"), "", lambda: (ui_state.params.put("ModelManager_LastSyncTime", 0), - ui_state.params.put("ModelManager_LastSyncTime_USBGPU", 0), + ui_state.params.put("ModelManager_LastSyncTime_Chestnut", 0), gui_app.push_widget(alert_dialog(tr("Fetching Latest Models"))))) self.clear_cache_item = ListItemSP( @@ -177,14 +177,14 @@ class ModelsLayout(Widget): big_state = big_model_state() carry_source, carry_internal, _ = carrying_model() segments = [] - for source, label in (("qcom", tr("small")), ("usbgpu", tr("big"))): + for source, label in (("qcom", tr("small")), ("chestnut", tr("big"))): if segments: segments.append(("|", rl.GRAY, None, None)) bundle = get_selected_bundle(ui_state.params, source) name = bundle.internalName if bundle else default_model_name(source) color = ON_COLOR if (source == carry_source and name == carry_internal) else rl.LIGHTGRAY name = "● " + name - if source == "usbgpu": + if source == "chestnut": if big_state == 'failed': color = rl.RED elif big_state == 'loading': @@ -208,10 +208,10 @@ class ModelsLayout(Widget): """The failover story for the Model Status row. One-way big -> small, and the fallback is runner-matched: a Default big can only fall back to the Default small (stock modeld), a custom big has no automatic fallback yet.""" - if not ui_state.usbgpu: + if not ui_state.chestnut_present: return "" - big_bundle = get_selected_bundle(ui_state.params, "usbgpu") - big_name = big_bundle.internalName if big_bundle else default_model_name("usbgpu") + big_bundle = get_selected_bundle(ui_state.params, "chestnut") + big_name = big_bundle.internalName if big_bundle else default_model_name("chestnut") big_is_default = big_bundle is None fallback_name = default_model_name("qcom") state = big_model_state() @@ -225,7 +225,7 @@ class ModelsLayout(Widget): return tr("Getting the big model ready.") if big_is_default: return tr("{} will drive. If it fails during a drive, {} takes over until the next drive.").format(big_name, fallback_name) - return tr("{} will drive when the eGPU is ready.").format(big_name) + return tr("{} will drive when the chestnut is ready.").format(big_name) @staticmethod def _download_row_state(progresses, name: str) -> dict: @@ -261,7 +261,7 @@ class ModelsLayout(Widget): ui_state.params.put("ModelManager_DownloadRef", selected_bundle.ref) def _resolve_selected_bundle(self, ref): - source_bundles = {source: bundles_for_source(source) for source in ("qcom", "usbgpu")} + source_bundles = {source: bundles_for_source(source) for source in ("qcom", "chestnut")} resolved = resolve_bundle_by_ref(ref, source_bundles) return resolved[0] if resolved else None @@ -329,7 +329,7 @@ class ModelsLayout(Widget): self._handle_bundle_download_progress() carry_source, _, carry_display = carrying_model() - for item, item_source in ((self.small_model_item, "qcom"), (self.big_model_item, "usbgpu")): + for item, item_source in ((self.small_model_item, "qcom"), (self.big_model_item, "chestnut")): bundle = get_selected_bundle(ui_state.params, item_source) name = bundle.displayName if bundle else default_model_name(item_source) color = ON_COLOR if (item_source == carry_source and name == carry_display) else style.ITEM_TEXT_VALUE_COLOR diff --git a/openpilot/selfdrive/ui/sunnypilot/layouts/sidebar.py b/openpilot/selfdrive/ui/sunnypilot/layouts/sidebar.py index 7c74c48469..815e7018a4 100644 --- a/openpilot/selfdrive/ui/sunnypilot/layouts/sidebar.py +++ b/openpilot/selfdrive/ui/sunnypilot/layouts/sidebar.py @@ -9,7 +9,7 @@ import math import pyray as rl import time from dataclasses import dataclass -from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state, ChestnutState from openpilot.sunnypilot.sunnylink.api import UNREGISTERED_SUNNYLINK_DONGLE_ID from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.lib.multilang import tr_noop @@ -21,8 +21,8 @@ METRIC_MARGIN = 30 METRIC_START_Y = 300 HOME_BTN = rl.Rectangle(60, 860, 180, 180) -EGPU_ICON_WIDTH = 180 -EGPU_ICON_HEIGHT = 133 +CHESTNUT_ICON_WIDTH = 180 +CHESTNUT_ICON_HEIGHT = 133 # Color scheme @@ -59,10 +59,9 @@ class MetricData: class SidebarSP: def __init__(self): self._sunnylink_status = MetricData(tr_noop("SUNNYLINK"), tr_noop("OFFLINE"), Colors.WARNING) - self._egpu_green_img = gui_app.texture("icons_mici/egpu_green.png", EGPU_ICON_WIDTH, EGPU_ICON_HEIGHT) - self._egpu_default_img = gui_app.texture("icons_mici/egpu.png", EGPU_ICON_WIDTH, EGPU_ICON_HEIGHT) - self._egpu_orange_img = gui_app.texture("icons_mici/egpu_orange.png", EGPU_ICON_WIDTH, EGPU_ICON_HEIGHT) - self._egpu_gray_img = gui_app.texture("icons_mici/egpu_gray.png", EGPU_ICON_WIDTH, EGPU_ICON_HEIGHT) + self._chestnut_green_img = gui_app.texture("icons_mici/chestnut_green.png", CHESTNUT_ICON_WIDTH, CHESTNUT_ICON_HEIGHT) + self._chestnut_default_img = gui_app.texture("icons_mici/chestnut.png", CHESTNUT_ICON_WIDTH, CHESTNUT_ICON_HEIGHT) + self._chestnut_orange_img = gui_app.texture("icons_mici/chestnut_orange.png", CHESTNUT_ICON_WIDTH, CHESTNUT_ICON_HEIGHT) def _update_sunnylink_status(self): if not ui_state.params.get_bool("SunnylinkEnabled"): @@ -90,22 +89,17 @@ class SidebarSP: def _get_home_icon(self, default_img: rl.Texture) -> tuple[rl.Texture, rl.Vector2, float]: default_pos = rl.Vector2(HOME_BTN.x, HOME_BTN.y) - if not ui_state.sm["deviceState"].chestnutPresent: + state = ui_state.chestnut_state + if state == ChestnutState.DISCONNECTED: return default_img, default_pos, 1.0 - big_model_selected = ui_state.usbgpu_compiled or ui_state.model_runner_tinygrad - big_model_failed = ui_state.started and ui_state.big_model_failed - loading = ui_state.usbgpu_loading or (big_model_selected and ui_state.started and ui_state.usbgpu_active is None) - - if loading: - icon = self._egpu_default_img + if state == ChestnutState.LOADING: + icon = self._chestnut_default_img opacity = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0)) - elif big_model_selected and big_model_failed: - icon, opacity = self._egpu_orange_img, 1.0 - elif big_model_selected: - icon, opacity = self._egpu_green_img, 1.0 + elif state in (ChestnutState.UNCOMPILED, ChestnutState.FAILED): + icon, opacity = self._chestnut_orange_img, 1.0 else: - icon, opacity = self._egpu_gray_img, 1.0 + icon, opacity = self._chestnut_green_img, 1.0 x = HOME_BTN.x + (HOME_BTN.width - icon.width) / 2 y = HOME_BTN.y + (HOME_BTN.height - icon.height) / 2 diff --git a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py index b261373947..8efb6c80e4 100644 --- a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py +++ b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/home.py @@ -9,7 +9,7 @@ import math import pyray as rl from openpilot.selfdrive.ui.mici.layouts.home import MiciHomeLayout -from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state, ChestnutState from openpilot.system.ui.lib.application import FontWeight from openpilot.system.ui.widgets.icon_widget import IconWidget from openpilot.system.ui.widgets.label import UnifiedLabel @@ -19,35 +19,16 @@ class MiciHomeLayoutSP(MiciHomeLayout): def __init__(self): super().__init__() self._openpilot_label = UnifiedLabel("sunnypilot", font_size=88, font_weight=FontWeight.AUDIOWIDE, max_width=480, wrap_text=False) - self._egpu_icon_default = IconWidget("icons_mici/egpu.png", (50, 37)) - self._egpu_icon_default.set_visible(False) - self._egpu_icon_orange = IconWidget("icons_mici/egpu_orange.png", (50, 37)) - self._egpu_icon_orange.set_visible(False) - gray_idx = self._status_bar_layout.widgets.index(self._egpu_icon_gray) - self._status_bar_layout.widgets.insert(gray_idx + 1, self._egpu_icon_default) - self._status_bar_layout.widgets.insert(gray_idx + 2, self._egpu_icon_orange) + self._chestnut_loading_icon = IconWidget("icons_mici/chestnut.png", (68, 40)) + self._chestnut_loading_icon.set_visible(False) + failed_idx = self._status_bar_layout.widgets.index(self._chestnut_failed_icon) + self._status_bar_layout.widgets.insert(failed_idx + 1, self._chestnut_loading_icon) - def _set_egpu_visibility(self): - chestnut = ui_state.sm["deviceState"].chestnutPresent - if not chestnut: - self._egpu_icon.set_visible(False) - self._egpu_icon_default.set_visible(False) - self._egpu_icon_orange.set_visible(False) - self._egpu_icon_gray.set_visible(False) - return - - big_model_selected = ui_state.usbgpu_compiled or ui_state.model_runner_tinygrad - big_model_failed = ui_state.started and ui_state.big_model_failed - loading = ui_state.usbgpu_loading or (big_model_selected and ui_state.started and ui_state.usbgpu_active is None) - - if loading: - self._egpu_icon_default._opacity = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0)) - self._egpu_icon_default.set_visible(True) - self._egpu_icon.set_visible(False) - self._egpu_icon_orange.set_visible(False) - self._egpu_icon_gray.set_visible(False) - else: - self._egpu_icon_default.set_visible(False) - self._egpu_icon.set_visible(big_model_selected and not big_model_failed) - self._egpu_icon_orange.set_visible(big_model_selected and big_model_failed) - self._egpu_icon_gray.set_visible(not big_model_selected) + def _set_chestnut_visibility(self): + # stock has no loading tier: it shows green from the moment a big model is available. keep the + # pulse so the status bar and the onroad HUD agree on what loading looks like. + loading = ui_state.chestnut_state == ChestnutState.LOADING + self._chestnut_loading_icon._opacity = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0)) + self._chestnut_loading_icon.set_visible(loading) + self._chestnut_icon.set_visible(not loading and ui_state.chestnut_state in (ChestnutState.READY, ChestnutState.ACTIVE)) + self._chestnut_failed_icon.set_visible(ui_state.chestnut_state in (ChestnutState.UNCOMPILED, ChestnutState.FAILED)) diff --git a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/models.py b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/models.py index 183b47fa58..4c769498f5 100644 --- a/openpilot/selfdrive/ui/sunnypilot/mici/layouts/models.py +++ b/openpilot/selfdrive/ui/sunnypilot/mici/layouts/models.py @@ -27,14 +27,14 @@ def _model_info() -> tuple[str, str, str]: state = big_model_state() _, _, carry_display = carrying_model() if carry_display is None: - big = get_selected_bundle(ui_state.params, "usbgpu") - carry_display = big.displayName if big else default_model_name("usbgpu") + big = get_selected_bundle(ui_state.params, "chestnut") + carry_display = big.displayName if big else default_model_name("chestnut") active_text = (carry_display or active_name).lower() if state == 'failed': return active_text, tr("big model"), tr("unavailable") if state == 'loading': return active_text, tr("big model"), tr("getting ready") - header = tr("small model") if source == "usbgpu" else tr("big model") + header = tr("small model") if source == "chestnut" else tr("big model") return active_text, header, other_name.lower() @@ -113,7 +113,7 @@ class ModelsLayoutMici(NavScroller): hardware_btns = [] active = active_source() - for source, label in (("qcom", tr("small models")), ("usbgpu", tr("big models"))): + for source, label in (("qcom", tr("small models")), ("chestnut", tr("big models"))): bundle = get_selected_bundle(ui_state.params, source) value = (bundle.internalName if bundle else default_model_name(source)).lower() if source == active: diff --git a/openpilot/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py b/openpilot/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py index ad75f7e969..9d39d01727 100644 --- a/openpilot/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py +++ b/openpilot/selfdrive/ui/sunnypilot/mici/onroad/hud_renderer.py @@ -7,7 +7,6 @@ See the LICENSE.md file in the root directory for more details. import pyray as rl from openpilot.selfdrive.ui.mici.onroad.hud_renderer import HudRenderer -from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.sunnypilot.onroad.blind_spot_indicators import BlindSpotIndicators @@ -22,8 +21,6 @@ class HudRendererSP(HudRenderer): def _render(self, rect: rl.Rectangle) -> None: super()._render(rect) - if ui_state.usbgpu and not ui_state.usbgpu_compiled and ui_state.model_runner_tinygrad: - self._draw_model_source(rect) self.blind_spot_indicators.render(rect) def _has_blind_spot_detected(self) -> bool: diff --git a/openpilot/selfdrive/ui/sunnypilot/model_info.py b/openpilot/selfdrive/ui/sunnypilot/model_info.py index a93a06f187..e338feb9ed 100644 --- a/openpilot/selfdrive/ui/sunnypilot/model_info.py +++ b/openpilot/selfdrive/ui/sunnypilot/model_info.py @@ -4,15 +4,15 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. This file is part of sunnypilot and is licensed under the MIT License. See the LICENSE.md file in the root directory for more details. """ -from openpilot.selfdrive.ui.ui_state import ui_state +from openpilot.selfdrive.ui.ui_state import ui_state, ChestnutState from openpilot.sunnypilot.models.fetcher import get_cached_bundles from openpilot.sunnypilot.models.helpers import get_active_source, get_selected_bundle, resolve_bundle_by_ref from openpilot.sunnypilot.models.model_name import DEFAULT_BIG_MODEL, DEFAULT_MODEL def active_source() -> str: - return get_active_source(usbgpu=ui_state.usbgpu, - usbgpu_active=ui_state.usbgpu_active, usbgpu_loading=ui_state.usbgpu_loading, + return get_active_source(chestnut=ui_state.chestnut_present, + chestnut_active=ui_state.chestnut_active, chestnut_loading=ui_state.chestnut_loading, offroad=ui_state.is_offroad()) @@ -23,7 +23,7 @@ def bundles_for_source(source: str): def default_model(source: str) -> str: - return DEFAULT_BIG_MODEL if source == 'usbgpu' else DEFAULT_MODEL + return DEFAULT_BIG_MODEL if source == 'chestnut' else DEFAULT_MODEL def default_model_name(source: str) -> str: @@ -31,13 +31,10 @@ def default_model_name(source: str) -> str: def big_model_state() -> str | None: - """'failed' | 'loading' | None, mirroring the sidebar's detection (#1969).""" - if ui_state.started and ui_state.usbgpu and ui_state.big_model_failed: - return 'failed' - big_selected = ui_state.usbgpu_compiled or ui_state.model_runner_tinygrad - if ui_state.usbgpu_loading or (big_selected and ui_state.started and ui_state.usbgpu_active is None): - return 'loading' - return None + """'failed' | 'loading' | None, from the same state the icons render.""" + return {ChestnutState.UNCOMPILED: 'failed', + ChestnutState.FAILED: 'failed', + ChestnutState.LOADING: 'loading'}.get(ui_state.chestnut_state) def carrying_model() -> tuple[str | None, str | None, str | None]: @@ -45,14 +42,14 @@ def carrying_model() -> tuple[str | None, str | None, str | None]: when a Default big cannot carry, stock modeld runs the Default small, never the small slot's pick; a custom big has no automatic fallback yet -> (None, None, None).""" source = active_source() - if source == "usbgpu": - bundle = get_selected_bundle(ui_state.params, "usbgpu") + if source == "chestnut": + bundle = get_selected_bundle(ui_state.params, "chestnut") if bundle: - return "usbgpu", bundle.internalName, bundle.displayName - name = default_model_name("usbgpu") - return "usbgpu", name, name - if ui_state.usbgpu: - if get_selected_bundle(ui_state.params, "usbgpu") is None: + return "chestnut", bundle.internalName, bundle.displayName + name = default_model_name("chestnut") + return "chestnut", name, name + if ui_state.chestnut_present: + if get_selected_bundle(ui_state.params, "chestnut") is None: name = default_model_name("qcom") return "qcom", name, name return None, None, None @@ -66,7 +63,7 @@ def carrying_model() -> tuple[str | None, str | None, str | None]: def queued_name(current_ref) -> str | None: ref = ui_state.params.get("ModelManager_DownloadRef") if ref and ref != current_ref: - source_bundles = {source: bundles_for_source(source) for source in ("qcom", "usbgpu")} + source_bundles = {source: bundles_for_source(source) for source in ("qcom", "chestnut")} if resolved := resolve_bundle_by_ref(ref, source_bundles): return resolved[0].internalName return None @@ -79,7 +76,7 @@ def model_info() -> tuple[str, str, str]: manager republishes a tick after a chestnut change, so the stale bundle would flash the wrong model.""" source = active_source() - other = "qcom" if source == "usbgpu" else "usbgpu" + other = "qcom" if source == "chestnut" else "chestnut" active_bundle = get_selected_bundle(ui_state.params, source) other_bundle = get_selected_bundle(ui_state.params, other) diff --git a/openpilot/selfdrive/ui/sunnypilot/ui_state.py b/openpilot/selfdrive/ui/sunnypilot/ui_state.py index 9bed533d3f..d130c3862a 100644 --- a/openpilot/selfdrive/ui/sunnypilot/ui_state.py +++ b/openpilot/selfdrive/ui/sunnypilot/ui_state.py @@ -152,10 +152,13 @@ class UIStateSP: self.has_icbm = self.CP_SP.intelligentCruiseButtonManagementAvailable and self.params.get_bool("IntelligentCruiseButtonManagement") self._enforce_constraints() - source = get_active_source(usbgpu=self.usbgpu, usbgpu_active=self.usbgpu_active, - usbgpu_loading=self.usbgpu_loading, offroad=self.is_offroad()) + source = get_active_source(chestnut=self.chestnut_present, chestnut_active=self.chestnut_active, + chestnut_loading=self.chestnut_loading, offroad=self.is_offroad()) self.active_bundle = self.params.get(ACTIVE_BUNDLE_KEYS[source]) self.model_runner_tinygrad = self.active_bundle is not None and self.active_bundle.get("runner") == "tinygrad" + # stock only counts the default big model's compiled pkl. a downloaded big bundle runs on the + # chestnut just the same, so ChestnutState has to see it as available too. + self.chestnut_compiled = self.chestnut_compiled or self.model_runner_tinygrad self.blindspot = self.params.get_bool("BlindSpot") self.chevron_metrics = self.params.get("ChevronInfo") self.custom_interactive_timeout = self.params.get("InteractivityTimeout", return_default=True) diff --git a/openpilot/selfdrive/ui/ui_state.py b/openpilot/selfdrive/ui/ui_state.py index 7e2a2494ea..4d93e1aacb 100644 --- a/openpilot/selfdrive/ui/ui_state.py +++ b/openpilot/selfdrive/ui/ui_state.py @@ -12,7 +12,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.ui.lib.prime_state import PrimeState from openpilot.system.ui.lib.application import gui_app from openpilot.common.hardware import HARDWARE, PC -from openpilot.selfdrive.modeld.helpers import usbgpu_compiled +from openpilot.selfdrive.modeld.helpers import chestnut_compiled from openpilot.selfdrive.ui.sunnypilot.ui_state import UIStateSP, DeviceSP @@ -28,6 +28,15 @@ class UIStatus(Enum): LONG_ONLY = "long_only" +class ChestnutState(Enum): + DISCONNECTED = "disconnected" + UNCOMPILED = "uncompiled" + READY = "ready" + LOADING = "loading" + ACTIVE = "active" + FAILED = "failed" + + class UIState(UIStateSP): _instance: 'UIState | None' = None @@ -82,10 +91,11 @@ class UIState(UIStateSP): self.always_on_dm: bool = self.params.get_bool("AlwaysOnDM") self.experimental_mode: bool = self.params.get_bool("ExperimentalMode") self.experimental_mode_confirmed: bool = self.params.get_bool("ExperimentalModeConfirmed") - self.usbgpu: bool = False - 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.chestnut_present: bool = False + self.chestnut_compiled: bool = chestnut_compiled() + self.chestnut_active: bool | None = None + self.chestnut_loading: bool = False + self.chestnut_state = ChestnutState.DISCONNECTED self.started: bool = False self.ignition: bool = False self.recording_audio: bool = False @@ -112,15 +122,6 @@ class UIState(UIStateSP): def add_on_body_changed_callbacks(self, callback: Callable[[], None]): self._on_body_changed_callbacks.append(callback) - @property - def big_model_failed(self) -> bool: - # Mirrors the onroad HUD's four-condition check so sidebar and home icons reflect the same failure states - return (self.usbgpu_active is False or - not self.sm['deviceState'].chestnutPresent or - (self.usbgpu_active is True and self.sm.recv_frame['modelV2'] > self.started_frame and - not self.sm.alive['modelV2']) or - (self.usbgpu_active is None and self.sm.recv_frame['modelV2'] > self.started_frame)) - @property def engaged(self) -> bool: return self.started and (self.sm["selfdriveState"].enabled or self.sm["selfdriveStateSP"].mads.enabled) @@ -140,6 +141,7 @@ class UIState(UIStateSP): self.sm.update(0) self._update_state() self._update_status() + self._update_chestnut_state() device.update() UIStateSP.update(self) @@ -203,12 +205,35 @@ class UIState(UIStateSP): self.status = UIStatus.DISENGAGED self.started_frame = self.sm.frame self.started_time = time.monotonic() + self.chestnut_present = self.sm["deviceState"].chestnutPresent for callback in self._offroad_transition_callbacks: callback() self._started_prev = self.started + def _update_chestnut_state(self) -> None: + detected = self.sm["deviceState"].chestnutPresent + if not self.started: + self.chestnut_present = detected + self.chestnut_state = (ChestnutState.READY if detected and self.chestnut_compiled else + ChestnutState.UNCOMPILED if detected else ChestnutState.DISCONNECTED) + return + + model_seen = self.sm.recv_frame["modelV2"] > self.started_frame + if not self.chestnut_present: + self.chestnut_state = ChestnutState.DISCONNECTED + elif not self.chestnut_compiled: + self.chestnut_state = ChestnutState.UNCOMPILED + elif self.chestnut_state == ChestnutState.FAILED or not detected or (model_seen and (not self.sm.alive["modelV2"] or not self.sm["modelV2"].big)): + self.chestnut_state = ChestnutState.FAILED + elif self.chestnut_loading or not model_seen: + self.chestnut_state = ChestnutState.LOADING + elif self.chestnut_active is False: + self.chestnut_state = ChestnutState.FAILED + else: + self.chestnut_state = ChestnutState.ACTIVE + def update_params(self) -> None: # For slower operations # Update longitudinal control state @@ -225,12 +250,10 @@ class UIState(UIStateSP): self.always_on_dm = self.params.get_bool("AlwaysOnDM") self.experimental_mode = self.params.get_bool("ExperimentalMode") self.experimental_mode_confirmed = self.params.get_bool("ExperimentalModeConfirmed") - # keep usbgpu UI active until offroad transition when gpu disappears - self.usbgpu = self.sm["deviceState"].chestnutPresent or (self.usbgpu and self.started) - if not self.usbgpu_compiled: - self.usbgpu_compiled = usbgpu_compiled() - self.usbgpu_active = self.params.get("UsbGpuActive") - self.usbgpu_loading = self.params.get_bool("UsbGpuLoading") + if not self.chestnut_compiled: + self.chestnut_compiled = chestnut_compiled() + self.chestnut_active = self.params.get("ChestnutActive") + self.chestnut_loading = self.params.get_bool("ChestnutLoading") UIStateSP.update_params(self) diff --git a/openpilot/sunnypilot/modeld_v2/compile_modeld.py b/openpilot/sunnypilot/modeld_v2/compile_modeld.py index 17687908c8..7a58352c36 100755 --- a/openpilot/sunnypilot/modeld_v2/compile_modeld.py +++ b/openpilot/sunnypilot/modeld_v2/compile_modeld.py @@ -298,7 +298,7 @@ def _load_policy_runners(args: argparse.Namespace) -> tuple[list, list]: if __name__ == "__main__": - if 'USB' in os.getenv('DEV', '') or os.getenv('USBGPU'): + if 'USB' in os.getenv('DEV', '') or os.getenv('CHESTNUT'): from openpilot.system.hardware.chestnut.flash import link_up for _ in range(10): if link_up(): diff --git a/openpilot/sunnypilot/modeld_v2/modeld.py b/openpilot/sunnypilot/modeld_v2/modeld.py index d180012279..d9c04d7824 100755 --- a/openpilot/sunnypilot/modeld_v2/modeld.py +++ b/openpilot/sunnypilot/modeld_v2/modeld.py @@ -9,7 +9,7 @@ See the LICENSE.md file in the root directory for more details. import os os.environ['GMMU'] = '0' from openpilot.common.hardware import COMMA_HARDWARE -from openpilot.selfdrive.modeld.helpers import usbgpu_present, load_oob +from openpilot.selfdrive.modeld.helpers import chestnut_present, load_oob import time import numpy as np import openpilot.cereal.messaging as messaging @@ -84,14 +84,14 @@ class ModelState(ModelStateBase): inputs: dict[str, np.ndarray] prev_desire: np.ndarray - def __init__(self, cam_w: int, cam_h: int, usbgpu: bool = False): + def __init__(self, cam_w: int, cam_h: int, chestnut: bool = False): ModelStateBase.__init__(self) env_pkl = os.environ.get('COMBINED_MODEL_PKL') if env_pkl and os.path.exists(env_pkl): model_bundle = None else: - model_bundle = get_active_bundle(usbgpu=usbgpu) + model_bundle = get_active_bundle(chestnut=chestnut) self.generation = model_bundle.generation if model_bundle is not None else None overrides = {override.key: override.value for override in model_bundle.overrides} if model_bundle else {} @@ -99,7 +99,7 @@ class ModelState(ModelStateBase): self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0")) self.MIN_LAT_CONTROL_SPEED = 0.3 self.PLANPLUS_CONTROL: float = 1.0 - self.usbgpu = usbgpu + self.chestnut = chestnut pkl_path = _find_driving_pkl(model_bundle) assert pkl_path is not None, "No driving pkl found — all models must be compiled with compile_modeld.py" @@ -110,7 +110,7 @@ class ModelState(ModelStateBase): jits = load_oob(open_file_chunked(pkl_path)) self.WARP_DEV = 'QCOM' if COMMA_HARDWARE else 'CPU' - self.DEV = 'AMD' if self.usbgpu else self.WARP_DEV + self.DEV = 'AMD' if self.chestnut else self.WARP_DEV self.QUEUE_DEV = self.DEV metadata = jits['metadata'] @@ -185,7 +185,7 @@ class ModelState(ModelStateBase): else: self.warp(**{k: self.input_queues[k] for k in WARP_INPUTS}, frame=frame_tensor, big_frame=big_frame_tensor) - if self.usbgpu: + if self.chestnut: self.warmup() def warmup(self) -> None: @@ -287,7 +287,7 @@ class ModelState(ModelStateBase): buf[0, :-1] = buf[0, 1:] buf[0, -1, :] = outputs['desired_curvature'][0, :] if not self.mlsim else 0 - if self.usbgpu and not np.all(np.isfinite(outputs.get('plan', np.array([0.])))): + if self.chestnut and not np.all(np.isfinite(outputs.get('plan', np.array([0.])))): cloudlog.error("model output not finite, dropping frame") return None @@ -327,13 +327,13 @@ def main(demo=False): setproctitle(PROCESS_NAME) config_realtime_process(7, 54) - USBGPU = usbgpu_present() - if USBGPU: + CHESTNUT = chestnut_present() + if CHESTNUT: os.environ['HCQDEV_WAIT_TIMEOUT_MS'] = '3000' params = Params() - params.put_bool("UsbGpuLoading", USBGPU) - params.remove("UsbGpuActive") + params.put_bool("ChestnutLoading", CHESTNUT) + params.remove("ChestnutActive") # visionipc clients while True: @@ -362,31 +362,31 @@ def main(demo=False): st = time.monotonic() model = None - if USBGPU: + if CHESTNUT: import threading def load(): nonlocal model - model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, usbgpu=True) + model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=True) t = threading.Thread(target=load, daemon=True) t.start() t.join(60) if model is None: - params.put_bool("UsbGpuActive", False) - raise RuntimeError("eGPU model load failed or timed out (60s)") - params.put_bool("UsbGpuActive", True) + params.put_bool("ChestnutActive", False) + raise RuntimeError("chestnut model load failed or timed out (60s)") + params.put_bool("ChestnutActive", True) else: - model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, usbgpu=False) + model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=False) - params.put_bool("UsbGpuLoading", False) + params.put_bool("ChestnutLoading", False) cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting") # messaging - pub_socks = ["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"] + (["chestnutState"] if USBGPU else []) + pub_socks = ["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"] + (["chestnutState"] if CHESTNUT else []) pm = PubMaster(pub_socks) sm = SubMaster(["deviceState", "carState", "narrowRoadCameraState", "extrinsicsCalibration", "driverMonitoringState", "carControl", "lateralDelay"]) publish_state = PublishState() - chestnut_state = ChestnutState(pm, USBGPU) if USBGPU else None + chestnut_state = ChestnutState(pm, CHESTNUT) if CHESTNUT else None # setup filter to track dropped frames frame_dropped_filter = FirstOrderFilter(0., 10., 1. / model.constants.MODEL_FREQ) @@ -524,7 +524,7 @@ def main(demo=False): fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, meta_constants) - modelv2_send.modelV2.big = model.usbgpu + modelv2_send.modelV2.big = model.chestnut desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/openpilot/sunnypilot/modeld_v2/tests/helpers.py b/openpilot/sunnypilot/modeld_v2/tests/helpers.py index 6e66bf771a..82e159a305 100644 --- a/openpilot/sunnypilot/modeld_v2/tests/helpers.py +++ b/openpilot/sunnypilot/modeld_v2/tests/helpers.py @@ -190,8 +190,8 @@ def tmp_path(): def patch_modeld(monkeypatch): def _patch(bundle): - monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None, *, usbgpu=None: bundle) - monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None, *, usbgpu=None: bundle) + monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None, *, chestnut=None: bundle) + monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None, *, chestnut=None: bundle) return _patch diff --git a/openpilot/sunnypilot/modeld_v2/tests/test_combined_pkl_loader.py b/openpilot/sunnypilot/modeld_v2/tests/test_combined_pkl_loader.py index ccd8cbc7f3..7f40c19eb3 100644 --- a/openpilot/sunnypilot/modeld_v2/tests/test_combined_pkl_loader.py +++ b/openpilot/sunnypilot/modeld_v2/tests/test_combined_pkl_loader.py @@ -59,8 +59,8 @@ class TestFindDrivingPkl(OpenpilotTestCase): class TestModelStateCombinedInit(OpenpilotTestCase): def test_asserts_when_no_pkl(self, monkeypatch): bundle = DummyBundle(models=[], is_20hz=True) - monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None, *, usbgpu=None: bundle) - monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None, *, usbgpu=None: bundle) + monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None, *, chestnut=None: bundle) + monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None, *, chestnut=None: bundle) with self.assertRaisesRegex(AssertionError, "No driving pkl found"): ModelState(cam_w=CAM_W, cam_h=CAM_H) diff --git a/openpilot/sunnypilot/models/default_model.py b/openpilot/sunnypilot/models/default_model.py index a8e55f8e69..69f08c2cd3 100755 --- a/openpilot/sunnypilot/models/default_model.py +++ b/openpilot/sunnypilot/models/default_model.py @@ -9,8 +9,8 @@ from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MO def get_default_model() -> str: - show_big_model = (ui_state.usbgpu - and (ui_state.usbgpu_active or ui_state.usbgpu_loading or ui_state.is_offroad())) + show_big_model = (ui_state.chestnut_present + and (ui_state.chestnut_active or ui_state.chestnut_loading or ui_state.is_offroad())) return DEFAULT_BIG_MODEL if show_big_model else DEFAULT_MODEL diff --git a/openpilot/sunnypilot/models/fetcher.py b/openpilot/sunnypilot/models/fetcher.py index 1bbfb02f70..d0115be045 100644 --- a/openpilot/sunnypilot/models/fetcher.py +++ b/openpilot/sunnypilot/models/fetcher.py @@ -139,11 +139,11 @@ 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_v21.json" - MODEL_URL_USBGPU = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_usbgpu_v22.json" + MODEL_URL_CHESTNUT = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_chestnut_v22.json" MODEL_SOURCES = { "qcom": (MODEL_URL, ""), - "usbgpu": (MODEL_URL_USBGPU, "_USBGPU"), + "chestnut": (MODEL_URL_CHESTNUT, "_Chestnut"), } def __init__(self, params: Params): @@ -156,12 +156,12 @@ class ModelFetcher: self._refetched: set[str] = set() self.params.put("ModelManager_ActiveJson", { "qcom": self.MODEL_URL, - "usbgpu": self.MODEL_URL_USBGPU, + "chestnut": self.MODEL_URL_CHESTNUT, }, block=True) @staticmethod def active_source(chestnut_present: bool) -> str: - return "usbgpu" if chestnut_present else "qcom" + return "chestnut" if chestnut_present else "qcom" def _fetch_and_cache_models(self, source: str) -> list[custom.ModelManagerSP.ModelBundle] | None: """Fetches fresh model data from remote and updates cache. @@ -200,7 +200,7 @@ class ModelFetcher: @staticmethod def _cache_matches_source(source: str, cached_data: dict) -> bool: bundles = cached_data.get("bundles", []) - if source == "usbgpu": + if source == "chestnut": return any(bundle.get("is_big") is True for bundle in bundles) return not any(bundle.get("is_big") is True for bundle in bundles) @@ -261,10 +261,10 @@ def get_cached_bundles(params: Params, source: str) -> list[custom.ModelManagerS if __name__ == "__main__": - from openpilot.selfdrive.modeld.helpers import usbgpu_present + from openpilot.selfdrive.modeld.helpers import chestnut_present params = Params() model_fetcher = ModelFetcher(params) - bundles = model_fetcher.get_bundles_for_source(ModelFetcher.active_source(usbgpu_present())) + bundles = model_fetcher.get_bundles_for_source(ModelFetcher.active_source(chestnut_present())) for bundle in bundles: for model in bundle.models: model_overrides = {override.key: override.value for override in bundle.overrides} diff --git a/openpilot/sunnypilot/models/helpers.py b/openpilot/sunnypilot/models/helpers.py index 707b86f722..3c3cc1107b 100644 --- a/openpilot/sunnypilot/models/helpers.py +++ b/openpilot/sunnypilot/models/helpers.py @@ -16,7 +16,7 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.sunnypilot.models.constants import Meta, MetaSimPose, MetaTombRaider from openpilot.common.hardware.hw import Paths -from openpilot.selfdrive.modeld.helpers import usbgpu_present +from openpilot.selfdrive.modeld.helpers import chestnut_present # SET ME TO THE EXACT JSON VERSION WE SET IN SUNNYPILOT_MODELS REPO REQUIRED_JSON_VERSION = 18 @@ -27,7 +27,7 @@ ModelManager = custom.ModelManagerSP ACTIVE_BUNDLE_KEYS = { "qcom": "ModelManager_ActiveBundle", - "usbgpu": "ModelManager_ActiveBundleUSBGPU", + "chestnut": "ModelManager_ActiveBundleChestnut", } _LAST_VALIDATED_RAW: dict[str, dict | None] = {} @@ -126,20 +126,20 @@ def get_selected_bundle(params: Params | None = None, source: str = "qcom") -> " return _parse_active_bundle(params.get(ACTIVE_BUNDLE_KEYS[source])) -def get_active_source(usbgpu: bool | None = None, usbgpu_active: bool | None = None, - usbgpu_loading: bool | None = None, offroad: bool | None = None) -> str: - if usbgpu is None: - usbgpu = usbgpu_present() - state_valid = usbgpu_active is not None or usbgpu_loading is not None or offroad is not None - big_active = usbgpu and (not state_valid or usbgpu_active or usbgpu_loading or offroad) - return "usbgpu" if big_active else "qcom" +def get_active_source(chestnut: bool | None = None, chestnut_active: bool | None = None, + chestnut_loading: bool | None = None, offroad: bool | None = None) -> str: + if chestnut is None: + chestnut = chestnut_present() + state_valid = chestnut_active is not None or chestnut_loading is not None or offroad is not None + big_active = chestnut and (not state_valid or chestnut_active or chestnut_loading or offroad) + return "chestnut" if big_active else "qcom" -def get_active_bundle(params: Params | None = None, *, usbgpu: bool | None = None) -> "custom.ModelManagerSP.ModelBundle | None": +def get_active_bundle(params: Params | None = None, *, chestnut: bool | None = None) -> "custom.ModelManagerSP.ModelBundle | None": # no cross-slot fallback: an empty active slot means the hardware default, which # only stock modeld can run - modeld_v2 requires a real bundle params = params or Params() - return get_selected_bundle(params, get_active_source(usbgpu=usbgpu)) + return get_selected_bundle(params, get_active_source(chestnut=chestnut)) def resolve_bundle_by_ref( diff --git a/openpilot/sunnypilot/models/manager.py b/openpilot/sunnypilot/models/manager.py index 178d6c04e5..16253db0ff 100644 --- a/openpilot/sunnypilot/models/manager.py +++ b/openpilot/sunnypilot/models/manager.py @@ -40,7 +40,7 @@ class ModelManagerSP: self.available_models: list[custom.ModelManagerSP.ModelBundle] = [] self.source_models: dict[str, list[custom.ModelManagerSP.ModelBundle]] = {} self.selected_bundle: custom.ModelManagerSP.ModelBundle = None - self.active_bundle: custom.ModelManagerSP.ModelBundle = get_active_bundle(self.params, usbgpu=self.chestnut_present) + self.active_bundle: custom.ModelManagerSP.ModelBundle = get_active_bundle(self.params, chestnut=self.chestnut_present) self._chunk_size = 128 * 1000 # 128 KB chunks self._download_start_times: dict[str, float] = {} # Track start time per model self._download_ref: bytes | str | None = None @@ -280,7 +280,7 @@ class ModelManagerSP: raise DownloadCancelled("Download cancelled") self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded self.params.put(ACTIVE_BUNDLE_KEYS[source], model_bundle.to_dict(), block=True) - self.active_bundle = get_active_bundle(self.params, usbgpu=self.chestnut_present) + self.active_bundle = get_active_bundle(self.params, chestnut=self.chestnut_present) except Exception: if self.selected_bundle is not None: @@ -326,7 +326,7 @@ class ModelManagerSP: self.source_models = {source: self.model_fetcher.get_bundles_for_source(source) for source in ModelFetcher.MODEL_SOURCES} self.available_models = self.source_models[ModelFetcher.active_source(self.chestnut_present)] validate_active_bundles(self.params, self.source_models) - self.active_bundle = get_active_bundle(self.params, usbgpu=self.chestnut_present) + self.active_bundle = get_active_bundle(self.params, chestnut=self.chestnut_present) self._process_download_requests() diff --git a/openpilot/sunnypilot/models/tests/test_manager_download.py b/openpilot/sunnypilot/models/tests/test_manager_download.py index 4d3b7989fb..489e0bc096 100644 --- a/openpilot/sunnypilot/models/tests/test_manager_download.py +++ b/openpilot/sunnypilot/models/tests/test_manager_download.py @@ -392,7 +392,7 @@ class TestManagerDownload(ManagerDownloadTestBase): asyncio.run(self.manager._download_bundle(self._bundle, self.dest, "qcom")) assert "ModelManager_ActiveBundle" in store, "qcom download must write the qcom slot" - assert "ModelManager_ActiveBundleUSBGPU" not in store, "qcom download must not touch the usbgpu slot" + assert "ModelManager_ActiveBundleChestnut" not in store, "qcom download must not touch the chestnut slot" assert self.manager.selected_bundle.status == custom.ModelManagerSP.DownloadStatus.downloaded assert self.manager.active_bundle is not None and self.manager.active_bundle.ref == "test-ref" assert self.manager.active_bundle.status == custom.ModelManagerSP.DownloadStatus.downloaded @@ -401,18 +401,18 @@ class TestManagerDownload(ManagerDownloadTestBase): assert missing == [], f"chunks missing from the cache: {missing}" self.run_with_server(body) - def test_download_writes_usbgpu_slot(self): - """A download resolved to the usbgpu source writes the usbgpu active bundle slot only.""" + def test_download_writes_chestnut_slot(self): + """A download resolved to the chestnut source writes the chestnut active bundle slot only.""" def body(): self.make_artifact(chunked=True) self._bundle.ref = "big-ref" self._bundle.minimumSelectorVersion = 18 params, store = self._make_params_with_store() self.manager.params = params - asyncio.run(self.manager._download_bundle(self._bundle, self.dest, "usbgpu")) + asyncio.run(self.manager._download_bundle(self._bundle, self.dest, "chestnut")) - assert "ModelManager_ActiveBundleUSBGPU" in store, "usbgpu download must write the usbgpu slot" - assert "ModelManager_ActiveBundle" not in store, "usbgpu download must not touch the qcom slot" + assert "ModelManager_ActiveBundleChestnut" in store, "chestnut download must write the chestnut slot" + assert "ModelManager_ActiveBundle" not in store, "chestnut download must not touch the qcom slot" assert self.manager.selected_bundle.status == custom.ModelManagerSP.DownloadStatus.downloaded self.run_with_server(body) @@ -447,20 +447,20 @@ class TestResolveBundleByRef(OpenpilotTestCase): def test_qcom_ref_resolves_to_qcom_slot(self): small = self._bundle("small") - assert resolve_bundle_by_ref("small", {"qcom": [small], "usbgpu": []}) == (small, "qcom") + assert resolve_bundle_by_ref("small", {"qcom": [small], "chestnut": []}) == (small, "qcom") - def test_usbgpu_ref_resolves_to_usbgpu_slot(self): + def test_chestnut_ref_resolves_to_chestnut_slot(self): big = self._bundle("big") - assert resolve_bundle_by_ref("big", {"qcom": [], "usbgpu": [big]}) == (big, "usbgpu") + assert resolve_bundle_by_ref("big", {"qcom": [], "chestnut": [big]}) == (big, "chestnut") def test_unknown_ref_returns_none(self): - source_bundles = {"qcom": [self._bundle("small")], "usbgpu": []} + source_bundles = {"qcom": [self._bundle("small")], "chestnut": []} assert resolve_bundle_by_ref("nope", source_bundles) is None def manifest_bundle(short_name: str, ref: str, index: int = 0, is_big: bool = False) -> dict: """Minimal manifest bundle dict, version-compatible (no chunks to avoid disk side effects). - Big (usbgpu) bundles carry `is_big: true` in the manifest JSON.""" + Big (chestnut) bundles carry `is_big: true` in the manifest JSON.""" return { "index": index, "short_name": short_name, @@ -489,15 +489,15 @@ class TestModelFetcherSources(OpenpilotTestCase): """Both manifests are always maintained: get_bundles_for_source exposes either source by name, and active_source picks which one matches the attached hardware.""" - def _make_params(self, qcom_manifest, usbgpu_manifest): + def _make_params(self, qcom_manifest, chestnut_manifest): params = mock.MagicMock() def get(key): if key == "ModelManager_ModelsCache": return qcom_manifest - if key == "ModelManager_ModelsCache_USBGPU": - return usbgpu_manifest - if key in ("ModelManager_LastSyncTime", "ModelManager_LastSyncTime_USBGPU"): + if key == "ModelManager_ModelsCache_Chestnut": + return chestnut_manifest + if key in ("ModelManager_LastSyncTime", "ModelManager_LastSyncTime_Chestnut"): return fresh_sync_time() return None @@ -506,14 +506,14 @@ class TestModelFetcherSources(OpenpilotTestCase): def test_active_source_follows_chestnut_presence(self): assert ModelFetcher.active_source(False) == "qcom" - assert ModelFetcher.active_source(True) == "usbgpu" + assert ModelFetcher.active_source(True) == "chestnut" def test_get_bundles_for_source_returns_each_source(self): params = self._make_params({"bundles": [manifest_bundle("small", "aaa")]}, {"bundles": [manifest_bundle("big", "bbb", is_big=True)]}) fetcher = ModelFetcher(params) assert [bundle.ref for bundle in fetcher.get_bundles_for_source("qcom")] == ["aaa"] - assert [bundle.ref for bundle in fetcher.get_bundles_for_source("usbgpu")] == ["bbb"] + assert [bundle.ref for bundle in fetcher.get_bundles_for_source("chestnut")] == ["bbb"] def test_get_bundles_for_source_unknown(self): assert ModelFetcher(mock.MagicMock()).get_bundles_for_source("bogus") == [] @@ -522,16 +522,16 @@ class TestModelFetcherSources(OpenpilotTestCase): params = self._make_params({"bundles": [manifest_bundle("small", "aaa")]}, {"bundles": [manifest_bundle("big", "bbb", is_big=True)]}) qcom_bundles = get_cached_bundles(params, "qcom") - usbgpu_bundles = get_cached_bundles(params, "usbgpu") + chestnut_bundles = get_cached_bundles(params, "chestnut") assert [b.ref for b in qcom_bundles] == ["aaa"] - assert [b.ref for b in usbgpu_bundles] == ["bbb"] + assert [b.ref for b in chestnut_bundles] == ["bbb"] assert qcom_bundles[0].displayName == "SMALL" def test_get_cached_bundles_empty_when_missing(self): params = mock.MagicMock() params.get.return_value = None assert get_cached_bundles(params, "qcom") == [] - assert get_cached_bundles(params, "usbgpu") == [] + assert get_cached_bundles(params, "chestnut") == [] def test_get_cached_bundles_unknown_source(self): assert get_cached_bundles(mock.MagicMock(), "bogus") == [] @@ -543,27 +543,27 @@ class TestModelFetcherSources(OpenpilotTestCase): assert active_json_calls, "expected ModelManager_ActiveJson to be written" assert active_json_calls[-1].args[1] == { "qcom": ModelFetcher.MODEL_URL, - "usbgpu": ModelFetcher.MODEL_URL_USBGPU, + "chestnut": ModelFetcher.MODEL_URL_CHESTNUT, } class TestSourceCacheIntegrity(OpenpilotTestCase): """Each source's cached manifest must contain only that source's models; the - `is_big` flag in the JSON marks the big (usbgpu) models. A mismatched cache is + `is_big` flag in the JSON marks the big (chestnut) models. A mismatched cache is legacy data from before the per-source split (the active manifest was cached under the unsuffixed key regardless of hardware) and is refetched. This replaces the old one-time bundle migration.""" - def _make_params(self, qcom_manifest, usbgpu_manifest): + def _make_params(self, qcom_manifest, chestnut_manifest): params = mock.MagicMock() def get(key): if key == "ModelManager_ModelsCache": return qcom_manifest - if key == "ModelManager_ModelsCache_USBGPU": - return usbgpu_manifest - if key in ("ModelManager_LastSyncTime", "ModelManager_LastSyncTime_USBGPU"): + if key == "ModelManager_ModelsCache_Chestnut": + return chestnut_manifest + if key in ("ModelManager_LastSyncTime", "ModelManager_LastSyncTime_Chestnut"): return fresh_sync_time() return None @@ -584,13 +584,13 @@ class TestSourceCacheIntegrity(OpenpilotTestCase): bundles = fetcher.get_bundles_for_source("qcom") assert [bundle.ref for bundle in bundles] == ["aaa"] - def test_usbgpu_cache_without_big_models_is_refetched(self): + def test_chestnut_cache_without_big_models_is_refetched(self): params = self._make_params({"bundles": [manifest_bundle("small", "aaa")]}, {"bundles": [manifest_bundle("big2", "ccc")]}) fetcher = ModelFetcher(params) fetched = self._fetched(manifest_bundle("big", "bbb", is_big=True)) with mock.patch.object(fetcher, "_fetch_and_cache_models", return_value=fetched): - bundles = fetcher.get_bundles_for_source("usbgpu") + bundles = fetcher.get_bundles_for_source("chestnut") assert [bundle.ref for bundle in bundles] == ["bbb"] def test_matching_caches_are_used_without_fetch(self): @@ -599,7 +599,7 @@ class TestSourceCacheIntegrity(OpenpilotTestCase): fetcher = ModelFetcher(params) with mock.patch.object(fetcher, "_fetch_and_cache_models", side_effect=AssertionError("cache should be used")): assert [bundle.ref for bundle in fetcher.get_bundles_for_source("qcom")] == ["aaa"] - assert [bundle.ref for bundle in fetcher.get_bundles_for_source("usbgpu")] == ["bbb"] + assert [bundle.ref for bundle in fetcher.get_bundles_for_source("chestnut")] == ["bbb"] def test_stale_version_cache_is_refetched(self): """A source-matching cache whose bundles are all filtered by the selector version @@ -660,28 +660,28 @@ class TestActiveBundleValidation(OpenpilotTestCase): bundle.runner = runner return bundle.to_dict() - def _params(self, qcom=None, usbgpu=None): + def _params(self, qcom=None, chestnut=None): params = mock.MagicMock() def get(key, *args, **kwargs): - return {"ModelManager_ActiveBundle": qcom, "ModelManager_ActiveBundleUSBGPU": usbgpu}.get(key) + return {"ModelManager_ActiveBundle": qcom, "ModelManager_ActiveBundleChestnut": chestnut}.get(key) params.get.side_effect = get return params def test_empty_catalog_does_not_reset_slot(self): params = self._params(qcom=self._raw_bundle("small")) - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=False): - validate_active_bundles(params, {"qcom": [], "usbgpu": []}) + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=False): + validate_active_bundles(params, {"qcom": [], "chestnut": []}) params.remove.assert_not_called() def test_reset_recomputes_runner_from_surviving_slot(self): tinygrad = int(custom.ModelManagerSP.Runner.tinygrad) big_raw = self._raw_bundle("big", runner=tinygrad) - params = self._params(qcom=self._raw_bundle("gone"), usbgpu=big_raw) + params = self._params(qcom=self._raw_bundle("gone"), chestnut=big_raw) catalog = {"qcom": [custom.ModelManagerSP.ModelBundle(**self._raw_bundle("other"))], - "usbgpu": [custom.ModelManagerSP.ModelBundle(**big_raw)]} - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=True): + "chestnut": [custom.ModelManagerSP.ModelBundle(**big_raw)]} + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=True): validate_active_bundles(params, catalog) params.remove.assert_called_once_with("ModelManager_ActiveBundle") runner_puts = [call for call in params.put.call_args_list if call.args[0] == "ModelRunnerTypeCache"] @@ -689,7 +689,7 @@ class TestActiveBundleValidation(OpenpilotTestCase): class TestActiveBundleSelection(OpenpilotTestCase): - """The effective active bundle is the active source's slot: usbgpu when a GPU is + """The effective active bundle is the active source's slot: chestnut when a GPU is present, qcom otherwise. An empty active slot means the hardware default (stock runner), never the other slot's pick - modeld_v2 requires a real bundle.""" @@ -700,37 +700,37 @@ class TestActiveBundleSelection(OpenpilotTestCase): bundle.minimumSelectorVersion = 18 return bundle.to_dict() - def _params(self, qcom=None, usbgpu=None): + def _params(self, qcom=None, chestnut=None): params = mock.MagicMock() def get(key, *args, **kwargs): if key == "ModelManager_ActiveBundle": return qcom - if key == "ModelManager_ActiveBundleUSBGPU": - return usbgpu + if key == "ModelManager_ActiveBundleChestnut": + return chestnut return None params.get.side_effect = get return params def test_selected_bundle_is_per_slot(self): - params = self._params(qcom=self._raw_bundle("small"), usbgpu=self._raw_bundle("big")) + params = self._params(qcom=self._raw_bundle("small"), chestnut=self._raw_bundle("big")) assert get_selected_bundle(params, "qcom").ref == "small" - assert get_selected_bundle(params, "usbgpu").ref == "big" + assert get_selected_bundle(params, "chestnut").ref == "big" def test_no_gpu_uses_qcom_slot(self): - params = self._params(qcom=self._raw_bundle("small"), usbgpu=self._raw_bundle("big")) - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=False): + params = self._params(qcom=self._raw_bundle("small"), chestnut=self._raw_bundle("big")) + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=False): assert get_active_bundle(params).ref == "small" - def test_gpu_uses_usbgpu_slot(self): - params = self._params(qcom=self._raw_bundle("small"), usbgpu=self._raw_bundle("big")) - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=True): + def test_gpu_uses_chestnut_slot(self): + params = self._params(qcom=self._raw_bundle("small"), chestnut=self._raw_bundle("big")) + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=True): assert get_active_bundle(params).ref == "big" def test_gpu_without_big_selection_is_hardware_default(self): - params = self._params(qcom=self._raw_bundle("small"), usbgpu=None) - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=True): + params = self._params(qcom=self._raw_bundle("small"), chestnut=None) + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=True): assert get_active_bundle(params) is None @@ -748,36 +748,36 @@ class TestEffectiveSource(OpenpilotTestCase): return bundle.to_dict() def test_runtime_no_gpu(self): - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=False): + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=False): assert get_active_source() == "qcom" def test_runtime_gpu_present(self): - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=True): - assert get_active_source() == "usbgpu" + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=True): + assert get_active_source() == "chestnut" def test_display_offroad_gpu_present_shows_big(self): - assert get_active_source(usbgpu=True, usbgpu_active=False, usbgpu_loading=False, offroad=True) == "usbgpu" + assert get_active_source(chestnut=True, chestnut_active=False, chestnut_loading=False, offroad=True) == "chestnut" def test_display_onroad_gpu_loading_shows_big(self): - assert get_active_source(usbgpu=True, usbgpu_active=False, usbgpu_loading=True, offroad=False) == "usbgpu" + assert get_active_source(chestnut=True, chestnut_active=False, chestnut_loading=True, offroad=False) == "chestnut" def test_display_onroad_gpu_active_shows_big(self): - assert get_active_source(usbgpu=True, usbgpu_active=True, usbgpu_loading=False, offroad=False) == "usbgpu" + assert get_active_source(chestnut=True, chestnut_active=True, chestnut_loading=False, offroad=False) == "chestnut" def test_display_onroad_gpu_idle_shows_small(self): - assert get_active_source(usbgpu=True, usbgpu_active=False, usbgpu_loading=False, offroad=False) == "qcom" + assert get_active_source(chestnut=True, chestnut_active=False, chestnut_loading=False, offroad=False) == "qcom" def test_display_active_none_is_idle(self): - assert get_active_source(usbgpu=True, usbgpu_active=None, usbgpu_loading=False, offroad=False) == "qcom" + assert get_active_source(chestnut=True, chestnut_active=None, chestnut_loading=False, offroad=False) == "qcom" def test_active_bundle_follows_source(self): params = mock.MagicMock() params.get.side_effect = lambda key: {"ModelManager_ActiveBundle": self._raw_bundle("small"), - "ModelManager_ActiveBundleUSBGPU": self._raw_bundle("big")}.get(key) - with mock.patch("openpilot.sunnypilot.models.helpers.usbgpu_present", return_value=False): + "ModelManager_ActiveBundleChestnut": self._raw_bundle("big")}.get(key) + with mock.patch("openpilot.sunnypilot.models.helpers.chestnut_present", return_value=False): assert get_active_bundle(params).ref == "small" - assert get_selected_bundle(params, get_active_source(usbgpu=True, usbgpu_active=False, - usbgpu_loading=False, offroad=True)).ref == "big" + assert get_selected_bundle(params, get_active_source(chestnut=True, chestnut_active=False, + chestnut_loading=False, offroad=True)).ref == "big" @unittest.skipUnless(os.environ.get('RUN_INTEGRATION_TESTS'), 'requires external network') diff --git a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py index 1ab2f373ed..c31e4ac711 100755 --- a/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py +++ b/openpilot/sunnypilot/sunnylink/athena/sunnylinkd.py @@ -183,7 +183,7 @@ def getParamsMetadata() -> str: schema["capability_labels"] = CAPABILITY_LABELS schema["default_model"] = DEFAULT_MODEL schema["default_big_model"] = DEFAULT_BIG_MODEL - schema["usbgpu_active"] = params.get_bool("UsbGpuActive") + schema["chestnut_active"] = params.get_bool("ChestnutActive") raw = json.dumps(schema, separators=(",", ":")).encode("utf-8") return base64.b64encode(gzip.compress(raw)).decode("utf-8") except Exception: diff --git a/openpilot/sunnypilot/sunnylink/statsd.py b/openpilot/sunnypilot/sunnylink/statsd.py index a221fc084f..9d01ac07fd 100755 --- a/openpilot/sunnypilot/sunnylink/statsd.py +++ b/openpilot/sunnypilot/sunnylink/statsd.py @@ -65,7 +65,7 @@ def sp_stats(end_event): 'MadsSteeringMode', 'MadsUnifiedEngagementMode', 'ModelManager_ActiveBundle', - 'ModelManager_ActiveBundleUSBGPU', + 'ModelManager_ActiveBundleChestnut', 'ModelManager_Favs', 'EnableSunnylinkUploader', 'SunnylinkEnabled', diff --git a/openpilot/sunnypilot/system/params_migration.py b/openpilot/sunnypilot/system/params_migration.py index f0f0d7248a..41cad9ad28 100644 --- a/openpilot/sunnypilot/system/params_migration.py +++ b/openpilot/sunnypilot/system/params_migration.py @@ -89,12 +89,16 @@ def _migrate_model_bundle_slots(_params): # ActiveBundle. Seed both slots; validation drops whichever does not match # its own manifest. try: - if _params.get("ModelManager_ActiveBundleUSBGPU") is not None: + if _params.get("ModelManager_ActiveBundleChestnut") is not None: + return + if (chestnut_bundle := _params.get("ModelManager_ActiveBundleUSBGPU")) is not None: + _params.put("ModelManager_ActiveBundleChestnut", chestnut_bundle, block=True) + cloudlog.info("params_migration: seeded ModelManager_ActiveBundleChestnut from ModelManager_ActiveBundleUSBGPU") return if (bundle := _params.get("ModelManager_ActiveBundle")) is None: return - _params.put("ModelManager_ActiveBundleUSBGPU", bundle, block=True) - cloudlog.info("params_migration: seeded ModelManager_ActiveBundleUSBGPU from ModelManager_ActiveBundle") + _params.put("ModelManager_ActiveBundleChestnut", bundle, block=True) + cloudlog.info("params_migration: seeded ModelManager_ActiveBundleChestnut from ModelManager_ActiveBundle") except Exception as e: cloudlog.exception(f"Error migrating model bundle slots: {e}") @@ -136,5 +140,5 @@ def run_migration(_params): # seed TeslaMadsScreenButton for existing Tesla installs _migrate_tesla_mads_screen_button(_params) - # seed the usbgpu model slot from the pre-split single slot + # seed the chestnut model slot from the pre-split single slot _migrate_model_bundle_slots(_params) diff --git a/openpilot/sunnypilot/system/tests/test_params_migration.py b/openpilot/sunnypilot/system/tests/test_params_migration.py index 328a7a65af..683c9dcdc7 100644 --- a/openpilot/sunnypilot/system/tests/test_params_migration.py +++ b/openpilot/sunnypilot/system/tests/test_params_migration.py @@ -15,22 +15,22 @@ class TestModelBundleSlotMigration(OpenpilotTestCase): The migration seeds both slots; per-source validation later drops whichever does not match its own manifest.""" - def test_seeds_usbgpu_slot_from_active_bundle(self): + def test_seeds_chestnut_slot_from_active_bundle(self): params = Params() bundle = {"ref": "big", "minimumSelectorVersion": 18} params.put("ModelManager_ActiveBundle", bundle, block=True) _migrate_model_bundle_slots(params) - assert params.get("ModelManager_ActiveBundleUSBGPU") == bundle + assert params.get("ModelManager_ActiveBundleChestnut") == bundle assert params.get("ModelManager_ActiveBundle") == bundle - def test_noop_when_usbgpu_slot_already_set(self): + def test_noop_when_chestnut_slot_already_set(self): params = Params() params.put("ModelManager_ActiveBundle", {"ref": "small"}, block=True) - params.put("ModelManager_ActiveBundleUSBGPU", {"ref": "big"}, block=True) + params.put("ModelManager_ActiveBundleChestnut", {"ref": "big"}, block=True) _migrate_model_bundle_slots(params) - assert params.get("ModelManager_ActiveBundleUSBGPU") == {"ref": "big"} + assert params.get("ModelManager_ActiveBundleChestnut") == {"ref": "big"} def test_noop_when_no_selection(self): params = Params() _migrate_model_bundle_slots(params) - assert params.get("ModelManager_ActiveBundleUSBGPU") is None + assert params.get("ModelManager_ActiveBundleChestnut") is None diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 8aed8be5b9..dd9c92269b 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -16,7 +16,7 @@ from openpilot.common.utils import strip_deprecated_keys from openpilot.common.filter_simple import FirstOrderFilter from openpilot.common.params import Params from openpilot.common.realtime import DT_HW -from openpilot.selfdrive.modeld.helpers import MODELS_DIR, usbgpu_compiled +from openpilot.selfdrive.modeld.helpers import MODELS_DIR, chestnut_compiled from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.common.hardware import HARDWARE, COMMA_HARDWARE from openpilot.common.basedir import BASEDIR @@ -239,7 +239,7 @@ def hardware_thread(end_event, hw_queue) -> None: fan_controller = FanController(int(1./DT_HW)) chestnut = Chestnut() - big_model_available = (MODELS_DIR / 'big_driving_supercombo.onnx').is_file() or usbgpu_compiled() + big_model_available = (MODELS_DIR / 'big_driving_supercombo.onnx').is_file() or chestnut_compiled() while not end_event.is_set(): sm.update(PANDA_STATES_TIMEOUT)