跳到主要内容

22 端侧部署与打包洞察

  • 章节编号:22
  • 所属层:D 部署与服务层(承接 12 Runtime、20 前端、21 模型)
  • 关联 ADR:ADR-075(端侧制品形态:vmfb+manifest bundle)、ADR-076(打包工具链与依赖)、ADR-077(版本/回滚与 A/B)、ADR-078(与 Yocto/rootfs 集成)
  • 上游依赖:10(Yocto/rootfs)、12(IREE VM)、18(tuning spec)、20(manifest)、21(模型验收)

学习目标

  • 前置知识:读过 21 章(具身模型族与 INT4 内存画像:π0/GR00T 的权重量级与 chunk 延迟靶标)、12 章(IREE VM 与运行时依赖)、18 章(tuning spec 的产出形态)。知道「编译产物 = vmfb + 常量」这条链路的输出是什么,以及「端侧不是一台云服务器」——它没有 Docker daemon、磁盘按 GB 计、每次刷机都要过安全启动。无需 Yocto 实战经验,但要理解「制品(artifact)」与「运行时(runtime)」是两件事。
  • 学完产出:① 能说清一个端侧 bundle 里到底装了什么(manifest / vmfb / 权重 / tuning spec / 签名),以及为什么它要「自描述 + 可校验」;② 能对标 TensorRT engine、QNN context binary、CANN OM 三种闭源制品,讲清「可 git diff 的 spec + manifest」这条差异化护城河的工程价值;③ 能亲手把「bundle 目标 ≤8GB、典型 4GB」这条硬指标算一遍——用模型权重 + runtime + 依赖的实际字节数反推,而不是背一个区间;④ 能在「Yocto 原生集成」与「容器化部署」之间做取舍,说清端侧为什么倾向前者、边缘侧为什么容忍后者;⑤ 能设计一套「版本回滚 + 固件 ABI 契约」机制,理解为什么「回滚 bundle」和「回滚固件」是两个不同量级的操作。
  • 阅读姿势:盯住一条主线——「端侧制品的一切设计,都是为了让一次刷机既能跑得动、又能安全回退」。云上部署可以「重新 build 一个镜像重启」,端侧不行:板子在客户现场、模型有 GB 级权重、刷坏了要人上门。所以本章所有的「自描述 manifest」「A/B 分区」「N-1 保留」「ABI 契约」,本质都在回答同一个问题:当新版本跑挂了,怎么在一次 reboot 内退回上一个能跑的状态

1. 范围与目标

本章定义 编译产物如何打包为端侧可部署单元,含 vmfb、权重、tuning spec、manifest、Runtime 依赖,以及与 rootfs/Yocto 的安装布局。不含 Fleet OTA(23 章)。

核心问题

  1. 端侧 制品(bundle)里有什么?
  2. 对标 TensorRT engine / QNN context binary / CANN OM 我们的等价物?
  3. GR00T 混合部署(PyTorch + AOT)如何打包?
  4. 版本、回滚、多模型并存如何组织?
  5. Demo 部署路径?

2. 需求洞察(具身驱动)

2.1 具体场景

场景打包诉求来源
π0 单模型部署vmfb + INT4 weights + spec + manifest21 章
GR00T 双系统VLM bundle + DiT bundle + link.yaml17 章
客户 POC一条命令 deploy 到 Thor 类板26 章
OTA 增量delta weights/spec23 章
多模型目录隔离 + MIG context(09)12/09

2.2 硬性指标

  • bundle 自描述(manifest+json);Runtime 校验 hash
  • 单 VLA bundle 目标 ≤8GB,典型 4GB(INT4,21 章)——见 §2.4 的字节级拆解。
  • deploy install ≤5min 到目标板。
  • 回滚 ≤1 reboot

2.3 端 / 边 / 中心

形态打包
端侧.dsa-bundle (概念名)
边缘同 bundle + 多 robot 索引
中心container/VM;规模化阶段

