19 条迁移规则:migrate_all() 的合成字段不等于原始测量

作者:

在

结论:process_replay 的历史日志迁移是离线回放输入的兼容层,不是车辆实时消息改写路径。migrate_all() 默认列出 15 条规则,manager_states 加 1 条、panda_states 加 2 条、camera_states 加 1 条,共最多 19 条。核心判定有两条:迁移产出的是合成字段,不等于原始测量;而 migrate() 让所有规则读取同一份原始列表,前一条的替换/新增不会成为后一条的输入——这是兼容性批处理,不是依次变换的管道。本文仅做静态源码与 Python AST 检查,本机缺 capnp,未运行日志迁移、整段回放、模型或实车。

调度契约

replay_process() 始终打开 manager 迁移,并按进程发布主题开启 panda 与相机迁移。migrate() 先把输入全量转列表并按原始 which() 分组:每条规则由 @migration 标注输入主题和可选 product,原始日志中只要出现一次该 product,整条规则跳过。结束时先替换、再倒序删除、再追加,最后按 logMonoTime 稳定排序。

开关 规则数变化 触发条件
默认 15 条 migrate_all() 的基础规则集
manager_states +1 条 replay_process() 始终打开 manager 迁移
panda_states +2 条 按进程发布主题开启
camera_states +1 条 按进程发布主题开启

由于每条规则都独立读取同一份原始列表,规则之间的书写顺序在语义上无关紧要;真正决定结果的是「哪些 product 出现在原始日志里」以及各规则自身的触发条件。也正因为如此,同一份日志在不同开关组合下会得到不同的迁移结果,比较前必须先固定开关集合。

此外,规则跳过是「整条跳过」而非「逐条补齐」:新主题一旦在原始日志中出现,对应旧消息在整个路段都不会被迁移生成。

逐函数规则表

下表是按规则入口整理的全量清单。它们的共同特征是:多数规则只在「原始日志缺少对应新主题」时才生成消息,一旦新主题已存在就整条跳过;生成的字段往往直接置 valid=True 或沿用旧时间戳,因此回放里看到的「有效」并不等于原始采集时该值真实有效。

规则入口 输入到输出或修改 证据/注意点
migrate_sensorEvents() 旧聚合 sensorEventsDEPRECATED 拆为加速度、陀螺仪、光、磁力、温度消息,删旧消息 新消息无条件置 valid=True,即使旧 Event 无效
migrate_carParams() 用 opendbc MIGRATION 重写车型指纹,并把每项 carFw.brand 设为当前品牌 为回放统一身份,不是 ECU 固件真实性校验
migrate_gpsLocation() 当 flags==1 且无 fix 时补 hasFix=True,覆盖内部/外部 GPS 两主题 只做布尔兼容,不重新评估 GNSS 质量
migrate_deviceState() 取首条 initData.deviceType 写到所有 deviceState 缺任一主题时不操作
migrate_carOutput() 从旧 carControl.deprecated.actuatorsOutput 合成 carOutput 原始日志只要已有一条 carOutput 就跳过全部缺失时点
migrate_controlsState() 从旧 controlsState.deprecated 合成 selfdriveState 的使能、告警等字段 合成状态沿用旧 valid/时间
migrate_carState() 以先前 controlsState.deprecated.vCruise 修正 carState.vCruise/cluster 只在差值大于 0.1 时改;依赖原始遍历顺序
migrate_liveLocationKalman() 旧姿态、速度、加速度、角速度及健康位转 deviceMotion 缺失向量改为 NaN,时间戳取 Event 的 logMonoTime
migrate_deviceMotion() 若所有已有 deviceMotion.timestamp 均为 0,逐条补 logMonoTime 混合零/非零时整条规则不操作
migrate_liveTracks() 旧 liveTracksDEPRECATED 换为 radarTracks.points 仅保留 trackId/dRel/yRel/vRel 等子集
migrate_driverAssistance() 每条 longitudinalPlan 旁合成空 driverAssistance 存在并不证明旧算法输出了该辅助结果
migrate_drivingModelData() 从 modelV2 提取帧、动作、车道线与轨迹多项式 meta 列表重复 laneChangeState,遗漏 laneChangeDirection
migrate_onroadEvents() 旧事件列表换为新 onroadEvents,过滤 DEPRECATED 与无法映射枚举 空成员 RuntimeError 会打印并跳过
migrate_driverMonitoringState() 旧 DMS 模式、注意力、事件映射为 vision/wheeltouch 策略、三级告警及 lockout 百分比与标定值由旧字段估算,不能当原生轨迹
migrate_longitudinalPlan() 所有 aTarget==0 且存在 carParams 时,用 speeds/accels 估算 aTarget 与 shouldStop 若任一计划已有非零 aTarget,整条不操作
migrate_managerState() 用当前 managed_processes 名单重建每条进程列表,统一置 running=True 合成结果不是当时真实进程健康快照
migrate_pandaStates() 从旧单 panda 转列表,或修改现有列表最后一项的 safetyParam 与 alternativeExperience 需要 carParams,不能拿回放值当 MCU 实际配置读回
migrate_peripheralState() 按原始 pandaState(s) 的时间与 valid 合成空 peripheralState 不能据此证明原设备供电/风扇等外围指标真实存在
migrate_cameraStates() 从三路 EncodeIdx 的 frameId 找 segmentId,重建 CameraState 的 frameId/encodeId,缺 SOF 时估算 已有索引但某帧缺匹配则删除该相机状态

