locationd 的四种健康位:20 Hz 输出为什么不能压缩成一个布尔值

作者:

在

结论:locationd 的健康状态是四个独立维度——deviceMotion.valid、inputsOK、sensorsOK、posenetOK,它们不能压缩成一个「正常」布尔值。顶层 valid 只等于 filter_initialized && isfinite(kf.t),不直接包含另外三项;而 filter_initialized 一旦置真,不会在消息异常时自动变回假。名义频率下,约第 2 个相机坏输入或第 10 个 IMU 坏输入才会让 critical_service_inputs_valid 变假。

调度与启动门:先看名义频率

main() 以 cameraOdometry 为 SubMaster 轮询主题,另开独立 socket 批量读取 IMU 消息。服务表配置 加速度计/陀螺仪名义 104 Hz,相机里程计和 deviceMotion 20 Hz。

主题 名义频率 作用
加速度计 / 陀螺仪 104 Hz IMU 观测源,经独立 socket 批量读取
相机里程计 20 Hz 轮询主题,同时触发 deviceMotion 发布
deviceMotion 20 Hz 仅在 sm.updated['cameraOdometry'] 时发布

启动门的细节很关键:初始化阶段只在 sm.all_checks() 与 sensor_all_checks() 同时满足时把 filter_initialized 置真;之后此布尔值不会在消息异常时自动变回假。输出侧,deviceMotion 仅在 sm.updated['cameraOdometry'] 时发布——单有 IMU 更新不触发输出。

循环把 IMU 与 SubMaster 更新的消息按 logMonoTime 排序,仅把顶层 valid=True 的消息交给 handle_log()。这是当前批次内排序,不能保证跨批次全局严格有序;历史观测由滤波器最大回绕窗口约束。进程还可读取持久的 LocationFilterInitialState 作为初始状态/协方差来源,但本快照常规运行代码未定位到写入该键的对应路径——不能假定每次启动都有历史姿态。

十八维状态与四类观测

States 把状态分为 NED 姿态、设备速度、角速度、陀螺偏置、设备加速度、加计偏置,共 18 维。generate_code() 定义姿态旋转、速度积分以及 IMU/相机四类观测方程,运行时通过 rednose 生成的 EKF 执行——不是简单对四种速度取平均。四类观测的门各不相同:

观测 生产条件与变换 拒绝 / 降权边界
加速度计 msg.which() == acceleration,硬件数组重排取负为 [-v[2], -v[1], -v[0]],用传感器纳秒时间戳 时间戳不能为零,与 logMonoTime 差严格不超过 0.1 秒,不能早于滤波状态 0.8 秒以上;bmx055 作为第二 IMU 源被忽略;范数须 <100 m/s²
陀螺仪 同样重排取负,先减滤波器陀螺偏置,再与相机偏航率分布交叉比较 除时间/来源门外,角速度范数须 <10 rad/s,偏航差须低于相机偏航标准差的 30 倍;只有通过才作为 PHONE_GYRO 观测
相机旋转 以 timestampEof - 0.1 秒作为视觉姿态时刻,用 device_from_calib 旋到设备系 受 0.8 秒回绕门;旋转范数 ≤10 rad/s;标准差各分量须 >1e-5,范数不可超对应上限的 10 倍;入滤波前标准差乘 10
相机平移 与旋转同一延迟时间和标定旋转;视觉平移写入 CAMERA_ODO_TRANSLATION 平移范数 ≤200 m/s;标准差须 >1e-5 且范数不超过 10*200,入滤波前标准差乘 4;方差旋到设备系后给 EKF

两处时间语义差异必须记住:carState 在此只更新 abs(vEgo) 供 posenetOK 门使用,不是这只姿态滤波器的直接速度观测;extrinsicsCalibration 在 rpyCalib 非空且分量位于 ±0.5 rad 内时更新 device_from_calib。更关键的是:相机观测没有像 IMU 那样检查自身时间戳与 logMonoTime 的 0.1 秒差,而是按 timestampEof-0.1 与滤波器时刻比较——新硬件不能把两种时间语义混为一谈。

坏输入计数:第 2 个相机、第 10 个 IMU