2.4 bundle 容量为何 ≤8GB:一次字节级拆解

「bundle ≤8GB」不是拍脑袋的区间,而是从端侧存储预算倒推出来的硬约束。一块典型的具身 SoC 板(Thor 类 / IQ10 类)eMMC/UFS 容量在 64–128GB 量级,扣掉 rootfs、内核、Yocto 系统层、日志、A/B 双分区冗余后,留给「一个 robot profile 的模型制品」的空间通常只剩 十几 GB。而 A/B 回滚要求同时保留 N 与 N-1 两份 bundle——单份的预算就被再砍一半。于是「典型 4GB / 上限 8GB」这条线自然浮现。

我们把一个 π0 级 INT4 单 VLA 的 bundle 逐项算一遍(数量级估算,权重口径对齐 21 章 §2.2):

组成内容典型体积备注
模型权重VLM(PaliGemma 3B / Gemma3 4B)INT4 + 300M 动作专家~2.0–2.6GB4B 参数 × 0.5 byte(INT4)≈ 2GB;动作专家 300M INT4 ≈ 0.15GB
编译产物 vmfbVLM.vmfb + action_head.vmfb(纯代码,不含权重)~50–150MBvmfb 只装 kernel 序列,权重外置 mmap
tuning spec*.tuning.spec.mlir(18 章调优决策)~1–10MB文本 IR,可 gzip
Runtime 依赖IREE runtime + HAL backend 库~200–500MB若随 bundle 附带;走 rootfs 共享则趋近 0
manifest + 签名manifest.yaml + signature.json(hash 列表)<1MB自描述元数据
感知/控制小网 + 缓冲ViT stub + 控制小网~0.3–0.8GB21 章「感知前端 + 控制小网」
合计(典型)~3–4GB落在「典型 4GB」
合计(带满配 FP8 VLM + 双系统)~6–8GB逼近「上限 8GB」

结论口径:典型 π0 INT4 单 VLA 落在 ~4GB;当 VLM 用 FP8(不减半)、或 GR00T 式双系统(VLM + DiT 两份权重共存)时,才逼近 8GB 上限。一旦某个 bundle 超过 8GB,A/B 双份就吃不下端侧存储预算——这就是把上限钉在 8GB 的物理根因。规避手段:权重外置 + mmap(见 §5)、delta OTA(23 章)、多 bundle 间共享 runtime 层(不重复打包)。


3. 技术现状与趋势(点名 + 来源)

3.1 TOP 级端侧部署制品深度对比(2025–2026)

制品可审计签名/加密增量 OTA具身案例优势劣势
TensorRT.engine❌ 二进制可选全量 rebuildGR00T DiT49msopaque;GPU 锁死
TensorRT-LLM / Edge-LLMengine + tokenizer可选困难GR00T VLMLLM 融合强闭源
IREE(我们).vmfb+constants✅ MLIR/spec可签(28)spec/weight delta待建git diff生态小
ExecuTorch 1.0.pte平台分区更新Meta 眼镜/IGPyTorch 原生非全图 VLA
QNNcontext binaryQTEEQIRP OTAIQ10车规封闭
CANN.om可选ATCπ0 310P国产非 manifest
LeRobotcheckpoint/✅ PyTorchHF Hubπ0/OpenVLA生态无 AOT
Docker/OCI镜像✅ Dockerfile签名层 delta边侧通用端侧 >GB 级

时效补注(2026-07):π0.7、GR00T N1.7 已进入 GA;上表「具身案例」列对应的模型代次以官方发布为准。制品格式层面(engine / context binary / om / vmfb)相对稳定,但各栈的 OTA/量化子能力仍在快速演进,以厂商官方 datasheet 与 release note 为准

3.1a 方案深度对比

IREE .dsa-bundle(我们)

