结论:坐标变换链里的矩阵和常数是当前设备配置,不是新摄像头或 IMU 的通用标定结果。三套轴向必须分清:road 为前/左/上,device 为前/右/下,view 为右/下/前;euler2rot_single() 用 Rz(yaw) @ Ry(pitch) @ Rx(roll),角度是弧度。更隐蔽的是「什么时候接受标定值」这件事,模型与 UI 两道门并不一样;此外接入未登记的新设备/传感器键会在静态路径上直接抛 KeyError。以下基于源码快照 c8fb906 的静态阅读与独立探针,未取得实机镜头、安装位姿或模型实体做数值验证。
坐标系与单位
| 坐标系 | 源码定义 | 接口要求 |
|---|---|---|
| 地理坐标 | geodetic2ecef_single() 接收纬度、经度、海拔 |
输入纬经度是度,函数内转弧度;海拔按 ECEF 公式使用长度单位;输出 x/y/z 为同一长度单位(通常米)。 |
| ECEF / NED | LocalCoord 以一处地理或 ECEF 坐标定原点 |
NED 的 z 朝下;ned2ecef 加回 ECEF 原点,ecef2ned 先减原点。不能把 GNSS 纬经度直接当车身坐标。 |
| 路面/设备/视图 | camera.py 轴向定义 |
road 前/左/上,device 前/右/下,view 右/下/前;view_frame_from_device_frame 是固定轴重排矩阵。 |
| 姿态 | euler2rot_single() 使用 Rz(yaw) @ Ry(pitch) @ Rx(roll) |
角度用弧度,欧拉角排列为 roll/pitch/yaw;rpyCalib 与 IMU 输出不可擅自交换轴或度/弧度。 |
五个文件与关键入口
几何能力集中在 common/transformations 的五个文件里,各自有明确职责与边界:
| 文件与关键入口 | 实际功能及边界 |
|---|---|
camera.py: CameraConfig、DeviceCameraConfig |
每路相机用宽、高、单个焦距构造 K(fx=fy、主点在画面中心);此对象不含真实镜头畸变系数或在线外参优化。 |
DEVICE_CAMERAS |
以 (deviceType, sensor) 选三路相机配置,含部分 unknown 兼容键;AR/OX 的 fisheye 焦距在源码注释里被标为可能不准。 |
get_view_frame_from_road_frame()、get_view_frame_from_calib_frame() |
按旋转角和高度构造 3x4 外参;模型预处理使用的高度为 0,其他调用不应隐去平移项。 |
normalize()、denormalize() |
用 K^-1 与 K 在像素坐标与归一化针孔坐标间变换;都不是带畸变的鱼眼矫正。 |
device_from_ecef()、img_from_device() |
前者依位姿四元数把 ECEF 点转设备系;后者转 view 后除以深度,负深度置 NaN,零深度无显式安全分支。 |
model.py: get_warp_matrix() |
计算 intrinsics @ view_frame_from_device_frame @ rot_from_euler(...) @ calib_from_model;bigmodel_frame=True 选 sbig 逆矩阵。 |
运行链:模型与 UI 的标定门不一样
modeld 在 extrinsicsCalibration 更新且已见设备/窄路相机状态时,用 (deviceType, sensor) 从 DEVICE_CAMERAS 取配置,按主/辅相机选内参并计算两套 warp。该访问没有默认配置分支——若接入新设备/传感器组合而未登记键,静态代码路径会抛 KeyError。而且这个触发条件没有显式要求 extrinsicsCalibration.valid 或 calStatus=calibrated,所以不能把 modeld 的矩阵更新等同于标定已通过全部质量门。
行车叠加的 AugmentedRoadView._update_calibration() 同样选择 DEVICE_CAMERAS,但更新外参前显式检查消息更新、valid、三元素 rpyCalib 和 calStatus=calibrated;宽相机还有 wideFromDeviceEuler 路径。也就是说,模型与 UI 的「何时接受标定值」门不同;单凭叠加图像看起来对齐,不能证明模型输入几何或相机身份正确。Calibrator 用姿态估计累积筛选标定块、依 pitch/yaw 范围更新状态,并在文件头明确假设 roll 为零,模型输入不校正 roll。
退化输入与边界
独立探针给出了几处具体边界:零角度且输入 med 模型内参时 get_warp_matrix() 为单位矩阵;normalize([2000, 600]) 在宽 1928 的内参下仍返回有限值,因为 normalize() 只把带负值的点置 NaN、不限制图像上界;denormalize() 未给边界时也返回有限值,传入 width=1928, height=1208 后返回 NaN,但它只拒绝 x>width,x==width 仍被接受,所以该边界不是有效像素索引判据。img_from_device() 的批量 (1,3) 背向点返回 NaN,但 (3,) 单点输入会因末尾二维切片触发 IndexError——注释称支持单点,实际不支持。未知传感器键则直接抛 KeyError。
测试现状方面:test_coordinates.py 含 ECEF/NED 数值样本、单点/批量和错误输入检查,test_orientation.py 含姿态转换一致性;但当前 transformations 测试目录未见独立的镜头畸变、DEVICE_CAMERAS 新键、img_from_device() 单点或 get_warp_matrix() 标定回归测试。用隔离运行器加载两份仓库原测试文件、仅把 OpenpilotTestCase 临时替换为标准 unittest.TestCase 后,12 个原测试方法全部通过——它执行的是原测试方法与原变换函数,但不运行项目测试夹具,不能替代镜头/IMU 实机标定。
新硬件接入至少需要什么
- 为真实相机/传感器身份登记尺寸和内参,避免行车路径上的
KeyError; - 以标定板或实景观测测量焦距、主点、鱼眼畸变、主宽相机相对姿态与安装高度;
- 确认 device/road/view 轴向与
rpyCalib弧度定义,不要让度/弧度或轴序在链路中被悄悄替换; - 对相机 SOF、IMU、GNSS 使用可比较时间基;
- 在回放中同时检查模型输入 warp、UI 叠加、定位残差与异常输入。
应由受控平台层发布标定结果及质量状态,普通插件不得在行车中任意替换几何矩阵或伪造校准完成标志。实机误差、长期漂移与车辆级安全影响仍待台架和封闭场地验证。
常见问题
模型输入几何能用 UI 叠加看起来对不对来验证吗?
不能。模型与 UI 的「何时接受标定值」门不同:UI 显式检查 valid、三元素 rpyCalib 与 calStatus=calibrated,而 modeld 的触发条件没有显式要求这些。叠加图像对齐不等于模型输入几何或相机身份正确。
接入新的相机或传感器组合为什么会直接抛 KeyError?
因为 DEVICE_CAMERAS 按 (deviceType, sensor) 取配置,代码没有默认配置分支。未登记键的设备/传感器组合会在静态路径上直接抛 KeyError,必须先登记尺寸与内参。
img_from_device() 支持单点输入吗?
注释称支持,但当前实现在单点 (3,) 输入时会因末尾二维切片抛 IndexError,批量 (N,3) 才可用。零深度也没有显式安全分支,调用者须自行处理非有限投影。
几何常量可以直接搬到新摄像头上吗?
不能。本文所述矩阵与常数是当前设备配置,不是新摄像头或 IMU 的通用标定结果;AR/OX 的 fisheye 焦距在源码注释里也被标为可能不准。新硬件应重新测量并规定轴向与弧度约定。
延伸阅读
- 车载传感器的曝光与 ISP —— 相机原始像素到感知输入的另一段几何与信号处理
- 52 张预览自动校验:五屏 HUD 怎么把「文字出界」变成一条断言 —— 视觉结果如何转成可复现的断言
- 标定状态持久化 —— pitch/yaw 标定块的累积、筛选与持久化
- 离线与实时数据管线的边界 —— 回放中同时核对模型输入与定位残差的工程方法
参考资料
- 项目 02 ——
openpilot-research/GEOMETRY_TRANSFORMS_HARDWARE_DEEP_DIVE.md(坐标变换与相机几何接口契约,含源码行号索引) - 项目 02 ——
openpilot-research/probes/run_transformations_source_tests.py(加载原测试文件的隔离运行器,12 个原测试方法通过) - 项目 02 ——
openpilot-research/probes/test_camera_geometry_plugin_contract.py(相机几何四项隔离探针)
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复