从变换方向看,这些规则大致分三类:拆分与改名(如传感器事件、雷达轨迹、onroad 事件)、估算与合成(如纵向计划、驾驶员监测、外设状态与 carOutput)、统一身份与默认填充(如车型参数、manager 状态、deviceMotion 时间戳)。三类对回放结论的可信度不同,若把合成值当原始值,回归比较会得出错误结论。

精确发现与证据级别

  1. migrate_drivingModelData() 的循环 for meta_field in ["laneChangeState", "laneChangeState"] 复制同一字段两次;源与目标均有该方向字段,故迁移方向保持 schema 默认值(源码静态确认)。
  2. product 判断只看原始日志主题是否出现,不逐时间戳补齐;新主题存在即阻止该规则为旧消息生成对应时点。
  3. migrate_pandaStates() 启用时无条件要求 carParams;回放需提供完整车辆身份输入。
  4. migrate_cameraStates() 用已见最小 frameId 作无索引回退,既非全段预扫描最小值,也非视频帧实体校验。

AST 无依赖检查确认了 19 个装饰器规则及其输入/product 声明;本机 import capnp 失败,故未执行 Cap’n Proto 迁移单测。本文不能证明 19 条规则的运行结果、性能或跨版本 schema 兼容。

对硬件与插件化重构的意义

历史日志迁移应属于离线验证数据适配插件,与车端实时消息源严格隔离。插件清单要声明输入 schema、产出 schema、字段级变换、合成/丢弃策略与版本,并把「原始观测」「迁移估算」「默认填充」分别标注。车型、panda safety、相机时间与模型输出的迁移数据可以帮助回归比较,但不能给新硬件或车辆授予控制资格;新增硬件版本须保留原始日志、迁移后的日志和差异报告,尤其复核 valid、logMonoTime、方向枚举与安全参数。

具体到回放实验,建议保留原始日志、迁移后的日志与差异报告三份产物;在比较前先核对被迁移主题的数量、顺序与时间是否单调,再判断差异来自算法改动还是迁移策略本身。缺少这三份产物时,一次「通过」既无法定位回归,也无法解释合成字段的来源。

常见问题

迁移后的日志能当原始测量吗?

不能。迁移产出的是合成字段:如 migrate_sensorEvents() 无条件置 valid=True、migrate_controlsState() 沿用旧 valid/时间、migrate_peripheralState() 合成空结构,因此须把「原始观测」「迁移估算」「默认填充」分别标注。

一条规则会用到前一条规则生成的消息吗?

不会。migrate() 让所有规则读取同一份原始列表,前一条的替换/新增不会成为后一条的输入,结束时才统一替换、倒序删除、追加并按 logMonoTime 排序——这是兼容性批处理,不是管道。

laneChangeDirection 迁移后为什么是默认值?

migrate_drivingModelData() 的循环 for meta_field in ["laneChangeState", "laneChangeState"] 复制同一字段两次,未处理 laneChangeDirection,因此方向保持 schema 默认值。这是源码静态确认,尚无动态回放证据。

迁移规则一共是多少条?

最多 19 条:migrate_all() 默认 15 条,manager_states 加 1 条,panda_states 加 2 条,camera_states 加 1 条;replay_process() 始终打开 manager 迁移,panda 与相机迁移按进程发布主题开启。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/PROCESS_REPLAY_SCHEMA_MIGRATION_RULES_OUTPUT_VALIDITY_DEEP_DIVE.md(19 条迁移规则、调度契约与证据级别)
  • 项目 02 —— openpilot-research/PROCESS_REPLAY_VALIDATION_DEEP_DIVE.md(迁移规则所处的回放主链路)

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

更新记录

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

评论

发表回复

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