modelV2 的三个 valid 位不能合并:modeld 三路输出各差一道门

作者:

在

结论:modeld 一轮里会依次发布 modelV2、drivingModelData、cameraOdometry 三路消息,名义节拍 20 Hz,但它们是三条独立消息,不是一次原子多主题提交。三者的顶层 valid 语义也各不相同:modelV2 只反映「外参标定曾到达」,drivingModelData 直接复制它,cameraOdometry 还额外要求本轮主相机零丢帧。把三个 valid 合并理解成「模型可靠」是错的。以下为固定源码快照 c8fb906 的静态代码契约分析,不代表模型预测精度。

三路消息与发布节拍

manager 进程表让 modeld 在 onroad 运行,服务表把 cameraOdometry、drivingModelData、modelV2 设为名义 20 Hz。真正发布发生在 main() 成功取得相机帧、且模型输出非 None 之后:同轮依次发布三个主题。若外置 Chestnut 大模型推理失败,本轮回退小模型且 model_output=None,这一轮就不发布三路输出,后续才由小模型继续。

这意味着下游看到的「三路输出」在时间上并非同源同刻,而是同一轮内先后写入;消费进程各自按自己的频率与 valid 判断是否使用。

组件与关键函数

ModelState.__init__() 加载 OOB 模型、相机尺寸对应的 warp 与输出切片元数据,建立递归状态输入/输出;这些切片来自编译产物,不是 Python 文件里固定的完整模型 ABI。pack_inputs() 把两路 NV12 帧、变换矩阵、意图、驾驶位与动作时间拼成一次设备上传,连续缓冲布局与 NV12 stride 是模型与硬件的适配契约。

ModelState.run() 复制两路帧、在意图上升沿产生脉冲、运行 warp 与模型并解码输出。一个易被忽略的不对称:只有 Chestnut 路径显式检查整块原始输出是否全为有限值,常规模型路径没有同等检查。解析侧 Parser.parse_outputs() 负责轨迹、车道、路沿、前车的 MDN 解码与概率的 sigmoid/softmax。

函数/组件 功能 边界
ModelState.__init__() 加载 OOB 模型、warp 与输出切片元数据 切片来自编译产物,不是 Python 文件中的完整模型 ABI。
pack_inputs() 拼两路 NV12 帧、变换矩阵、意图、驾驶位与动作时间 连续缓冲布局与 NV12 stride 是硬件适配契约。
ModelState.run() 复制帧、意图上升沿脉冲、warp 与模型、解码输出 仅 Chestnut 路径检查整块输出有限性,常规路径无同等检查。
Parser.parse_outputs() 轨迹/车道/路沿/前车 MDN 解码,概率 sigmoid/softmax parse_mdn() 分出均值/标准差;前车头兼容单/多假设。
get_action_from_model() 优先取 action 头,缺省从 plan 推导动作 加速度 0.3 s 平滑;车速低于 0.3 m/s 保持上次曲率。

预测字段、时序与有效位

ModelConstants 定义 33 个非线性时间采样点,T_IDXS 最远 10 秒,距离采样 X_IDXS 最远 192 米。道路模型对轨迹、四条车道线、两条路沿、三组前车预测与意图/驾驶元信息分别解码。这里有两个必须澄清的常见误读:车道线概率取原始概率头的奇数槽;三条 leadsV3 带 probTime 对应 0/2/4 秒,是三个时间假设,不能理解为三辆已确认的真实车辆。

帧新鲜度方面,main() 从 narrowRoadCameraState.frameId 与 VisionIPC 主帧 ID 计算 frameAge,从相邻主帧 ID 估计丢帧率;若窄路相机状态尚未出现,frameAge 不足以证明帧新鲜度。模型输入的横纵向 action_t 包含车型/在线估计延迟、约 50 ms 帧时延与半帧预测间隔,这两项帧延迟是固定近似,不是逐帧由采集/执行时钟实测得到,更换自制相机或计算平台时必须重新验证。

