mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-09-07 08:43:40 +08:00
1a8dd310ae
* Add support for TinyGrad model runner processing Introduced a new function `is_tinygrad_model` to detect TinyGrad as an active model runner. Updated the `is_stock_model` logic to account for TinyGrad models and added a new process entry for TinyGrad in the model manager. This enables handling TinyGrad models alongside existing configurations. adding modeld back Add support for `modeld_v2` and update paths for consistency Updated `SConscript` files to integrate `modeld_v2` alongside `modeld` and adjusted script paths for correct metadata handling. Adjusted various configurations and scripts, such as `labeler.yaml` and `build_release.sh`, to include `modeld_v2` and ensure cohesive project structure. Refactor imports to use updated `modeld_v2` paths. Replaced outdated `modeld` references with their `modeld_v2` counterparts for consistency and clarity across the codebase. Also updated `.gitignore` to accommodate new directory structure. This change ensures better maintainability and alignment with the new directory schema. Refactor and reorganize modeld to sunnypilot/modeld_v2 structure. Moved and renamed `modeld` components to the new `sunnypilot/modeld_v2` directory for better organization and modularity. Updated imports and file references to align with the new structure, ensuring compatibility and functionality. Streamlined project structure to improve maintainability and future development. * typo * Use `stock` model runner and refactor model checks. Replaces outdated model detection logic with unified `stock` runner integration, simplifying the decision flow for model selection. Includes `stock` as a new enum in the `Runner` type and updates affected references accordingly. * Handle missing 'sim_pose' in model outputs gracefully. Added conditional checks to ensure the code handles cases where 'sim_pose' is absent in the model outputs. Fallback behaviors use 'plan' data when 'sim_pose' is unavailable, preventing potential errors and enhancing robustness.
135 lines
5.2 KiB
Python
135 lines
5.2 KiB
Python
import os
|
|
import pickle
|
|
from abc import ABC, abstractmethod
|
|
import numpy as np
|
|
|
|
from cereal import custom
|
|
from openpilot.sunnypilot.modeld_v2 import MODEL_PATH, MODEL_PKL_PATH, METADATA_PATH
|
|
from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem
|
|
from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES
|
|
from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
|
from openpilot.system.hardware import TICI
|
|
from openpilot.system.hardware.hw import Paths
|
|
|
|
from openpilot.sunnypilot.models.helpers import get_active_bundle
|
|
from tinygrad.tensor import Tensor
|
|
|
|
if TICI:
|
|
os.environ['QCOM'] = '1'
|
|
|
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
|
CUSTOM_MODEL_PATH = Paths.model_root()
|
|
ModelManager = custom.ModelManagerSP
|
|
|
|
|
|
class ModelRunner(ABC):
|
|
"""Abstract base class for model runners that defines the interface for running ML models."""
|
|
|
|
def __init__(self):
|
|
"""Initialize the model runner with paths to model and metadata files."""
|
|
metadata_path = METADATA_PATH
|
|
self.is_20hz = None
|
|
self._drive_model = None
|
|
self._metadata_model = None
|
|
|
|
if bundle := get_active_bundle():
|
|
bundle_models = {model.type.raw: model for model in bundle.models}
|
|
self._drive_model = bundle_models.get(ModelManager.Type.drive)
|
|
self._metadata_model = bundle_models.get(ModelManager.Type.metadata)
|
|
self.is_20hz = bundle.is20hz
|
|
|
|
# Override the metadata path if a metadata model is found in the active bundle
|
|
if self._metadata_model:
|
|
metadata_path = f"{CUSTOM_MODEL_PATH}/{self._metadata_model.fileName}"
|
|
|
|
with open(metadata_path, 'rb') as f:
|
|
self.model_metadata = pickle.load(f)
|
|
|
|
self.input_shapes = self.model_metadata['input_shapes']
|
|
self.output_slices = self.model_metadata['output_slices']
|
|
self.inputs: dict = {}
|
|
|
|
@abstractmethod
|
|
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
|
|
"""Prepare inputs for model inference."""
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def run_model(self):
|
|
"""Run model inference with prepared inputs."""
|
|
|
|
def slice_outputs(self, model_outputs: np.ndarray) -> dict:
|
|
"""Slice model outputs according to metadata configuration."""
|
|
parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()}
|
|
if SEND_RAW_PRED:
|
|
parsed_outputs['raw_pred'] = model_outputs.copy()
|
|
return parsed_outputs
|
|
|
|
|
|
class TinygradRunner(ModelRunner):
|
|
"""Tinygrad implementation of model runner for TICI hardware."""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
model_pkl_path = MODEL_PKL_PATH
|
|
if self._drive_model:
|
|
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.fileName}"
|
|
assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner"
|
|
|
|
# Load Tinygrad model
|
|
with open(model_pkl_path, "rb") as f:
|
|
try:
|
|
self.model_run = pickle.load(f)
|
|
except FileNotFoundError as e:
|
|
assert "/dev/kgsl-3d0" not in str(e), "Model was built on C3 or C3X, but is being loaded on PC"
|
|
raise
|
|
|
|
self.input_to_dtype = {}
|
|
self.input_to_device = {}
|
|
|
|
for idx, name in enumerate(self.model_run.captured.expected_names):
|
|
self.input_to_dtype[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][2] # 2 is the dtype
|
|
self.input_to_device[name] = self.model_run.captured.expected_st_vars_dtype_device[idx][3] # 3 is the device
|
|
|
|
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
|
|
# Initialize image tensors if not already done
|
|
for key in imgs_cl:
|
|
if TICI and key not in self.inputs:
|
|
self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=self.input_to_dtype[key])
|
|
elif not TICI:
|
|
shape = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
|
|
self.inputs[key] = Tensor(shape, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
|
|
|
|
# Update numpy inputs
|
|
for key, value in numpy_inputs.items():
|
|
if key not in imgs_cl:
|
|
self.inputs[key] = Tensor(value, device=self.input_to_device[key], dtype=self.input_to_dtype[key]).realize()
|
|
|
|
return self.inputs
|
|
|
|
def run_model(self):
|
|
return self.model_run(**self.inputs).numpy().flatten()
|
|
|
|
|
|
class ONNXRunner(ModelRunner):
|
|
"""ONNX implementation of model runner for non-TICI hardware."""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.runner = make_onnx_cpu_runner(MODEL_PATH)
|
|
|
|
self.input_to_nptype = {
|
|
model_input.name: ORT_TYPES_TO_NP_TYPES[model_input.type]
|
|
for model_input in self.runner.get_inputs()
|
|
}
|
|
|
|
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
|
|
self.inputs = numpy_inputs
|
|
for key in imgs_cl:
|
|
self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key])
|
|
return self.inputs
|
|
|
|
def run_model(self):
|
|
return self.runner.run(None, self.inputs)[0].flatten()
|