modeld 构建:6 通道 NV12 warp 与 256 字节对齐的带外权重

作者:

在

结论:modeld 的构建产物链把三件事固定在一起:ONNX 模型编译(compile_onnx.py,--out-of-band)、相机布局相关的 warp 编译(道路 YUV420 双帧、座舱 luma 单帧)与运行期输入打包。道路 warp 把 NV12 拆成 Y 四相位加两个色度面,拼成模型的 6 通道张量;座舱 warp 只取 Y 平面并用 16 填充边框。要记住两个边界:构建脚本的种子 42/43 只是自洽检查,没有与独立 ONNX Runtime 或实车真值比较;工件是可执行对象序列化而非纯数据,不能把未认证的工件交给 pickle.load()。道路与座舱两条路径的缓冲所有权与过期帧风险不同,不能用道路双帧上传的验证代替座舱零拷贝验证。

构建入口:三条后端路径与产物矩阵

SConscript 在非 comma ARM64、非 Darwin 平台缺 libLLVM 时跳过模型编译。comma ARM64 用 DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1,Darwin 用 DEV=METAL JIT=2,其他平台用 DEV=CPU:LLVM。Darwin 分支的注释明确说明 JIT=2 是为了禁用在 buffer 变化后可能产生错误输出的 graph batching。这些标志写入构建动作和依赖,不能在不同后端之间只重命名 .pkl 产物。

产物 构建调用 运行期消费者
道路模型 driving_tinygrad.pkl 与座舱模型 dmonitoring_model_tinygrad.pkl compile_model() 调 compile_onnx.py,使用 --out-of-band --benchmark-runs 1。 modeld.ModelState.__init__() 与 dmonitoringmodeld.ModelState.__init__() 经 load_oob() 载入。
每种相机尺寸的道路 warp SConscript 调 compile_warp.py --layout yuv420 --frames 2,frame size 按 stride 与 Y/UV 分配高度求得。 modeld 以 img/big_img 两帧和两个逆变换调用捕获图。
每种相机尺寸的座舱 warp SConscript 调 --layout luma --border-fill 16 --transform-device NPY。 dmonitoringmodeld.ModelState.run() 用 VisionIPC 缓冲指针和主机变换矩阵执行。

这里有一个显式的产物缺口:该 SConscript 没有定义 Chestnut 的 big_driving_tinygrad.pkl 或 big_driving_warp_* 构建动作,chestnut_compiled() 只检查这些文件是否存在。它们的来源和与当前模型的兼容性,不能由普通模型构建链推定。

ONNX 捕获与序列化:自洽检查不等于数值正确

onnx_metadata() 只扫描 ONNX 顶层 metadata 和 output 声明,不读取权重节点,计算由 OnnxRunner 负责。主程序从 graph input 推出 input_specs,先用随机填充输入运行一次得到 output_specs,再把 metadata、input_shapes、output_shapes、捕获的 run 与规格一起写入工件。运行时道路模型还从 metadata 的 output_slices 取切片,因此模型元数据和解析器版本属于同一接口契约。

run() 以 TinyJit(prune=True) 捕获,对传入的 output_buffers 执行赋值并物化。构建脚本用种子 42 的固定随机输入作捕获、两次重复一致性和反序列化后一致性检查,并要求种子 43 的不同输入使输出发生变化,以避免完全忽略输入。但这些都只是自洽检查:没有与独立 ONNX Runtime、车辆标注或实车真值比较,--benchmark-runs 1 也不构成尾延迟统计。

序列化层面,dump_pickle() 对默认设备上的 Buffer 使用 persistent_id,把内容按 256 字节对齐存到带外区域;load_pickle() 建立目标设备 arena,并按 32 MiB 逻辑块复制后恢复视图。这里的 pickle 是可执行对象序列化而非纯数据格式,自制插件不能把未认证的工件交给 pickle.load()。--retargetable 可去掉 CPU 程序中的二进制以便重新编译,但常规模型动作未传此参数。

NV12 warp 的几何:逆 3×3、最近邻与越界填充

