标签: 有效位

  • 仿真相机与传感器:图像缓冲、采样节拍及有效位

    结论:仿真感知链适合状态机和流程回放,但不能替代传感器校准、时钟同步、panda 安全钩子或物理总线测试。发布时戳不是物理曝光时戳——仿真把同一个 logMonoTime 同时当作 VisionIPC 的 SOF 与 EOF;重复样本也不等于传感器频率——主循环每圈发 5 条加速度、5 条陀螺仪与 10 条 GPS。所有频率推导均以 SimulatorBridge 主循环达到标称 100 Hz 为前提,实际频率未测。固定快照基线为 c8fb906。

    图像从 MetaDrive 到 VisionIPC

    MetaDriveBridge.spawn_world() 为窄路相机及可选广角相机配置 RGB 图像源,分辨率取 W=1928, H=1208。MetaDriveWorld 用 multiprocessing.Array 暴露两块 RGB 内存;metadrive_process() 把渲染图像写入共享数组后对 image_lock.release() 计数;桥接侧另有目标 20 Hz 的相机线程,在 send_camera_images() 开头 acquire() 一次,再把当前数组转为 NV12 并发布。

    这里的 image_lock 实际是初值 0 的 multiprocessing.Semaphore,用于等待至少一帧,而非读写互斥:生产者写图前不获取它,消费者转换 RGB 时也不阻止下一次写入。因此静态上存在读取一幅图像过程中被生产者覆盖的窗口;若生产速度超过消费者,semaphore 计数积压,消费者可能对同一块「最新图像」连续取样。是否出现撕裂或重复帧需要像素哈希 / 帧号探针验证。

    环节 实现事实
    色彩转换 rgb_to_nv12() 用整数 BT.601 近似系数生成 Y,2×2 box filter 后交错 U / V;要求偶数宽高;调用方固定断言 1928×1208×3 且 uint8。
    缓冲 Camerad.__init__() 为每路建立 5 个 VisionIPC 缓冲;窄路总建,广角按 dual_camera 决定。
    发布 _send_yuv() 先构造外层 Event.valid=True 的 *CameraState,把同一个 dat.logMonoTime 同时当作 SOF / EOF,再发布独立递增的仿真帧号和单位变换矩阵。
    有效位 底层 VisionIpcServer.send() 另把 extra.valid 固定为 False;CameraState 外层有效位与 VisionIPC extra 有效位不同。

    三项原类探针确认单路只分配窄路五个缓冲、双路帧号分别从零递增、图像发送先于状态消息发布,且每帧 SOF = EOF。它是仿真 RGB 走 CPU 拷贝的路径,不证明真实 ISP 的色彩、曝光或 DMA 缓存行为。探针还确认:若假 VisionIPC 已接受图像而假 PubMaster.send() 抛错,cam_send_yuv_road() 尚未递增帧号;调用方若捕获异常并再次调用,会用同一帧号发送另一份图像、但产生新时间戳——这是函数边界的部分提交状态,不证明现有桥接线程会自动重试。

    IMU、GPS、驾驶员与外设占位

    SimulatorBridge._run() 以目标 100 Hz 的主循环调用 SimulatedSensors.update(),另起目标 20 Hz 相机线程。每次 update() 中,send_imu_message() 连续发布 5 条加速度和 5 条陀螺仪消息,数据都取同一份 SimulatorState.imu,各条的内层 timestamp 使用各自消息的 logMonoTime,并非模拟器采样时刻。

    消息 仿真节拍与标称值
    IMU(加速度 / 陀螺仪) 每圈各 5 条;100 Hz 主循环下约 500 条 / 秒 / 主题;服务表标称 104 Hz 不是实际硬件节拍。
    GPS 仅在 simulator_state.valid 时,每圈发布 10 条 gpsLocationExternal,约 1000 条 / 秒;服务表标称 10 Hz。
    驾驶员状态 update() 以 DT_DMON/2 检查驾驶员消息,以 0.25 秒检查外设状态,并非严格 20 / 4 Hz 定时器。

    send_gps_message() 把当前系统墙钟填成 unixTimestampMillis、固定 flags=1、精度常量和 source=ublox;这些重复包不能用于证明 GNSS 热启动、时钟校准、观测协方差或实际链路吞吐。send_fake_driver_monitoring() 把两侧脸概率置 1、分心置假、注意力置满;driverMonitoringState 外层显式有效,而 driverStateV2 使用 new_message() 默认外层 valid=False。不可把这些「健康」字段当作自研传感器或驾驶员监测的物理验证。

    假时钟探针还确认两个节拍门都使用严格 >:恰好到 0.25 秒时不发外设状态,到 0.251 秒才发;只有发送后才更新对应的上次发布时间。无效仿真状态并不阻止驾驶员 / 外设占位消息,且 driverStateV2.valid=False 与 driverMonitoringState.valid=True 会同时出现。

    退出、测试和插件化边界

    仿真启动脚本 launch_openpilot.sh 设置 NOBOARD=1、SIMULATION=1,屏蔽真实 camerad、encoderd、loggerd 等进程。主循环、相机线程和 MetaDrive 子进程的生命周期分散在桥接层;相机线程的 Semaphore.acquire() 没有超时,模拟器不再产帧时单靠线程 exit_event 不能立即唤醒它。现有 test_driving() 被 unittest.skip 标记,不能据此声称当前闭环已通过。

    自研硬件插件应分别验证图像采样帧号、读出时戳、消息外层 / 内层有效位、stride 与缓冲持有期,以及 IMU / GNSS 的独立采样时刻和真实频率。建议先在隔离命名空间记录每路源帧号、共享内存哈希、发布时戳和消费时戳,再注入掉帧 / 停发 / 速率偏差。

    常见问题

    仿真里 SOF 和 EOF 为什么不等于曝光时间?

    因为 _send_yuv() 把同一个 dat.logMonoTime 同时当作 VisionIPC 的 SOF 与 EOF,表示「在软件发布时打的同一时刻」,不是传感器曝光 / 读出测量。它是仿真 RGB 走 CPU 拷贝的路径,不证明真实 ISP 行为。

    「CameraState 有效」和「图像有效」是一回事吗?

    不是。仿真里 *CameraState 外层 Event.valid=True,而底层 VisionIpcServer.send() 把 extra.valid 固定为 False。两者是不同层级的有效位,必须分别检查。

    仿真里 IMU 和 GPS 的频率是多少?

    取决于主循环实际频率。每次 update() 发 5 条加速度、5 条陀螺仪与 10 条 GPS;若主循环确为 100 Hz,则约 500 条 / 秒 / IMU 主题、约 1000 条 / 秒 GPS,而服务表标称 IMU 104 Hz、GPS 10 Hz。重复样本不等于传感器频率。

    仿真通过能代替传感器标定吗?

    不能。仿真链适合状态机和流程回放,但不能替代传感器校准、时钟同步、panda 安全钩子或物理总线测试。若要验证自研硬件,应分别记录源帧号、共享内存哈希、发布 / 消费时戳,并注入掉帧、停发与速率偏差。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/SIM_CAMERA_SENSOR_TIMING_DEEP_DIVE.md(取用了图像缓冲与 semaphore 行为、IMU / GPS 仿真节拍、有效位与插件化边界分析)。

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

    更新记录

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

  • 8 个扭矩桶、4000 点门槛:横向扭矩在线估计与三层「可用」

    结论:横向扭矩响应的在线估计把样本分进八个扭矩桶,用全最小二乘拟合「扭矩—横向加速度」的斜率与偏置,并以残差离散度近似摩擦。但它对外暴露的「可用」其实是三层不同的门:顶层消息有效位来自输入通信检查,内层参数有效位要求各桶达到 [100, 300, 500, 500, 500, 500, 300, 100] 的最小点数且总数达到 4000,「是否使用参数」则只是品牌白名单与调校类型门。三者互不推导,控制侧甚至在未检查内层有效位的情况下就可能使用估计值。

    采样门:从健康位到扭矩桶

    采样横向加速度按「车速 × 校准偏航率 − sin(滚转) × 重力」计算。进入桶之前要逐层过关:上游各健康位全真、扭矩历史缓冲填满、横向控制已连续激活并稳定 2 秒且驾驶员未覆盖、车速大于 15 m/s、扭矩绝对值大于 0.02、横向加速度绝对值不超过 1。入桶时扭矩值还要落在八个开右端点的 [−0.5, 0.5) 区间之一,区间外的点会被静默忽略。

    门 条件 备注
    健康位 / 历史缓冲 全真且历史填满 前置门,不满足则不入桶
    激活稳定性 连续激活 2 秒、无驾驶员覆盖 —
    车速 >15 m/s 严格大于
    扭矩绝对值 >0.02 严格大于
    横向加速度 ≤1 m/s² 超限不入正常桶

    有个容易被忽略的边界:给车控、执行输出与车况的时间戳加的是「当时」的延迟,而设备观测用的是消息内部时间戳。若延迟被置成负值,用于插值的时间窗可能为空;由于对空序列的判断特性,即便激活历史全假、驾驶员覆盖全真,样本也可能被放行。正常延迟名义上是正的,这只是一个「函数本身没有拒绝异常延迟」的说明,并不代表生产环境会收到负延迟。

    八个桶与 4000 点门槛

    「能算」和「算得可信」是两个不同的门:只要每个桶里至少有一个点,就可以先输出原始拟合;但要标记为有效,各桶必须达到各自的最小点数,且总点数不少于 4000。进度百分比取「总量完成率」与「最低桶完成率」的平均后取整——这意味着某些桶偏少时,整体进度会被拉低。

    桶(扭矩区间,左闭右开) 最小点数
    第 1 桶 100
    第 2 桶 300
    第 3 ~ 6 桶 各 500
    第 7 桶 300
    第 8 桶 100
    合计 ≥4000

    全最小二乘与负斜率的坑

    拟合在「扭矩、常数 1、横向加速度」组成的点阵上做全最小二乘(SVD),把样本随机抽到最多 2000 点,只捕获线性代数异常。斜率与摩擦分别被约束在离线调校的 ±30% 与 ±50% 之内,偏置没有同类限幅。合成验证给出一个值得记住的结论:当斜率为负时(例如拟合 y = −2x + 0.5),斜率本身能被正确恢复,但摩擦却显著非零——原因是把斜率转成旋转角时用了平方根,结果恒为非负,负斜率下的残差没有沿直线方向被消去。这是合成负斜率分支的源码/数值结论,并不证明正常车型会出现负扭矩因子。

    数值健壮性还有一处边界:发布与滤波前只用「空值 / 非数」判断拟合结果,没有显式检查正负无穷;在合成输入下,偏置为无穷仍可能带着内层有效位进入滤波。这提示序列化与滤波层都需要独立的有限值校验。

    三层「可用」不是同一门

    层 来源 实际含义
    顶层消息有效 输入主题的通信/频率/有效性检查 不表示拟合已经成熟
    内层参数有效 桶分布与数量达标、拟合非空非非数 估计是否成熟
    是否使用参数 品牌白名单 + 调校类型 与样本量、内层有效位无关

    关键消费事实是:控制侧在扭矩车型上只要求「主题通信健康」与「是否使用参数」为真,就调用参数更新,没有检查内层有效位。因此未完成新标定时,也可能使用初始离线值或历史滤波值——不能把「内层无效」误读为「控制器必然停用」。滤波本身按一阶方式更新,衰减时间从 50 持续增大到最多 250;一次更新里会先用旧系数更新滤波值,再用增大后的衰减重算系数。整个过程最终仍要经过车辆接口与 panda 安全策略,估计插件不得直接发送 CAN。

    持久化与恢复的边界

    缓存键是持久参数。恢复时先核对车型指纹与恢复键:匹配且内层有效时恢复三个滤波值与缓存点;兼容但内层无效时,仍会加载缓存点并恢复衰减值。恢复不是事务式提交——一旦解析中途异常,内存中已经装载的部分状态可能保留,而已写入的缓存键会被删除,形成一个「条件性部分恢复」的风险;车型指纹或版本不匹配时则跳过缓存但不删除键。

    插件化边界

    做插件化时,扭矩估计器宜先作为只读影子实例,输出独立候选主题,控制端只允许单一权威发布者。插件契约需要显式携带车型/调校版本、延迟时间语义、坐标系、采样覆盖率、顶层通信有效位、内层拟合有效位与输出年龄。切换为权威源前必须证明缓存恢复、缺失或异常输入、横向激活与驾驶员覆盖门以及回放等价性,且不得绕开既有安全许可。

    常见问题

    每个扭矩桶里有点,就等于参数可用了吗?

    不等于。「能算」只要求每个桶非空,可以先输出原始拟合;「可信」还要求各桶达到 100/300/500 的最小点数且总数达到 4000,才会把内层参数有效位标为真并更新滤波值。

    从缓存恢复的估计点一定是本次标定的吗?

    不一定。兼容时无论内层有效位真假,都会加载缓存点并恢复衰减值;缓存恢复不是事务式提交,解析中途异常可能留下部分内存状态,因此只能视为条件性部分恢复。

    为什么负斜率的摩擦估计会明显偏大?

    因为把斜率转成旋转角时使用了平方根,结果恒为非负,负斜率下的残差没有沿直线方向被消去,于是垂直残差偏大、摩擦被高估。这是合成负斜率分支的结论,不证明正常车型会出现负扭矩因子。

    内层参数标记为无效,控制器会不会停用估计值?

    不会仅凭这一点停用。控制侧只检查主题通信健康与「是否使用参数」,不检查内层有效位,因此未完成新标定时仍可能使用初始离线值或历史滤波值;最终输出还要经过车辆接口与安全策略。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TORQUED_LATERAL_TORQUE_LEARNING_DEEP_DIVE.md(采样门、八个扭矩桶与 4000 点门槛、全最小二乘与滤波、三层有效位、持久化与插件边界)

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有桶阈值、点数门槛与配置均出自源文档,标注为未验证的结论保留其原有边界;合成点拟合结论不代表实车扭矩响应。

    更新记录

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

  • 车速 >1 m/s、转角 <45°:车辆参数在线学习的九维 EKF 与观测门

    结论:车辆参数在线学习是一台九维卡尔曼滤波器,但九个状态里只有横向速度与偏航率两个有显式微分方程,其余七个在状态转移中保持不变。学习被三道门约束:车速 >1 m/s、转向角绝对值 <45°、且非倒挡;此外偏航率与路面滚转观测只在「位姿有效」时才写入。它的输出是在线估计值,不是车辆认证参数,也不能替代独立的执行器安全限制。

    九维状态与两条微分方程

    滤波器状态依次为:轮胎刚度因子、转向比、慢零偏、快零偏、二维车速(纵向/横向)、偏航率、转向角与路面滚转。动力学按线性化单轨横向模型生成:每步用一阶欧拉积分 x_next = x + dt·x_dot 推进,而代码只给横向速度与偏航率指定了导数,其余七个状态只是保持原值。

    状态 含义 是否有显式微分方程
    STIFFNESS 轮胎侧偏刚度因子 否
    STEER_RATIO 转向比 否(出现在动力学分母)
    慢 / 快 ANGLE_OFFSET 转向零偏(慢、快两档) 否(慢零偏无直接观测方程)
    VELOCITY(二维) 纵向 / 横向车速 仅横向速度有方程
    YAW_RATE 偏航率 是
    STEER_ANGLE 转向角 否
    ROAD_ROLL 路面滚转 否

    横向速度与偏航率的导数中,纵向速度与转向比都出现在分母;而学习门只在外部输入层面检查车速与转角,并未对滤波内部状态每次预测时的分母做形式化保证。实现也没有把车身侧倾、路面超高、轮胎非线性或高阶积分显式放进这套方程,因此不能把它外推为全工况动力学模型。

    什么条件下才允许学习

    只有在车辆运动且转向不极端时才启用学习:车速大于 1 m/s、转向角绝对值小于 45°、且不在倒挡。偏航率观测要求角速度有效、标准差在 0 到 10 rad/s 之间、偏航率绝对值小于 1 rad/s,否则以 0 和 10 度标准差替代;滚转观测要求标准差小于 1.5°、数值严格落在 ±10° 内且传感器状态正常,否则同样回退,并在进入滤波器前受单步限速。若「位姿有效」为假,则偏航率与滚转观测不写入,但快零偏以及刚度/转向比的弱约束观测仍会继续。

    输入 启用条件 不满足时
    车况(学习使能) vEgo > 1 m/s、|转向角| < 45°、非倒挡 暂停学习,设置滤波时间
    偏航率观测 角速度有效、0 < std < 10 rad/s、|yaw| < 1 rad/s 以 0 与 10° 标准差替代
    滚转观测 标准差 < 1.5°、数值严格在 ±10° 内、传感器正常 以 0 与 10° 标准差替代
    位姿门 位姿有效为真 不写偏航率/滚转观测,弱约束仍执行

    观测码与噪声:默认表不等于实际传入

    滤波器为每个观测码准备了默认噪声,但调用方可以显式覆盖。例如偏航率与横向速度的默认项本身「未定义」,由运行时代码显式传入方差;滚转的默认项也会被显式覆盖。这意味着「默认表」只是兜底,实际噪声要按调用点判断。

    观测码 直接观测的状态 默认噪声项
    ROAD_FRAME_YAW_RATE 偏航率 未定义,由调用方显式传方差
    ROAD_FRAME_X_SPEED 纵向车速 0.1²
    STEER_ANGLE 转向角 rad(0.05)²
    ANGLE_OFFSET_FAST 快零偏 rad(10)²
    STEER_RATIO 转向比 5²
    STIFFNESS 刚度因子 0.5²
    ROAD_ROLL 路面滚转 rad(1)²(调用时显式覆盖)

    有效位分层:一个「healthy」不够用

    这套输出的有效位是分层的,不能折叠成一个布尔值:顶层「消息有效」直接采用输入的通信检查结果;内层「参数有效」是慢/总零偏、滚转范围与标准差、刚度及转向比范围的合取;「传感器有效」只在活动且车速大于 10 m/s 时检查车速与偏航率差的量级是否小于 4 m/s²,且不进入内层有效的合取;「位姿有效」在本快照恒为真,因此不能当作上游位姿状态的透传。输出值还对角度与滚转各做每帧 1° 的限速。

    下游消费同样要注意边界:控制侧直接读取刚度、转向比、零偏与滚转,其中前两者仅以「与 0.1 取较大值」设下限后更新车辆模型,并没有用内层有效位包住这次更新。安全效果依赖上层状态机、通信故障处理与独立网关的组合,不能把某一个有效位当成单点安全证明。

    持久化与插件边界

    正常运行会周期性写入持久缓存,并另有一个由车型流程写入的「上一行程车型参数」键用于车型核对。恢复时要求两个键都存在,且只明确校验车型指纹相同、历史转向比落在相对范围内;缺任一键就回退默认值且不清除另一个键,车型不符或越界时只删除估计缓存键、保留车型键。写缓存的节拍按帧号取模,「约一分钟」取决于循环节拍,不是墙上计时保证。插件化时,估计器可作为单写者、版本化、可回放的候选,但应先做影子输出比对,不直接抢占正式主题;输入契约须固定时间戳、坐标系、角度单位、车型参数版本与噪声标准差,输出契约要保留顶层通信有效位、内层参数有效位、分项有效位与数据年龄。

    常见问题

    在线估计出的车辆参数能当认证参数用吗?

    不能。它是在线估计值,依赖行驶激励与观测门;低速、倒挡或转向极端时学习会暂停,慢零偏甚至没有直接观测方程。它不能替代车辆认证参数,也不能替代独立的执行器安全限制。

    停车时滤波器还在更新吗?

    不会照常更新。车况门要求车速大于 1 m/s 且转角绝对值小于 45° 才启用学习,停车后不再产生新的学习观测,并会停止协方差的时间增长与回绕。

    为什么同一个参数会有好几个「有效位」?

    因为它们语义不同:顶层有效位来自输入通信检查,内层有效位是参数范围与不确定度的合取,传感器有效位只做横向一致性检查且不参与内层合取,位姿有效位在本快照恒为真。折叠成一个 healthy 会丢掉这些区别。

    倒车时会参与参数学习吗?

    不会。学习门明确排除倒挡:只有车速大于 1 m/s、转向角绝对值小于 45° 且非倒挡时才启用学习。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PARAMSD_VEHICLE_LEARNING_DEEP_DIVE.md(九维 EKF 方程、观测码与噪声、学习门、有效位分层、持久化与插件边界)

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有状态维度、阈值与配置均出自源文档,标注为未验证的结论保留其原有边界;参数均为在线估计值,不代表任何车型的认证参数。

    更新记录

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

  • 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