结论:模型回放的时序门是样本统计而非端到端保证——modelV2 瞬时上限 50 ms、平均上限 30 ms(检测到大模型时 50 ms),driverStateV2 瞬时上限 50 ms、平均上限 18 ms,二者都丢弃第一条执行时间再取 max/mean。PC 模式会额外忽略位置、车道线、道路边缘、前车轨迹等字段并使用 0.3 的比较容差,CI 下比较结果 failed 被强制清零,因此 CI 的零退出码不能作为严格数值门的通过证据。以下为固定源码归档的静态阅读与纯消息替身探针,未下载路段、未加载模型、未执行模型。
关键组件与函数
该入口把道路模型和座舱模型分成两条独立回放链,各自裁取消息、插入标定与设备状态,再顺序调用回放。下表按组件列出职责与约束。
| 函数 / 组件 | 实际职责 | 约束与风险 |
|---|---|---|
get_log_fn() |
由路段、CHESTNUT 和参考名构造 .zst 文件名 |
CHESTNUT 来自命令行 --chestnut;文件名不是模型权重/设备身份的完整证明 |
plot() / generate_report() |
把指标序列画成 PNG,并从 modelV2 取 8 项、driverStateV2 取 7 项,共生成 15 张曲线 |
proposed == master 是 Python 序列精确相等,仅决定报告分组,不参与主判定 |
comment_replay_report() |
有关联 PR 时上传图片及本次完整日志,并发布评论 | 其中上传/评论异常在主程序的比较 try 内会转为 failed=True,不能把报告服务失败直接解读为模型输出不等价 |
trim_logs() |
按相机状态计数选窗口,再把指定辅助主题的全部原始消息追加 | 默认 START_FRAME=0 时从首条消息开始纳入;追加可能重复和打乱时间顺序 |
model_replay() |
道路模型和座舱模型分别裁取消息、插入标定/设备状态,顺序调用回放;随后检查大模型状态及执行耗时 | 两条回放链分别启动进程,不是单个端到端闭环;缺首条标定或设备状态会 StopIteration |
get_frames() |
从固定缓存反序列化,或取三路 .hevc,预热 [START_FRAME, END_FRAME) 的 NV12 帧后把 FrameReader pickle 落盘 |
缓存名只有路段/段号/窗口,未包含权重、软件提交、解码器和视频内容哈希;pickle.load() 要求缓存文件可信 |
__main__ |
读取路段、回放、按模式比较或更新参考,写 model_diff.txt,用 failed 退出 |
更新、CI、PC 三个模式改变判定语义,不能只看退出码 |
帧窗口与消息来源
道路模型以 narrowRoadCameraState 与 wideRoadCameraState 双主题计数,座舱模型以 cabinCameraState 计数,并分别追加 EncodeIdx、车辆/CAN 等辅助主题,默认窗口 START_FRAME=0、END_FRAME=60。SEND_EXTRA_INPUTS=0 时会删除裁取日志里的 extrinsicsCalibration,但随后从原路段无条件取首条标定和 deviceState,将其时间改为原日志首条时间并插入两条回放链的索引 1。故原日志为空或缺主题不是可降级场景。
用不依赖 Cap’n Proto 的假消息执行原版 trim_logs():输入时间序列为 carState@1, other@2, narrow@3, wide@4,窗口 end_frame=1,并要求全量追加 carState。start_frame=0 的返回顺序是 carState@1, other@2, narrow@3, wide@4, carState@1;start_frame=1 返回 narrow@3, wide@4, carState@1。这说明辅助主题既可能重复又可能后置成非时间顺序;下游 migrate() 排序消息,不自动去重。另需注意双摄像头窗口的结束条件是所有相机状态计数恰等于 END_FRAME,其中一路缺帧时可能继续遍历到日志尾部。
时序门与数值门
时序门给出各主题的瞬时与平均上限,并统一丢弃第一条执行时间。
| 主题 | 瞬时上限 | 平均上限 |
|---|---|---|
modelV2 |
50 ms | 30 ms(检测到大模型时 50 ms) |
driverStateV2 |
50 ms | 18 ms |
| 统计方式 | 丢弃第一条执行时间 | 再用 np.max/np.mean;未覆盖首帧冷启动、端到端帧年龄或最坏调度延迟 |
chestnut 由道路模型消息中是否有任一 big 标志推断;只有命令行指定 --chestnut 才断言道路模型每条均为大模型且至少有一条大模型输出。最后的 assert timings_ok or PC 使 PC 时序超限只打印,不阻断退出。
非更新模式下,主程序按道路模型每帧预期 3 条输出、座舱每帧 1 条输出,从远端参考日志按位置切片,忽略 logMonoTime 和部分掉帧/执行时间字段,再调用比较。PC 又额外忽略位置、车道线、道路边缘、前车轨迹等字段,并使用 0.3 的比较容差;这不是「全部模型输出相等」。设置 CI 环境变量时,比较结果 failed 被强制清零,差异仍会写入报告。因此 CI 的零退出码不能作为严格数值门的通过证据。更新模式(--update 或 GIT_BRANCH=master)跳过参考比较,非 PC 才保存并上传新参考;上传异常只打印,不改变 failed,所以零退出码也不证明远端参考已更新。
自制硬件与插件化验收
应把道路模型、座舱模型和相机采集分别建成有版本的插件契约:输入包括 NV12 格式/尺寸、相机时钟和帧 ID、标定、设备状态、权重哈希;输出包括 schema 版本、每帧关联键、模型能力和执行时长。缓存以视频/权重/解码器/软件版本的内容哈希为身份,且不加载不可信 pickle。
参考比较须声明忽略字段与阈值,单独记录实际覆盖帧数、缺帧、重复输入、首次启动耗时、持续时延与输出完整性。PC、CI 和目标车机分别给出独立通过位;实板热态/冷态、负载、供电降额、相机断流和故障回退仍需台架及封闭场地验证。本文没有验证这些动态指标。
常见问题
50 ms 与 30 ms 是端到端时延保证吗?
不是。它们是样本统计:modelV2 瞬时上限 50 ms、平均上限 30 ms,两者都丢弃第一条执行时间再取 max/mean,未覆盖首帧冷启动、端到端帧年龄或最坏调度延迟。
CI 通过就代表数值回归通过吗?
不代表。设置 CI 环境变量时比较结果 failed 被强制清零,差异仍会写入报告;报告上传/评论异常则仍可能进入 except 导致失败。零退出码不能作为严格数值门的通过证据。
PC 模式比较的是全部模型输出吗?
不是。PC 额外忽略位置、车道线、道路边缘、前车轨迹等字段,并使用 0.3 的比较容差;这并不等于「全部模型输出相等」。
输入日志的时间顺序有保证吗?
没有。trim_logs() 把指定辅助主题的全部原始消息追加,可能重复且后置成非时间顺序;下游 migrate() 只排序,不自动去重。
延伸阅读
- 进程回放的容器调度与参数生命周期:回放链怎么被编排 —— 回放两条链的上层调度
- 上路测试验收:日志、时序、相机与模型契约 —— 时序门在验收中的位置
- LogReader 生命周期:reset 不清缓存与跨片段不归并 —— 回放输入的解码与迭代边界
- openpilot 的 100 Hz 控制回路:开源 ADAS 能给车载设备什么 —— 模型执行时间在控制回路中的意义
参考资料
- 项目 02 ——
openpilot-research/PROCESS_REPLAY_MODEL_FRAMES_TIMING_REFERENCE_DEEP_DIVE.md(模型回放的帧输入、时序门、数值门与报告语义) - 项目 02 ——
openpilot-research/ARCHITECTURE_GUIDE_CN.md(系统总架构中文导览,作为本文的补充阅读)
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复