HandleLogResult 区分成功、时间非法、值非法和传感器来源非法。主循环只对关键主题的时间/值非法增加坏输入计数,成功时乘以按频率计算的 calculate_invalid_input_decay();SENSOR_SOURCE_INVALID 不进入这两个分支。

名义频率下,相机 input_invalid_limit=2、IMU 各为 round(2*104/20)=10,输出门使用比它们各低 0.5 的阈值——所以约第 2 个相机坏输入或第 10 个 IMU 坏输入,才会让 critical_service_inputs_valid=False。源文档特别澄清:实际恢复取决于后续成功样本数量和消息速率,不是固定墙上 10 秒。

四种健康含义各不相同

输出字段 源码计算 不代表什么
deviceMotion.valid filter_initialized && isfinite(kf.t);同样设置四组 XYZMeasurement.valid 不直接包含当前 inputsOK/sensorsOK/posenetOK;某传感器掉线时顶层 valid 不一定同步变假
inputsOK sm.all_valid() 与关键观测坏输入计数门同时成立 all_valid() 只检查消息有效位,不等于全部主题当前 alive/频率正常
sensorsOK 对两路 IMU 各保存最近消息的 valid 位;非仿真要求距接收单调时间 <0.1 秒 不等于加速度/陀螺数值一定通过 handle_log() 的物理合理性与时间检查
posenetOK 对相机平移标准差的前/后各 20 项均值作比,后均值是前均值 4 倍以上且 >7、车速 >5 m/s 时置假(低速放行) 它是模型运动估计的波动门,不是整条视觉链的真实性证明

复位逻辑同样要分清:_finite_check() 在滤波返回的状态或协方差出现非有限值时调用 reset(t);但它不自动重置 filter_initialized 或所有输入计数。上游 selfdrived 对 posenetOK/inputsOK 另产生事件,controlsd 消费设备姿态与车辆参数——健康位与事件门是分层契约。

测试与硬件插件边界

test_locationd_scenarios.py 使用路段回放构造 IMU 缺失、数值峰值和 150 ms 时间偏移场景;其测试依赖路段数据下载/缓存,本轮未执行,不据测试文件存在而宣称当前设备已验证。

自制 IMU/相机插件至少要保持轴序、符号、单位、硬件时间戳、logMonoTime 对齐、噪声标准差及顶层 valid 语义;对异步/乱序观测要验证 0.1 秒传感器门、0.8 秒回绕窗口和视觉 0.1 秒模型延迟。算法层允许回退或过滤观测,独立执行器安全限制仍在 panda 固件。

常见问题

顶层 valid 为真,能否说明所有传感器都正常?

不能。deviceMotion.valid 只等于 filter_initialized && isfinite(kf.t),不直接包含 inputsOK/sensorsOK/posenetOK。由于 filter_initialized 一旦置真不会在消息异常时自动变回假,首次初始化后某传感器掉线,顶层 valid 不一定同步变假。

为什么相机和 IMU 的坏输入阈值差这么多?

因为阈值按名义频率换算:相机 20 Hz 对应 input_invalid_limit=2,IMU 104 Hz 对应 round(2*104/20)=10,输出门再各低 0.5。所以约第 2 个相机坏输入或第 10 个 IMU 坏输入才触发,恢复则取决于后续成功样本数量和消息速率,不是固定时长。

posenetOK 变假是否等于视觉定位失效?

不等于。它是对相机平移标准差前后各 20 项均值作比得到的波动门(后半均值是前半 4 倍以上且 >7、车速 >5 m/s 时置假,低速放行),只反映模型运动估计的波动,不是整条视觉链的真实性证明。

相机观测和 IMU 观测的时间门一样吗?

不一样。IMU 检查自身时间戳不能为零、与 logMonoTime 差严格不超过 0.1 秒、且不早于滤波状态 0.8 秒以上;相机观测没有检查自身时间戳与该 0.1 秒差,而是按 timestampEof-0.1 与滤波器时刻比较。两种时间语义不能混用。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/LOCATIOND_SENSOR_FUSION_HEALTH_DEEP_DIVE.md(取用了 18 维状态、四类观测门、坏输入计数与四种健康含义)。
  • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(仅用于确认 selfdrived/controlsd 与健康位事件的分层关系)。

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

更新记录

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

评论

发表回复

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