优势劣势
manifest + tuning spec + vmfb 均可版本化;与 18/20 同源;支持 INT4 weight mmap客户需学习新 bundle 格式;无 NGC 式商店(靠 24 CDN)
GR00T 式双 bundle + link.yaml 表达混合部署过渡期 PyTorch sidecar 破坏单一制品

TensorRT engine(NVIDIA GR00T)

优势劣势
Builder 自动 fusion;GR00T 公开 92msengine 与 CUDA/driver 强绑定;无法 inspect fusion 决策
ModelOpt 与 engine 一体OTA 常需 完整 re-build

ExecuTorch .pte(Qualcomm/Meta Plan B)

优势劣势
2025 GA;Instagram/WhatsApp 数十亿 部署;QnnPartitioner 简单大 VLA 多步 loop 难单 .pte;与 ROS2 需额外桥
一行换 CPU/QNN/CoreML无 tuning spec

3.1b 打包维度评分(1–5)

维度TRT engineIREE bundleExecuTorchQNN ctxLeRobot ckpt
性能54442
可维护/audit15324
OTA 友好24343
VLA 全图45335(eager)
生态53435

3.2 我们的 bundle 结构(候选)

my_robot_vla.dsa-bundle/
├── manifest.yaml # ADR-073 SSOT
├── models/
│ ├── vlm.vmfb
│ ├── action_head.vmfb
│ └── weights/ # INT4/NVFP4 blobs
├── tuning/
│ └── *.tuning.spec.mlir # 18 章
├── runtime/
│ └── iree-runtime.conf # HAL backend, power_profile
└── signature.json # hashes

为什么权重与 vmfb 分离:vmfb 只装 kernel 序列与调度图,不内嵌权重;权重以独立 blob 存在 weights/ 下,Runtime 用 mmap 按需映射。这么做有两个直接收益——(1)delta OTA:换一版 tuning spec 或修一个 kernel,只推 vmfb(几十 MB),不必重传 GB 级权重;(2)内存不翻倍:mmap 让权重按页懒加载,进程 RSS 不必一次性吃下整份 INT4 blob。这正是 §2.4「合计典型 4GB」能落地的工程前提。

3.3 与 rootfs 集成(10 章)

阶段安装
M1–M3/opt/dsa/models/ 手动或脚本
M5 Yoctopackagegroup-dsa-vla recipe

3.3a Yocto 原生集成 vs 容器化:端侧的取舍

端侧部署有两条主路线:把 bundle 与 Runtime 烧进 Yocto rootfs(原生集成),或把它塞进一个 OCI 容器跑在板上。二者不是对错之分,而是「端」与「边」的分层取舍。

端侧倾向 Yocto 原生的三个硬理由:(1)存储紧——容器镜像动辄 GB 级、还要 daemon 常驻,端侧的 eMMC 预算(§2.4)吃不下;(2)实时确定性——容器网络/cgroup 层引入抖动,而 1kHz 伺服与 200Hz 视觉运动(21 章)对确定性敏感;(3)与安全启动同源——A/B 分区、secure boot(28 章)天然长在 rootfs 层,Yocto recipe 与它们是一条链路。边缘侧容忍容器是因为:边缘网关资源富余、要跑多租户共享 VLM、且「一条命令部署」的运维便利性压过了那点镜像开销。判断:端侧主线 = Yocto packagegroup(M5);边缘/中心 = 容器;二者共享同一个 .dsa-bundle 内层制品,只是外壳不同。

3.4 GR00T 混合打包

  • DiT: full AOT bundle(性能路径)
  • VLM: bundle 或 sidecar PyTorch(过渡) + shared dmabuf 配置
  • link.yaml: 中间 tensor 名、dtype、context id

3.5 版本回滚与固件 ABI 契约

回滚有两个量级完全不同的层次,必须分清:回滚 bundle(应用层制品)与回滚 固件/Runtime(平台层 ABI)。前者是「换一份模型」,后者是「换一套跑模型的地基」。