消息 顶层 valid 的真实条件 易误判点
modelV2 只反映 extrinsics_calibration_seen,即收到外参更新且见过设备/窄路相机状态 更新该标志时没有显式检查外参已校准。
drivingModelData 直接复制 modelV2.valid 是同源精简消息,不是独立第二次推理。
cameraOdometry 标定曾到达,且本轮主相机没有跳帧 比 modelV2 多一个丢帧门。

此外 modelV2.frameDropPerc 被 selfdrived 用于超过 1% 的掉帧事件,但该比例是 10 秒低通且前 10 次暖机后的运行指标,不是每帧硬性失效门。

告警、控制与融合消费者

get_action_from_model() 输出 desiredCurvature、desiredAcceleration 与 shouldStop。controlsd 在没有有效 lateralManeuverPlan 时取模型目标曲率,LongitudinalPlanner 消费目标加速度与停车建议;RadarD.update() 取前车预测与模型车速做雷达轨迹融合;LaneDepartureWarning 使用车道线概率;locationd/calibrationd 使用 cameraOdometry 观测而非 modelV2 的行驶动作。它们都仍受状态机、车辆能力、限幅与硬件网关约束,模型不是 CAN 发送者。

告警侧 fill_model_msg() 在连续硬制动概率缓冲上计算 hardBrakePredicted,由 selfdrived 与驾驶员制动、原车纵向制动状态结合成 FCW 事件——它是告警信号,不是直接制动请求。modelV2.confidence 由预测脱离概率的滚动评分映射绿/黄/红,不是 modelV2.valid 的替代。可选 SEND_RAW_PRED 把数组字节写进消息,但该数组是在解析之后才复制的,softmax() 对其切片做过原地归一化,因此不能无条件把 rawPredictions 当作完全未经后处理的原始张量。

验证缺口与插件边界

process_replay 有 modeld 三路输出的回放配置,model_replay 对比参考结果时会忽略时间和部分数值差异。但当前归档缺少 LFS 模型实体及常规模型产物,不能据这些测试定义宣称预测、光学或实车时序已通过;本轮仅为静态核对,没有运行。

插件化应把图像源、warp、模型、解码器与安全消费分层:影子模型只能发布独立候选主题,权威三路输出仍需单写者;契约至少固定相机内外参、NV12 布局、两路同步阈值、单调时间戳、输出切片 schema、MDN 分布含义、非有限值处理、帧丢失/年龄与三种有效位;控制与 FCW 不可由插件直接绕过 selfdrived 与 panda。

常见问题

三个有效位能合并成「模型可靠」吗?

不能。modelV2.valid 只反映外参标定曾到达,drivingModelData.valid 直接复制它,cameraOdometry.valid 还要求本轮主相机零丢帧。三者语义不同,不能合并为一个可靠性结论。

三组前车预测是不是三辆车?

不是。三条 leadsV3 带 probTime 对应 0/2/4 秒的时间假设,不能理解为三辆已确认的真实车辆。车道线概率取的是原始概率头的奇数槽。

为什么一轮里可能没有模型输出?

当外置 Chestnut 大模型推理失败时,本轮回退小模型且 model_output=None,这一轮就不发布三路输出,后续才由小模型继续。常规模型路径失败则抛出异常。

rawPredictions 是未经处理的原始张量吗?

不完全是。sendraw 复制数组字节发生在解析之后,而 softmax() 已对其切片做过原地归一化,因此不能无条件把它当作完全未经后处理的模型原始张量;它还涉及日志体积与数据隐私。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/ROAD_MODEL_OUTPUT_CONTRACTS_DEEP_DIVE.md(modeld 三路消息、MDN 解码、三种有效位与消费链)。
  • 项目 02 —— openpilot-research/ 同目录探针记录:两项意图脉冲契约假推理隔离探针结论。

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

更新记录

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

评论

发表回复

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