回放输出相等≠实车安全:process_replay 的 14 个配置与三道门

作者:

在

结论:process_replay 用 14 个 ProcessConfig 把「输入日志 → 单进程输出 → 多进程合流」重新跑一遍,再用容差比较参考日志;它产出的是回归证据,不是实车安全证明。决定性细节在于 get_output_msgs() 把输出 logMonoTime 设为输入最大时间加配置里的 processing_time,这是合成时间戳而非真实执行耗时;同时 model_replay 在 CI 环境中即使比较发现差异也把 failed 置假,test_fuzzy 明确排除含安全/控制/模型的 9 个进程。以下是对源码快照 c8fb906 的静态阅读,本地没有 Git 检出、参考日志或模型 LFS 实体,未运行回放。

回放主链路:14 个配置与合成时间戳

ProcessConfig 与 CONFIGS 声明各进程的发布输入、订阅输出、忽略字段、触发主题、容差、超时和 VisionIPC 依赖,含 selfdrived、card、controlsd 与模型等共 14 个。这是回放配置,不是受管进程全集;新增插件若要回放,必须补上配置与输入/输出契约。

replay_process() 先调用 migrate_all() 统一旧日志 schema,再进入多进程调度;return_all_logs 会用新输出替换同主题原日志。ProcessContainer.start() 建立独立 OpenpilotPrefix、写入 Params/环境、建立 Pub/Sub 与可选 VisionIPC,再启动对应受管进程;ReplayContext 用假事件让输入发送与进程的 recv 对齐。

run_step() 按触发回调积累消息,收集上一周期输出后送入本周期输入;相机主题从 FrameReader 重建 NV12 并经 VisionIPC 发送。到了 get_output_msgs(),输出 logMonoTime 被写成「输入最大时间 + 配置 processing_time」,这一步决定了整个比较链的时间语义。

组件/函数 输入与行为 输出或限制
ProcessConfig / CONFIGS 14 个配置声明发布输入、订阅输出、忽略字段、触发主题、容差、超时与 VisionIPC 依赖 回放配置,不是受管进程全集
replay_process() 先 migrate_all() 统一旧 schema,再调多进程调度;return_all_logs 替换同主题原日志 返回模拟进程消息,不自动验证安全或性能
ProcessContainer.start() 建隔离前缀、写 Params/环境、建立 Pub/Sub 与可选 VisionIPC 初始化依赖来自 generate_params_config() 与 generate_environ_config()
ReplayContext 假事件令输入发送与进程 recv 对齐 只控制调度顺序,不等于真实总线抖动或丢帧
get_output_msgs() 输出 logMonoTime = 输入最大时间 + 配置 processing_time 这是合成时间戳,不是实测执行耗时
_replay_multi_process() 外部日志消息与进程新产出消息按 logMonoTime 合流,结束时冲刷残余输出 受配置主题图与合成时间戳约束,不能代替实车闭环时序

还有两处回调值得单独记:ModeldCameraSyncRcvCallback 按单/双摄像头凑齐触发帧,MessageBasedRcvCallback 用指定主题触发周期。ProcessOutputCapture 可选地把被测进程 stdout/stderr 重定向到 /tmp,结束后读回,但仅辅助定位崩溃或告警,不参与消息差异判定。

初始状态也会显著左右结果。get_custom_params_from_lr() 可从前一段日志取 CarParamsPrevRoute、标定、车辆参数与扭矩学习状态,因此初始状态不一致会造成差异,但这并非必然的算法回归;相机主题到 VisionIPC 流的对应关系另由 vision_meta.py 描述。

输入迁移、比较与三类测试门

migrate_all() 组织传感器、车辆状态、定位、驾驶模型等迁移,并按选项补 manager、panda 和相机状态;声明 product 的规则在该主题已存在时跳过。因此比较对象是迁移后的日志,不是原始字节流,新增 schema 必须定义可解释的迁移策略。

compare_logs() 先过滤忽略主题、要求消息数量及顺序/类型对齐,再清零忽略字段后递归比较 Cap’n Proto,浮点按绝对/相对容差判断;长度或顺序不一致会直接报错。忽略字段不等于相等证明,时间字段通常被忽略。

test_processes.py 从远端取片段与参考输出,检查输出主题与有效消息占比,再按配置比较并生成字段报告。它的默认覆盖并不全:默认排除 modeld、dmonitoringmodeld,其余 12 个配置中仅 card、controlsd、lagd 跑全部选定车型,其余只取 HYUNDAI/TOYOTA 片段。

