mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-06-22 01:32:07 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40d9e092b6 | |||
| 6318aa52e3 | |||
| 7852fa66b1 | |||
| 8d9c1e2035 | |||
| c39f722f7b | |||
| 550c08ac4c | |||
| 8c838af5fa | |||
| 2521d60b1b | |||
| 206398d3b5 | |||
| a9e99615cd | |||
| ec44b78ad8 | |||
| dcd3e09294 | |||
| 839a7a58e0 |
+11
-49
@@ -1,21 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
from openpilot.system.hardware import TICI
|
from openpilot.system.hardware import TICI
|
||||||
|
|
||||||
|
from openpilot.selfdrive.modeld.runners.model_runner import ONNXRunner, TinygradRunner
|
||||||
|
|
||||||
#
|
#
|
||||||
if TICI:
|
|
||||||
from tinygrad.tensor import Tensor
|
|
||||||
from tinygrad.dtype import dtypes
|
|
||||||
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
|
||||||
os.environ['QCOM'] = '1'
|
|
||||||
else:
|
|
||||||
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner
|
|
||||||
import time
|
import time
|
||||||
import pickle
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import cereal.messaging as messaging
|
import cereal.messaging as messaging
|
||||||
from cereal import car, log
|
from cereal import car, log
|
||||||
from pathlib import Path
|
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
from cereal.messaging import PubMaster, SubMaster
|
from cereal.messaging import PubMaster, SubMaster
|
||||||
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
|
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
|
||||||
@@ -33,13 +25,8 @@ from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_
|
|||||||
from openpilot.selfdrive.modeld.constants import ModelConstants
|
from openpilot.selfdrive.modeld.constants import ModelConstants
|
||||||
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
|
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
|
||||||
|
|
||||||
|
|
||||||
PROCESS_NAME = "selfdrive.modeld.modeld"
|
PROCESS_NAME = "selfdrive.modeld.modeld"
|
||||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
|
||||||
|
|
||||||
MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx'
|
|
||||||
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
|
|
||||||
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
|
|
||||||
|
|
||||||
class FrameMeta:
|
class FrameMeta:
|
||||||
frame_id: int = 0
|
frame_id: int = 0
|
||||||
@@ -69,27 +56,12 @@ class ModelState:
|
|||||||
'features_buffer': np.zeros((1, ModelConstants.HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
|
'features_buffer': np.zeros((1, ModelConstants.HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(METADATA_PATH, 'rb') as f:
|
# Initialize model runner
|
||||||
model_metadata = pickle.load(f)
|
self.model_runner = TinygradRunner() if TICI else ONNXRunner(self.frames)
|
||||||
self.input_shapes = model_metadata['input_shapes']
|
|
||||||
|
|
||||||
self.output_slices = model_metadata['output_slices']
|
|
||||||
net_output_size = model_metadata['output_shapes']['outputs'][1]
|
|
||||||
self.output = np.zeros(net_output_size, dtype=np.float32)
|
|
||||||
self.parser = Parser()
|
self.parser = Parser()
|
||||||
|
|
||||||
if TICI:
|
net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1]
|
||||||
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
self.output = np.zeros(net_output_size, dtype=np.float32)
|
||||||
with open(MODEL_PKL_PATH, "rb") as f:
|
|
||||||
self.model_run = pickle.load(f)
|
|
||||||
else:
|
|
||||||
self.onnx_cpu_runner = make_onnx_cpu_runner(MODEL_PATH)
|
|
||||||
|
|
||||||
def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]:
|
|
||||||
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
|
|
||||||
if SEND_RAW_PRED:
|
|
||||||
parsed_model_outputs['raw_pred'] = model_outputs.copy()
|
|
||||||
return parsed_model_outputs
|
|
||||||
|
|
||||||
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
|
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
|
||||||
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
|
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
|
||||||
@@ -106,24 +78,15 @@ class ModelState:
|
|||||||
imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()),
|
imgs_cl = {'input_imgs': self.frames['input_imgs'].prepare(buf, transform.flatten()),
|
||||||
'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())}
|
'big_input_imgs': self.frames['big_input_imgs'].prepare(wbuf, transform_wide.flatten())}
|
||||||
|
|
||||||
if TICI:
|
# Prepare inputs using the model runner
|
||||||
# The imgs tensors are backed by opencl memory, only need init once
|
self.model_runner.prepare_inputs(imgs_cl, self.numpy_inputs)
|
||||||
for key in imgs_cl:
|
|
||||||
if key not in self.tensor_inputs:
|
|
||||||
self.tensor_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
|
|
||||||
else:
|
|
||||||
for key in imgs_cl:
|
|
||||||
self.numpy_inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
|
|
||||||
|
|
||||||
if prepare_only:
|
if prepare_only:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if TICI:
|
# Run model inference
|
||||||
self.output = self.model_run(**self.tensor_inputs).numpy().flatten()
|
self.output = self.model_runner.run_model()
|
||||||
else:
|
outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output))
|
||||||
self.output = self.onnx_cpu_runner.run(None, self.numpy_inputs)[0].flatten()
|
|
||||||
|
|
||||||
outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
|
|
||||||
|
|
||||||
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
|
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
|
||||||
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
|
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
|
||||||
@@ -190,7 +153,6 @@ def main(demo=False):
|
|||||||
meta_main = FrameMeta()
|
meta_main = FrameMeta()
|
||||||
meta_extra = FrameMeta()
|
meta_extra = FrameMeta()
|
||||||
|
|
||||||
|
|
||||||
if demo:
|
if demo:
|
||||||
CP = get_demo_car_params()
|
CP = get_demo_car_params()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import os
|
||||||
|
from openpilot.system.hardware import TICI
|
||||||
|
|
||||||
|
#
|
||||||
|
if TICI:
|
||||||
|
from tinygrad.tensor import Tensor
|
||||||
|
from tinygrad.dtype import dtypes
|
||||||
|
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||||
|
os.environ['QCOM'] = '1'
|
||||||
|
else:
|
||||||
|
from openpilot.selfdrive.modeld.runners.ort_helpers import make_onnx_cpu_runner
|
||||||
|
import pickle
|
||||||
|
import numpy as np
|
||||||
|
from pathlib import Path
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLMem
|
||||||
|
|
||||||
|
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||||
|
MODEL_PATH = Path(__file__).parent / '../models/supercombo.onnx'
|
||||||
|
MODEL_PKL_PATH = Path(__file__).parent / '../models/supercombo_tinygrad.pkl'
|
||||||
|
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
|
||||||
|
|
||||||
|
|
||||||
|
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."""
|
||||||
|
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])-> dict:
|
||||||
|
"""Prepare inputs for model inference."""
|
||||||
|
|
||||||
|
@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__()
|
||||||
|
# Load Tinygrad model
|
||||||
|
with open(MODEL_PKL_PATH, "rb") as f:
|
||||||
|
self.model_run = pickle.load(f)
|
||||||
|
|
||||||
|
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray]) -> dict:
|
||||||
|
# Initialize image tensors if not already done
|
||||||
|
for key in imgs_cl:
|
||||||
|
if key not in self.inputs:
|
||||||
|
self.inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
|
||||||
|
|
||||||
|
# Update numpy inputs
|
||||||
|
for k, v in numpy_inputs.items():
|
||||||
|
if k not in self.inputs:
|
||||||
|
self.inputs[k] = Tensor(v, device='NPY').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, frames: dict[str, DrivingModelFrame]):
|
||||||
|
super().__init__()
|
||||||
|
self.runner = make_onnx_cpu_runner(MODEL_PATH)
|
||||||
|
self.frames = frames
|
||||||
|
|
||||||
|
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray]) -> dict:
|
||||||
|
self.inputs = numpy_inputs.copy()
|
||||||
|
for key in imgs_cl:
|
||||||
|
self.inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
|
||||||
|
return self.inputs
|
||||||
|
|
||||||
|
def run_model(self):
|
||||||
|
return self.runner.run(None, self.inputs)[0].flatten()
|
||||||
Reference in New Issue
Block a user