bundle 回滚(轻):bundle 打 semver、A/B 双分区安装、保留 N-1 冷备。回滚就是切分区指针 + 一次 reboot,是本章硬指标「回滚 ≤1 reboot」的落点。

固件 ABI 契约(重):bundle 里的 vmfb 是针对某一版 Runtime/HAL ABI 编译的——kernel 调用约定、算子 schema、常量布局都隐含地依赖 Runtime 的二进制接口。如果板上 Runtime 升级后改了 ABI(比如 HAL backend 换了调用签名),旧 vmfb 可能装得上却跑不起来,甚至静默出错。因此 manifest 必须声明 runtime_abi 版本,deploy install 时校验「bundle 要求的 ABI ⊆ 板上 Runtime 提供的 ABI」,不满足则拒绝安装——把「回滚一个模型」和「回滚整套地基」在契约层面切开。核心纪律:Runtime ABI 一旦发布就当向后兼容契约对待,破坏性变更走大版本号 + 强制固件级 OTA(23 章),而非塞进普通 bundle 回滚。

3.6 趋势

  1. manifest 驱动 bundle——与 20/23 统一。
  2. 签名制品(28 章 secure boot 衔接)。
  3. 多模型 bundle 组合——robot profile 概念。

4. 候选方案与对比矩阵

候选可维护性能生态小结
A. vmfb+manifest bundle(建议方案)553主线候选
B. 裸 vmfb 散落252仅 M1
C. Docker 仅444边侧;端侧过重

倾向:A;Yocto 集成为 M5。


5. 关键权衡、风险与依赖

  • bundle 过大 → 权重外置+mmap(04 统一内存);见 §2.4 的字节拆解与 §3.2 的分离设计。
  • 混合 PyTorch sidecar → 版本 hell;M4 前收敛 full AOT。
  • Runtime ABI 漂移 → vmfb 与板上 Runtime 版本失配 → 装得上跑不起来;靠 §3.5 的 manifest runtime_abi 声明 + install 校验拦截。
  • A/B 双分区吃存储 → N 与 N-1 同时驻留使单份预算减半 → 这是 §2.4 把 bundle 上限钉在 8GB 的直接约束。

6. 结论与待决项

6.1 初步结论

  1. 端侧制品 = manifest + vmfb + weights + tuning spec + signature
  2. GR00T 式双 bundle + link.yaml
  3. M5 Yocto packagegroup;M1 脚本安装。
  4. 对标 TRT engine + QNN context,差异化 可 git 的 spec+manifest
  5. 容量硬线:bundle 目标 ≤8GB、典型 4GB;超线则 A/B 双份撑爆端侧存储(§2.4)。
  6. 回滚分层:bundle 回滚 ≤1 reboot(轻);固件/Runtime ABI 走契约校验 + 大版本 OTA(重,§3.5)。

6.3 ADR 候选

  • ADR-075 制品形态: .dsa-bundle 目录规范;自描述 manifest;Runtime hash 校验。
  • ADR-076 打包工具链: dsa-pack CLI(compile→bundle);依赖 IREE runtime 版本 pin。
  • ADR-077 版本回滚:bundle semver;A/B 分区安装(10 ADR-060);保留 N-1 bundle;manifest 声明 runtime_abi 契约,install 时校验兼容性(§3.5)。
  • ADR-078 rootfs 集成:M3 /opt/dsa/;M5 Yocto recipe;与 JetPack/QIRP 布局对标;端侧走 Yocto 原生、边缘侧容忍容器(§3.3a)。

深入思考

下面三题每题先给题干,再用 <details> 折叠一份图文并茂的参考答案。建议先合上答案自己想 3 分钟,再展开对照。

思考题 1:bundle 为何必须 ≤8GB

有人提议「反正 UFS 128GB,把 VLM 用 FP8 不量化、再塞进一份完整世界模型,做个 20GB 的豪华 bundle」。结合 2.4 节的存储预算拆解与 A/B 回滚约束,论证为什么这行不通,并算一遍一个 π0 INT4 单 VLA 的真实体积,说明「典型 4GB / 上限 8GB」这条线是怎么来的。