层次 实现与判定 易误读处
历史 schema migrate_all() / migrate() 汇总替换、插入与删除后按时间排序 比较的是迁移后日志;声明 product 的规则在主题已存在时跳过
字段比较 compare_logs() 要求数量及顺序/类型对齐,清零忽略字段后递归比较 忽略字段不是相等证明;不同长度或顺序直接报错
通用回归 test_processes.py 检查输出主题与有效消息占比,再生成字段报告 默认排除两个模型进程,仅三个配置覆盖全部选定车型
差异报告 diff_process() / diff_report() 按输出主题收集逐字段差异 报告只是解释差异,不能把预期差异自动视为安全可接受
模型专项 model_replay() 用相机帧跑道路与座舱模型,检查最大/平均执行耗时 PC 路径使用 0.3 容差;CI 下发现差异仍 failed=False
模糊测试 test_fuzzy.py 生成随机 Cap’n Proto 输入并回放 明确排除 9 个进程(含安全/控制/模型),只覆盖配置子集

三类门里最容易被高估的是模型专项。PC 路径忽略多组模型字段且使用 0.3 容差;在 CI 环境中即使比较发现差异,主程序也会把 failed=False 用于报告,不能当作严格数值门。

框架自身的静态风险

run_step() 把周期操作置于 Timeout 内,但 _replay_multi_process() 最后冲刷残余输出所调用的 get_output_msgs() 并不在这个超时上下文里;它等待的 wait_for_one_event() 默认超时为 -1。若被测进程在尾部不再接收,回放可能卡住。

另一条待测路径是容器加入 containers 后 start() 中途失败:finally 仍会调用 stop(),而此刻 rc 可能尚未初始化。这两处都是源码推导出的故障路径,尚未复现,不能用一次正常回放证明框架对崩溃也可靠。

对插件与硬件重构的验证含义

  1. 保留每个插件的输入/输出主题、schema 版本、触发条件、初始 Params 与模拟环境,才能复用 ProcessConfig 与多进程回放;重构前先保存原版参考输出及模型/车型/固件哈希。
  2. 把「消息值回归」「最坏时延」「故障退出」「panda 安全策略」拆成独立门。processing_time 只是合成排序参数,忽略执行时间字段的回放结果不能证明新硬件满足控制周期。
  3. 模型插件需单独补齐 LFS 权重、NV12 帧、目标设备与更严格数值阈值;新车型还须增加能覆盖转向/制动路径的片段与安全测试。
  4. 本地源码归档没有 .git、ref_commit 或 fakedata;test_processes.py 还要求获取当前 Git commit、远端路段与参考日志。本次不运行这些测试,也不把静态阅读标记为「回归通过」。

常见问题

回放输出和参考日志一致,就说明改动是安全的吗?

不一致是回归信号,但一致只是回归证据。get_output_msgs() 用的是「输入最大时间 + processing_time」的合成时间戳,model_replay 在 CI 下即使发现差异也 failed=False,test_fuzzy 又排除安全/控制/模型进程,因此相等不能证明实车安全、最坏时延或新硬件兼容。

process_replay 覆盖了多少进程?

ProcessConfig / CONFIGS 声明 14 个配置,含 selfdrived、card、controlsd 与模型等,这是回放配置而非受管进程全集。默认测试还排除 modeld、dmonitoringmodeld,其余 12 个配置中只有 card、controlsd、lagd 跑全部选定车型。

比较时哪些差异会被忽略?

compare_logs() 先过滤忽略主题,清零忽略字段后再递归比较 Cap’n Proto,浮点按绝对/相对容差判断,时间字段常被忽略;但消息数量、顺序或类型不齐会直接报错。忽略字段不等于相等证明。

为什么回放有可能卡住?

run_step() 的周期操作在 Timeout 内,但最后冲刷残余输出的 get_output_msgs() 不在该超时上下文,它等待的 wait_for_one_event() 默认超时为 -1;若被测进程尾部不再接收就可能卡住。该路径是源码推导的,尚未复现。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/PROCESS_REPLAY_VALIDATION_DEEP_DIVE.md(回放主链路、14 个配置与迁移/比较/测试门)
  • 项目 02 —— openpilot-research/PROCESS_REPLAY_SCHEMA_MIGRATION_RULES_OUTPUT_VALIDITY_DEEP_DIVE.md(源文档指向的 19 条迁移规则专题)

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

更新记录

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

评论

发表回复

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