warp_perspective_tinygrad() 对目标像素用逆 3×3 矩阵求源坐标,以 Tensor.round 做最近邻采样,先把索引裁到图像边界;若传 border_fill_val,再依取整后的坐标是否越界决定填充。它没有在本函数中显式处理齐次分母 src_w=0、NaN 或非法矩阵,上游标定/变换有效性必须单独校验。普通道路 warp 不给 border_fill,因此越界像素采用夹边采样;座舱 warp 明确用 16 填充。

make_frame_prepare() 用 frame stride 与 Y 分配高度确定 UV 偏移,把 NV12 交错 UV 拆成 U/V,按缩放后的逆矩阵各自透视采样,再拼成 YUV420 面;frames_to_tensor() 把四个 Y 相位面和两个色度面排列成模型的 6 通道张量。它依赖偶数模型尺寸、相机 stride 与 Y/UV 分配高度同 get_nv12_info() 一致。道路运行时 ModelState.pack_inputs() 把两个帧的 Y/UV 有效分配区和浮点辅助输入放在一个 host upload 中,而不是复制包含尾部 padding 的整个 VisionIPC 缓冲;因此更换相机、NV12 pitch 或 buffer 长度须连同构建 warp、输入切片和运行时复制大小一起更新。

两条运行路径的缓冲所有权不同

make_luma_warp() 只取 NV12 的 Y 平面;座舱运行时在 ModelState.run() 中缓存 Tensor.from_blob(),让它指向 VisionIPC 帧内存,不复制完整帧。这与道路双帧的 host upload 路径在所有权和过期帧风险上都不一样,见下表。

维度 道路模型(双帧 YUV420) 座舱模型(单帧 luma)
输入布局 四个 Y 相位面 + 两个色度面,共 6 通道 仅 Y 平面,边框用 16 填充
越界处理 不给 border_fill,夹边采样 明确 --border-fill 16
上传方式 把两帧 Y/UV 有效分配区与浮点辅助输入合并为一次 host upload 缓存 Tensor.from_blob() 指向 VisionIPC 帧内存,零拷贝
主要风险 复制大小与 NV12 pitch/stride 强耦合 帧内存所有权与过期帧

移植边界

源文档给出的做法是:目标硬件应固定 ONNX、元数据、相机尺寸/stride、逆变换、tinygrad 提交和设备编译器版本,先建立独立 CPU 数值基准;再比较道路 YUV420 六通道、座舱 luma 填充、模型输入/输出形状、跨帧状态与不同后端输出,最后测帧级尾延迟及断流/非法矩阵回退。核心原则是一句话:算法产物可插件化,但工件加载信任、结果有效性和车辆安全控制许可不能交由插件自证。本轮仅核对构建命令、源码和运行期消费,没有执行 ONNX/warp 构建,也未取得 LFS 模型实体。

常见问题

构建脚本里的数值一致性检查能证明模型正确吗?

不能。种子 42 的捕获与一致性检查、种子 43 的输入敏感性检查都只是自洽检查,源文档明确它们没有与独立 ONNX Runtime、车辆标注或实车真值比较,--benchmark-runs 1 也不构成尾延迟统计。

道路 warp 和座舱 warp 的关键差别是什么?

道路 warp 用 --layout yuv420 --frames 2,把 NV12 拆成四个 Y 相位面加两个色度面凑成 6 通道,越界走夹边采样;座舱 warp 用 --layout luma --border-fill 16,只取 Y 平面并用 16 填充边框。两者的缓冲所有权与过期帧风险不同。

warp 函数会处理非法变换矩阵吗?

不会。warp_perspective_tinygrad() 用逆 3×3 矩阵求源坐标、Tensor.round 最近邻采样并裁到边界,但源文档明确指出它没有在本函数中显式处理齐次分母 src_w=0、NaN 或非法矩阵,上游标定/变换有效性必须单独校验。

模型工件可以直接 pickle 加载吗?

不可以随意加载。源文档说明这里的 pickle 是可执行对象序列化而非纯数据格式,自制插件不能把未认证的工件交给 pickle.load()。权重经 dump_pickle() 按 256 字节对齐存到带外区域,由 load_pickle() 建立 arena 后按 32 MiB 逻辑块复制恢复。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/MODELD_BUILD_ARTIFACT_WARP_CONTRACT_DEEP_DIVE.md(取用了构建后端标志与产物矩阵、ONNX 捕获与序列化边界、NV12 warp 几何与两条运行路径的缓冲所有权)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注