f9fcc7adab
date: 2026-06-28T09:48:35 master commit: da6313dbe95b3f24bb5d8018b0e5f950f5823ca7
481 lines
21 KiB
Python
Executable File
481 lines
21 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
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.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import pickle
|
|
import time
|
|
from functools import partial
|
|
from collections import defaultdict
|
|
|
|
import numpy as np
|
|
from tinygrad.tensor import Tensor
|
|
from tinygrad.device import Device
|
|
from tinygrad.engine.jit import TinyJit
|
|
|
|
from openpilot.selfdrive.modeld.compile_modeld import (
|
|
NV12Frame, make_frame_prepare,
|
|
shift_and_sample, sample_skip, sample_desire,
|
|
)
|
|
|
|
MODEL_TYPES = ('vision_policy', 'supercombo', 'vision_multi_policy')
|
|
|
|
|
|
def _detect_desire_key(policy_input_shapes):
|
|
for k in policy_input_shapes:
|
|
if k.startswith('desire'):
|
|
return k
|
|
return None
|
|
|
|
|
|
def _detect_vision_keys(vision_input_shapes):
|
|
img_keys = sorted([k for k in vision_input_shapes if 'img' in k])
|
|
road_key = next((k for k in img_keys if 'big' not in k), None)
|
|
wide_key = next((k for k in img_keys if 'big' in k), None)
|
|
if road_key is None or wide_key is None:
|
|
raise ValueError(f"Cannot determine road/wide image keys from {list(vision_input_shapes.keys())}")
|
|
return road_key, wide_key
|
|
|
|
|
|
def make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, device):
|
|
road_key, _ = _detect_vision_keys(vision_input_shapes)
|
|
img = vision_input_shapes[road_key]
|
|
n_frames = img[1] // 6
|
|
img_buf_shape = (frame_skip * (n_frames - 1) + 1, 6, img[2], img[3])
|
|
|
|
fb = policy_input_shapes['features_buffer']
|
|
desire_key = _detect_desire_key(policy_input_shapes)
|
|
dp = policy_input_shapes[desire_key]
|
|
tc = policy_input_shapes.get('traffic_convention', (1, 2))
|
|
|
|
npy = {
|
|
'desire': np.zeros(dp[2], dtype=np.float32),
|
|
'traffic_convention': np.zeros(tc, dtype=np.float32),
|
|
'tfm': np.zeros((3, 3), dtype=np.float32),
|
|
'big_tfm': np.zeros((3, 3), dtype=np.float32),
|
|
}
|
|
|
|
handled = {'features_buffer', desire_key, 'traffic_convention'}
|
|
for key, shape in policy_input_shapes.items():
|
|
if key in handled:
|
|
continue
|
|
npy[key] = np.zeros(shape, dtype=np.float32)
|
|
|
|
input_queues = {
|
|
'img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
|
|
'big_img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
|
|
'feat_q': Tensor(np.zeros((frame_skip * (fb[1] - 1) + 1, fb[0], fb[2]), dtype=np.float32), device=device).contiguous().realize(),
|
|
'desire_q': Tensor(np.zeros((frame_skip * dp[1], dp[0], dp[2]), dtype=np.float32), device=device).contiguous().realize(),
|
|
**{k: Tensor(v, device='NPY').realize() for k, v in npy.items()},
|
|
}
|
|
return input_queues, npy
|
|
|
|
|
|
def make_run_split_policy(vision_runner, policy_runner, nv12: NV12Frame, model_w, model_h,
|
|
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
|
|
vision_road_key, vision_wide_key, prepare_only=False):
|
|
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
|
|
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
|
|
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
|
|
|
|
def run_policy(img_q, big_img_q, feat_q, desire_q, desire, traffic_convention, tfm, big_tfm, frame, big_frame, **extra):
|
|
npy_tensors = [tfm.to(Device.DEFAULT), big_tfm.to(Device.DEFAULT),
|
|
desire.to(Device.DEFAULT), traffic_convention.to(Device.DEFAULT)]
|
|
extra_device = {k: extra[k].to(Device.DEFAULT) for k in extra_policy_keys}
|
|
Tensor.realize(*npy_tensors, *extra_device.values())
|
|
tfm, big_tfm, desire, traffic_convention = npy_tensors
|
|
|
|
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
|
|
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
|
|
|
|
if prepare_only:
|
|
return img, big_img
|
|
|
|
vision_out = next(iter(vision_runner({vision_road_key: img, vision_wide_key: big_img}).values())).cast('float32')
|
|
|
|
new_feat = vision_out[:, vision_features_slice].reshape(1, -1).unsqueeze(0)
|
|
feat_buf = shift_and_sample(feat_q, new_feat, sample_skip_fn)
|
|
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
|
|
|
|
inputs = {'features_buffer': feat_buf, desire_key: desire_buf, 'traffic_convention': traffic_convention, **extra_device}
|
|
policy_out = next(iter(policy_runner(inputs).values())).cast('float32')
|
|
|
|
return vision_out, policy_out
|
|
return run_policy
|
|
|
|
|
|
def compile_split_policy(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
|
|
vision_runner, policy_runner, vision_metadata, policy_metadata):
|
|
print(f"Compiling combined policy JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
|
|
|
|
vision_features_slice = vision_metadata['output_slices']['hidden_state']
|
|
vision_input_shapes = vision_metadata['input_shapes']
|
|
policy_input_shapes = policy_metadata['input_shapes']
|
|
desire_key = _detect_desire_key(policy_input_shapes)
|
|
extra_policy_keys = [k for k in policy_input_shapes if k not in ('features_buffer', desire_key, 'traffic_convention')]
|
|
vision_road_key, vision_wide_key = _detect_vision_keys(vision_input_shapes)
|
|
|
|
_run = make_run_split_policy(vision_runner, policy_runner, nv12, model_w, model_h,
|
|
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
|
|
vision_road_key, vision_wide_key, prepare_only)
|
|
run_policy_jit = TinyJit(_run, prune=True)
|
|
|
|
SEED = 42
|
|
|
|
def random_inputs_run_fn(fn, seed, test_val=None, test_buffers=None, expect_match=True):
|
|
input_queues, npy = make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, Device.DEFAULT)
|
|
np.random.seed(seed)
|
|
Tensor.manual_seed(seed)
|
|
|
|
testing = test_val is not None or test_buffers is not None
|
|
n_runs = 1 if testing else 3
|
|
|
|
for i in range(n_runs):
|
|
frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
|
|
big_frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
|
|
for v in npy.values():
|
|
v[:] = np.random.randn(*v.shape).astype(v.dtype)
|
|
Device.default.synchronize()
|
|
st = time.perf_counter()
|
|
outs = fn(**input_queues, frame=frame, big_frame=big_frame)
|
|
mt = time.perf_counter()
|
|
Device.default.synchronize()
|
|
et = time.perf_counter()
|
|
print(f" [{i+1}/{n_runs}] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms")
|
|
|
|
if i == 0:
|
|
val = [np.copy(v.numpy()) for v in outs]
|
|
buffers = [np.copy(v.numpy().copy()) for v in input_queues.values()]
|
|
|
|
if test_val is not None:
|
|
match = all(np.array_equal(a, b) for a, b in zip(val, test_val, strict=True))
|
|
assert match == expect_match, f"outputs {'differ from' if expect_match else 'match'} baseline (seed={seed})"
|
|
if test_buffers is not None:
|
|
match = all(np.array_equal(a, b) for a, b in zip(buffers, test_buffers, strict=True))
|
|
assert match == expect_match, f"buffers {'differ from' if expect_match else 'match'} baseline (seed={seed})"
|
|
return fn, val, buffers
|
|
|
|
print('capture + replay')
|
|
run_policy_jit, test_val, test_buffers = random_inputs_run_fn(run_policy_jit, SEED)
|
|
|
|
print('pickle round trip')
|
|
run_policy_jit = pickle.loads(pickle.dumps(run_policy_jit))
|
|
random_inputs_run_fn(run_policy_jit, SEED, test_val, test_buffers, expect_match=True)
|
|
random_inputs_run_fn(run_policy_jit, SEED+1, test_val, test_buffers, expect_match=False)
|
|
return run_policy_jit
|
|
|
|
|
|
def derive_frame_skip(vision_input_shapes, policy_input_shapes):
|
|
fb = policy_input_shapes.get('features_buffer')
|
|
if fb is None:
|
|
return 1
|
|
fb_history = fb[1]
|
|
if fb_history >= 99:
|
|
return 1
|
|
return 4
|
|
|
|
|
|
def make_supercombo_input_queues(input_shapes, frame_skip, device):
|
|
img_shape = input_shapes.get('img', input_shapes.get('input_imgs'))
|
|
if img_shape is None:
|
|
raise ValueError("No img input found in model shapes")
|
|
|
|
n_frames = img_shape[1] // 6
|
|
img_buf_shape = (frame_skip * (n_frames - 1) + 1, 6, img_shape[2], img_shape[3])
|
|
|
|
numpy_keys = {}
|
|
queue_keys = {}
|
|
|
|
for key, shape in input_shapes.items():
|
|
if 'img' in key:
|
|
continue
|
|
if len(shape) == 3 and shape[1] > 1:
|
|
if key.startswith('desire'):
|
|
numpy_keys[key] = np.zeros(shape[2], dtype=np.float32)
|
|
queue_keys[f'{key}_q'] = Tensor(
|
|
np.zeros((frame_skip * shape[1], shape[0], shape[2]), dtype=np.float32),
|
|
device=device).contiguous().realize()
|
|
elif key == 'features_buffer':
|
|
queue_keys['feat_q'] = Tensor(
|
|
np.zeros((frame_skip * (shape[1] - 1) + 1, shape[0], shape[2]), dtype=np.float32),
|
|
device=device).contiguous().realize()
|
|
else:
|
|
numpy_keys[key] = np.zeros(shape, dtype=np.float32)
|
|
elif len(shape) == 2:
|
|
numpy_keys[key] = np.zeros(shape, dtype=np.float32)
|
|
|
|
if 'traffic_convention' not in numpy_keys:
|
|
tc_shape = input_shapes.get('traffic_convention', (1, 2))
|
|
numpy_keys['traffic_convention'] = np.zeros(tc_shape, dtype=np.float32)
|
|
|
|
numpy_keys['tfm'] = np.zeros((3, 3), dtype=np.float32)
|
|
numpy_keys['big_tfm'] = np.zeros((3, 3), dtype=np.float32)
|
|
|
|
input_queues = {
|
|
'img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
|
|
'big_img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
|
|
**queue_keys,
|
|
**{k: Tensor(v, device='NPY').realize() for k, v in numpy_keys.items()},
|
|
}
|
|
return input_queues, numpy_keys
|
|
|
|
|
|
def make_run_supercombo(model_runner, nv12: NV12Frame, model_w, model_h,
|
|
features_slice, frame_skip, input_shapes, prepare_only=False):
|
|
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
|
|
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
|
|
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
|
|
|
|
desire_key = _detect_desire_key(input_shapes)
|
|
if desire_key is None:
|
|
raise ValueError(f"No desire* key found in input_shapes: {list(input_shapes.keys())}")
|
|
road_img_key, wide_img_key = _detect_vision_keys(input_shapes)
|
|
extra_policy_keys = [k for k in input_shapes
|
|
if k not in (desire_key, 'features_buffer', 'traffic_convention')
|
|
and 'img' not in k]
|
|
|
|
def run_supercombo(img_q, big_img_q, feat_q, desire_q,
|
|
frame, big_frame, **kwargs):
|
|
desire = kwargs.get(desire_key)
|
|
traffic_convention = kwargs.get('traffic_convention')
|
|
tfm = kwargs['tfm']
|
|
big_tfm = kwargs['big_tfm']
|
|
|
|
tfm = tfm.to(Device.DEFAULT)
|
|
big_tfm = big_tfm.to(Device.DEFAULT)
|
|
desire = desire.to(Device.DEFAULT)
|
|
traffic_convention = traffic_convention.to(Device.DEFAULT)
|
|
Tensor.realize(tfm, big_tfm, desire, traffic_convention)
|
|
|
|
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
|
|
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
|
|
|
|
if prepare_only:
|
|
return img, big_img
|
|
|
|
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
|
|
feat_buf = sample_skip_fn(feat_q)
|
|
|
|
inputs = {road_img_key: img, wide_img_key: big_img,
|
|
desire_key: desire_buf, 'features_buffer': feat_buf,
|
|
'traffic_convention': traffic_convention}
|
|
for k in extra_policy_keys:
|
|
if k in kwargs:
|
|
inputs[k] = kwargs[k].to(Device.DEFAULT)
|
|
|
|
model_out = next(iter(model_runner(inputs).values())).cast('float32')
|
|
|
|
new_feat = model_out[:, features_slice].reshape(1, -1).unsqueeze(0)
|
|
shift_and_sample(feat_q, new_feat, sample_skip_fn)
|
|
|
|
return model_out
|
|
|
|
return run_supercombo
|
|
|
|
|
|
def make_run_vision_multi_policy(vision_runner, policy_runners, nv12: NV12Frame, model_w, model_h,
|
|
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
|
|
vision_road_key, vision_wide_key, prepare_only=False):
|
|
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
|
|
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
|
|
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
|
|
|
|
def run_multi_policy(img_q, big_img_q, feat_q, desire_q, desire,
|
|
traffic_convention, tfm, big_tfm, frame, big_frame, **extra):
|
|
npy_tensors = [tfm.to(Device.DEFAULT), big_tfm.to(Device.DEFAULT),
|
|
desire.to(Device.DEFAULT), traffic_convention.to(Device.DEFAULT)]
|
|
extra_device = {k: extra[k].to(Device.DEFAULT) for k in extra_policy_keys}
|
|
Tensor.realize(*npy_tensors, *extra_device.values())
|
|
tfm, big_tfm, desire, traffic_convention = npy_tensors
|
|
|
|
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
|
|
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
|
|
|
|
if prepare_only:
|
|
return img, big_img
|
|
|
|
vision_out = next(iter(vision_runner({vision_road_key: img, vision_wide_key: big_img}).values())).cast('float32')
|
|
|
|
new_feat = vision_out[:, vision_features_slice].reshape(1, -1).unsqueeze(0)
|
|
feat_buf = shift_and_sample(feat_q, new_feat, sample_skip_fn)
|
|
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
|
|
|
|
inputs = {'features_buffer': feat_buf, desire_key: desire_buf, 'traffic_convention': traffic_convention, **extra_device}
|
|
|
|
policy_outputs = []
|
|
for runner in policy_runners:
|
|
policy_out = next(iter(runner(inputs).values())).cast('float32')
|
|
policy_outputs.append(policy_out)
|
|
|
|
return (vision_out, *policy_outputs)
|
|
|
|
return run_multi_policy
|
|
|
|
|
|
def _warmup_and_serialize(run_jit, input_queues, npy, nv12):
|
|
for i in range(3):
|
|
np.random.seed(42 + i)
|
|
frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
|
|
big_frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
|
|
for v in npy.values():
|
|
v[:] = np.random.randn(*v.shape).astype(v.dtype)
|
|
Device.default.synchronize()
|
|
st = time.perf_counter()
|
|
run_jit(**input_queues, frame=frame, big_frame=big_frame)
|
|
mt = time.perf_counter()
|
|
Device.default.synchronize()
|
|
et = time.perf_counter()
|
|
print(f" [{i + 1}/3] enqueue {(mt - st) * 1e3:6.2f} ms -- total {(et - st) * 1e3:6.2f} ms")
|
|
return pickle.loads(pickle.dumps(run_jit))
|
|
|
|
|
|
def compile_supercombo(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
|
|
model_runner, metadata):
|
|
print(f"Compiling combined supercombo JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
|
|
|
|
features_slice = metadata['output_slices']['hidden_state']
|
|
input_shapes = metadata['input_shapes']
|
|
|
|
_run = make_run_supercombo(model_runner, nv12, model_w, model_h,
|
|
features_slice, frame_skip, input_shapes, prepare_only)
|
|
run_jit = TinyJit(_run, prune=True)
|
|
|
|
input_queues, npy = make_supercombo_input_queues(input_shapes, frame_skip, Device.DEFAULT)
|
|
|
|
run_jit = _warmup_and_serialize(run_jit, input_queues, npy, nv12)
|
|
return run_jit
|
|
|
|
|
|
def compile_multi_policy(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
|
|
vision_runner, policy_runners, vision_metadata, policy_metadata):
|
|
print(f"Compiling combined multi-policy JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
|
|
|
|
vision_features_slice = vision_metadata['output_slices']['hidden_state']
|
|
vision_input_shapes = vision_metadata['input_shapes']
|
|
policy_input_shapes = policy_metadata['input_shapes']
|
|
desire_key = _detect_desire_key(policy_input_shapes)
|
|
extra_policy_keys = [k for k in policy_input_shapes if k not in ('features_buffer', desire_key, 'traffic_convention')]
|
|
vision_road_key, vision_wide_key = _detect_vision_keys(vision_input_shapes)
|
|
|
|
_run = make_run_vision_multi_policy(vision_runner, policy_runners, nv12, model_w, model_h,
|
|
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
|
|
vision_road_key, vision_wide_key, prepare_only)
|
|
run_jit = TinyJit(_run, prune=True)
|
|
|
|
input_queues, npy = make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, Device.DEFAULT)
|
|
|
|
run_jit = _warmup_and_serialize(run_jit, input_queues, npy, nv12)
|
|
return run_jit
|
|
|
|
|
|
def _parse_size(s):
|
|
w, h = s.lower().split('x')
|
|
return int(w), int(h)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from tinygrad.nn.onnx import OnnxRunner
|
|
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
|
from openpilot.selfdrive.modeld.get_model_metadata import make_metadata_dict
|
|
|
|
p = argparse.ArgumentParser(description="Compile combined JIT pkl for sunnypilot modeld_v2")
|
|
p.add_argument('--model-type', choices=MODEL_TYPES, required=True)
|
|
p.add_argument('--model-size', type=_parse_size, required=True, help='model input WxH')
|
|
p.add_argument('--camera-resolutions', type=_parse_size, nargs='+', required=True)
|
|
p.add_argument('--frame-skip', type=int, default=None, help='frame skip value (auto-derived if not provided)')
|
|
p.add_argument('--output', required=True)
|
|
|
|
p.add_argument('--vision-onnx', help='vision ONNX (for split models)')
|
|
p.add_argument('--policy-onnx', help='policy ONNX (for vision_policy)')
|
|
p.add_argument('--off-policy-onnx', help='off-policy ONNX (for vision_multi_policy)')
|
|
p.add_argument('--on-policy-onnx', help='on-policy ONNX (for vision_multi_policy)')
|
|
p.add_argument('--supercombo-onnx', help='supercombo ONNX (for supercombo)')
|
|
|
|
args = p.parse_args()
|
|
out = defaultdict(dict)
|
|
|
|
if args.model_type == 'vision_policy':
|
|
assert args.vision_onnx and args.policy_onnx
|
|
vision_runner = OnnxRunner(args.vision_onnx)
|
|
policy_runner = OnnxRunner(args.policy_onnx)
|
|
out['metadata']['vision'] = make_metadata_dict(args.vision_onnx)
|
|
out['metadata']['policy'] = make_metadata_dict(args.policy_onnx)
|
|
|
|
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip(out['metadata']['vision']['input_shapes'],
|
|
out['metadata']['policy']['input_shapes'])
|
|
|
|
for cam_w, cam_h in args.camera_resolutions:
|
|
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
|
|
model_w, model_h = args.model_size
|
|
out[(cam_w, cam_h)] = {
|
|
name: compile_split_policy(nv12, model_w, model_h, prepare_only, frame_skip,
|
|
vision_runner, policy_runner,
|
|
out['metadata']['vision'], out['metadata']['policy'])
|
|
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
|
|
}
|
|
|
|
elif args.model_type == 'supercombo':
|
|
assert args.supercombo_onnx
|
|
model_runner = OnnxRunner(args.supercombo_onnx)
|
|
out['metadata']['model'] = make_metadata_dict(args.supercombo_onnx)
|
|
|
|
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip({}, out['metadata']['model']['input_shapes'])
|
|
|
|
for cam_w, cam_h in args.camera_resolutions:
|
|
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
|
|
model_w, model_h = args.model_size
|
|
out[(cam_w, cam_h)] = {
|
|
name: compile_supercombo(nv12, model_w, model_h, prepare_only, frame_skip,
|
|
model_runner, out['metadata']['model'])
|
|
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
|
|
}
|
|
|
|
elif args.model_type == 'vision_multi_policy':
|
|
assert args.vision_onnx
|
|
vision_runner = OnnxRunner(args.vision_onnx)
|
|
out['metadata']['vision'] = make_metadata_dict(args.vision_onnx)
|
|
|
|
policy_runners = []
|
|
policy_onnxes = []
|
|
if args.policy_onnx:
|
|
policy_onnxes.append(('policy', args.policy_onnx))
|
|
if args.off_policy_onnx:
|
|
policy_onnxes.append(('off_policy', args.off_policy_onnx))
|
|
if args.on_policy_onnx:
|
|
policy_onnxes.append(('on_policy', args.on_policy_onnx))
|
|
|
|
for name, onnx_path in policy_onnxes:
|
|
runner = OnnxRunner(onnx_path)
|
|
policy_runners.append(runner)
|
|
out['metadata'][name] = make_metadata_dict(onnx_path)
|
|
|
|
first_policy_key = policy_onnxes[0][0]
|
|
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip(out['metadata']['vision']['input_shapes'],
|
|
out['metadata'][first_policy_key]['input_shapes'])
|
|
|
|
for cam_w, cam_h in args.camera_resolutions:
|
|
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
|
|
model_w, model_h = args.model_size
|
|
out[(cam_w, cam_h)] = {
|
|
name: compile_multi_policy(nv12, model_w, model_h, prepare_only, frame_skip,
|
|
vision_runner, policy_runners,
|
|
out['metadata']['vision'], out['metadata'][first_policy_key])
|
|
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
|
|
}
|
|
|
|
with open(args.output, "wb") as f:
|
|
pickle.dump(out, f)
|
|
pkl_size = os.path.getsize(args.output)
|
|
print(f"Saved combined JIT to {args.output} ({pkl_size / 1e6:.2f} MB)")
|
|
|
|
from openpilot.common.file_chunker import chunk_file, get_chunk_targets
|
|
chunk_targets = get_chunk_targets(args.output, pkl_size)
|
|
chunk_file(args.output, chunk_targets)
|
|
num_chunks = len(chunk_targets) - 1
|
|
print(f"Chunked into {num_chunks} file(s)")
|