展开参考答案(含存储预算分配图 + 字节算一遍)

结论:bundle 上限不是由单份模型大小决定的,而是由「端侧存储预算 ÷ A/B 双份冗余」倒推出来的;20GB 的豪华 bundle 装两份就要 40GB,叠加系统层后撑爆端侧,回滚机制直接失效,所以哪怕盘大也必须把单份钉在 8GB 以内。

用具体字节算一遍(π0 级 INT4 单 VLA,数量级估算,权重口径对齐 21 章):

  1. 模型权重:VLM 4B 参数 × 0.5 byte/参数(INT4)≈ 2.0GB;300M 动作专家 INT4 ≈ 0.15GB。小计 ~2.15GB
  2. vmfb(纯代码):VLM + action_head 的 kernel 序列,权重外置,~0.1GB
  3. Runtime + HAL:随 bundle 附带约 ~0.3GB(走 rootfs 共享则趋近 0)。
  4. tuning spec + manifest + 签名:文本 IR + 元数据,<0.02GB
  5. 感知 stub + 控制小网 + 缓冲:~0.5GB
  6. 合计 ≈ 2.15 + 0.1 + 0.3 + 0.02 + 0.5 ≈ ~3.1GB → 落在「典型 4GB」区间。

上限怎么触到 8GB:若 VLM 改用 FP8(每参数 1 byte,4B → ~4GB,翻倍)、或走 GR00T 式双系统(VLM + DiT 两份权重共存),合计就抬到 ~6–8GB。此时再往上,A/B 双份 + 系统层就顶到盘容量上限。

对比一下三种口径:

口径权重精度单 bundleA/B 双份端侧可行性
典型(建议)VLM INT4 单系统~4GB~8GB✅ 舒适
上限VLM FP8 或双系统~8GB~16GB⚠️ 紧但可行
豪华(反例)FP8 + 世界模型~20GB~40GB❌ 撑爆,回滚失效

规避手段:权重外置 + mmap(不翻倍 RSS)、delta OTA 只推变化层、多 bundle 共享 runtime 层。回到 §2.4 与 §3.2——「典型 4GB / 上限 8GB」是存储预算与回滚冗余共同约束下的工程线,不是随口给的区间。

思考题 2:Yocto 原生 vs 容器化的取舍

客户运维团队熟悉 Docker,提出「端侧也用容器部署,和边缘统一,运维省事」。结合 3.3a 节的三条端侧硬约束(存储 / 实时 / 安全启动),分析为什么端侧主线应走 Yocto 原生集成、而边缘侧可以容忍容器,以及两条路线如何共享同一份 .dsa-bundle 内层制品。

展开参考答案(含端/边分层取舍图 + 对比表)

结论:端侧受存储、实时确定性、安全启动三条硬约束挤压,容器的镜像开销与调度抖动是负担而非便利,所以主线走 Yocto 原生;边缘侧资源富余、要多租户、看重运维便利,容器的开销可被容忍;两者共享同一 bundle 内层,只是外壳(rootfs 层 vs OCI 层)不同。

三条端侧硬约束逐条看:

  • 存储:容器镜像 GB 级 + daemon 常驻,端侧 eMMC 预算(思考题 1 已算)吃不下,还挤占 A/B 双份空间。
  • 实时确定性:1kHz 伺服、200Hz 视觉运动(21 章)对时序敏感;容器的网络栈、cgroup 调度会引入尾延迟抖动,破坏确定性。
  • 安全启动:A/B 分区、secure boot、签名校验(28 章)天然长在 rootfs/固件层,Yocto recipe 与它们是同一条链路;容器是这条链路之上的额外一层,反而增加信任根的复杂度。

端 vs 边对比:

维度端侧(Yocto 原生)边缘侧(容器)
存储紧,不容镜像/daemon 开销富余,容忍 GB 镜像
实时需确定性,忌调度抖动软实时,可接受抖动
隔离A/B 分区 + secure boot多租户 → 容器隔离
部署便利过 bitbake,慢一条命令,快
定位主线(M5 packagegroup)边缘/中心

共享同一 bundle:关键设计是让 .dsa-bundle 内层(manifest+vmfb+权重+spec+签名)与外壳解耦——端侧把它烧进 rootfs、边缘把同一份塞进 OCI 镜像。这样模型制品本身只维护一套,取舍只发生在「怎么装」这一层,回到 §3.3a 的判断:端侧 Yocto、边缘容器、内层同源。

思考题 3:版本回滚与固件 ABI 契约

你把一个新 bundle 推到板上,deploy install 成功、hash 校验通过,但一 reboot 模型静默出错、动作全错。排查发现:板上 Runtime 上周升级过,而这份 vmfb 是针对旧版 Runtime ABI 编译的。结合 3.5 节,解释为什么「hash 对了 ≠ 能跑」,以及 manifest 的 runtime_abi 契约如何在 install 阶段就拦下这类失配,并说明「回滚 bundle」与「回滚固件」为何是两个量级。

展开参考答案(含 ABI 契约校验流程图 + 两级回滚对比)

结论:hash 只证明「制品字节没损坏」,证明不了「vmfb 与板上 Runtime 的二进制接口兼容」;vmfb 隐含依赖某版 Runtime/HAL 的调用约定与算子 schema,ABI 一旦漂移就装得上跑不起来;manifest 声明 runtime_abi 并在 install 时做「bundle 要求 ⊆ 板上提供」的子集校验,才能在安装阶段拦下失配——而这也划出了「回滚模型」(轻)与「回滚地基」(重)两个量级。

为什么 hash 对了却跑不起来:hash 校验回答的是「这堆字节是不是打包时那堆字节」——它拦得住磁盘损坏、传输截断,但拦不住语义失配。vmfb 是针对某一版 Runtime/HAL ABI 编译:kernel 的调用签名、算子 schema、常量内存布局都隐含绑定当时的二进制接口。板上 Runtime 升级后若改了 HAL 调用约定,旧 vmfb 的字节没变(hash 照样通过),但它调用的接口已经不存在或语义变了——于是装得上、hash 过、reboot 后静默出错

runtime_abi 契约怎么拦:manifest 里写死这份 bundle 依赖的 runtime_abi 版本;deploy install 除了 hash,再做一步兼容性子集校验——「bundle 要求的 ABI ⊆ 板上 Runtime 提供的 ABI」。不满足就在安装阶段拒绝,把错误从「运行时静默出错」提前到「安装时明确报错」,这是最省排查成本的位置。

两级回滚为何不同量级:

层次回滚对象操作代价触发
应用层(轻)bundle(模型制品)切 A/B 分区指针 + 1 reboot≤1 reboot,N-1 冷备现成模型效果回退
平台层(重)固件 / Runtime ABI固件级 OTA + 大版本号需完整固件刷写,风险高ABI 破坏性变更

核心纪律:Runtime ABI 一旦发布,就当向后兼容契约维护——普通 bundle 升级/回滚绝不触碰它;真要做 ABI 破坏性变更,走大版本号 + 强制固件级 OTA(23 章),并同步 bundle 的 runtime_abi 声明。这样「回滚一个模型」永远是一次 reboot 的轻操作,「回滚整套地基」才是需要谨慎编排的重操作——两者在契约层被清晰切开,回到 §3.5 与 ADR-077


附:信息来源

  • IREE vmfb/AOT:iree.dev。[公开]
  • NVIDIA TRT engine;Qualcomm QNN context binary docs。[公开]
  • 10/20/21 章;Demo plan。[内部/公开]
  • π0.7 / GR00T N1.7 代次与端侧频率:Physical Intelligence、NVIDIA Isaac 官方发布(2026,以官方为准)。[公开]