复习:内参
内参(intrinsics)描述相机的内部成像几何 ,与相机装在哪、朝哪完全无关,只由镜头和图像传感器决定。它解决一个问题:给定一条从光心射出的射线方向,它会落在图像的哪个像素上(以及反过来)。
内参矩阵 K
针孔模型下,归一化像平面坐标 ( x n , y n ) = ( X c / Z c , Y c / Z c ) (x_n, y_n) = (X_c/Z_c, Y_c/Z_c) ( x n , y n ) = ( X c / Z c , Y c / Z c ) 映射到像素 ( u , v ) (u, v) ( u , v ) :
[ u v 1 ] = K [ x n y n 1 ] , K = [ f x 0 c x 0 f y c y 0 0 1 ] \begin{bmatrix} u \\ v \\ 1 \end{bmatrix}
= K \begin{bmatrix} x_n \\ y_n \\ 1 \end{bmatrix},
\qquad
K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} u v 1 = K x n y n 1 , K = f x 0 0 0 f y 0 c x c y 1
四个参数:
f x , f y f_x, f_y f x , f y :以像素为单位的焦距。物理焦距 f f f (毫米)除以像元尺寸得 f x = f / d x f_x = f/d_x f x = f / d x 、f y = f / d y f_y = f/d_y f y = f / d y 。正方形像元时 f x = f y f_x = f_y f x = f y ,但标定里常分开估以容许非正方形像素。
c x , c y c_x, c_y c x , c y :主点(principal point),光轴穿过图像平面的位置。理论上接近图像中心,但不严格等于几何中心,必须标定。
反投影:测距/重建的逆过程
正向(世界 → 像素)用于渲染、投影;逆向(像素 → 射线)用于测距、重建、SLAM。求逆(K K K 是上三角,逆容易):
[ x n y n 1 ] = K − 1 [ u v 1 ] , K − 1 = [ 1 / f x 0 − c x / f x 0 1 / f y − c y / f y 0 0 1 ] \begin{bmatrix} x_n \\ y_n \\ 1 \end{bmatrix}
= K^{-1}\begin{bmatrix} u \\ v \\ 1 \end{bmatrix},
\qquad
K^{-1} = \begin{bmatrix} 1/f_x & 0 & -c_x/f_x \\ 0 & 1/f_y & -c_y/f_y \\ 0 & 0 & 1 \end{bmatrix} x n y n 1 = K − 1 u v 1 , K − 1 = 1/ f x 0 0 0 1/ f y 0 − c x / f x − c y / f y 1
得到归一化坐标后,射线方向就是 d = [ x n , y n , 1 ] ⊤ \mathbf{d} = [x_n, y_n, 1]^\top d = [ x n , y n , 1 ] ⊤ (再归一化)。内参错则所有射线方向一起偏 ——这是内参精度直接决定下游测距/重建精度的原因。
Kalibr 里的内参记法
Kalibr 的 camchain.yaml 中,每个相机有 intrinsics 字段,对 pinhole 模型是 [fx, fy, cx, cy],畸变单独在 distortion_coeffs 里。命令行用 --models 指定模型类型(pinhole-radtan / pinhole-equi / omni 等),见 B11。
易错点
主点不是图像几何中心 。把它默认设成 ( W / 2 , H / 2 ) (W/2, H/2) ( W /2 , H /2 ) 会引入系统偏差,主点必须标。
f x ≠ f y f_x \ne f_y f x = f y 是正常的(非正方形像元、镜头装配误差),不要强行取平均。
内参和分辨率绑定。同一相机不同分辨率(binning、ROI、缩放),f x , f y , c x , c y f_x, f_y, c_x, c_y f x , f y , c x , c y 都要等比例换算或重标,不能直接套用。
内参假设焦距固定。变焦、重新对焦后内参变化,要重标。
参考
复习:外参与标定本质
外参(extrinsics)描述相机相对某个参考坐标系(世界系、机器人体系、另一台相机)的位姿。它和内参正交:内参管”镜头怎么成像”,外参管”相机装在哪、朝哪”。
外参的数学表示
外参是一个 S E ( 3 ) SE(3) S E ( 3 ) 变换 T = ( R , t ) T = (R, \mathbf{t}) T = ( R , t ) ,4×4 齐次矩阵:
[ p C 1 ] = T C W [ p W 1 ] = [ R C W t C W 0 ⊤ 1 ] [ p W 1 ] \begin{bmatrix} \mathbf{p}^C \\ 1 \end{bmatrix}
= T_{CW}\begin{bmatrix} \mathbf{p}^W \\ 1 \end{bmatrix}
= \begin{bmatrix} R_{CW} & \mathbf{t}_{CW} \\ \mathbf{0}^\top & 1 \end{bmatrix}\begin{bmatrix} \mathbf{p}^W \\ 1 \end{bmatrix} [ p C 1 ] = T C W [ p W 1 ] = [ R C W 0 ⊤ t C W 1 ] [ p W 1 ]
R C W ∈ S O ( 3 ) R_{CW} \in SO(3) R C W ∈ S O ( 3 ) :旋转,世界系坐标轴在相机系里的朝向。
t C W ∈ R 3 \mathbf{t}_{CW} \in \mathbb{R}^3 t C W ∈ R 3 :平移,世界系原点在相机系里的位置(即相机光心在世界系里的位置的负值变换)。
下标读法:T C W T_{CW} T C W 把世界系(W W W )下的坐标变到相机系(C C C )。多相机标定里关心的是 T C 0 C 1 T_{C_0 C_1} T C 0 C 1 (相机间外参),见 B10。
标定本质:一个反问题
标定是相机成像的逆问题 。正向过程已知:标定板上每个角点的 3D 世界坐标 p i W \mathbf{p}_i^W p i W (板的几何已知),它们在图像里被检测到的 2D 像素 u i \mathbf{u}_i u i 。已知正向映射:
u i = π ( K , d , T C W , p i W ) + 噪声 \mathbf{u}_i = \pi\big(K,\, \mathbf{d},\, T_{CW},\, \mathbf{p}_i^W\big) + \text{噪声} u i = π ( K , d , T C W , p i W ) + 噪声
其中 π \pi π 是投影函数(含内参 K K K 、畸变 d \mathbf{d} d 、外参 T C W T_{CW} T C W )。标定是反过来:给定一堆 ( p i W , u i ) (\mathbf{p}_i^W, \mathbf{u}_i) ( p i W , u i ) 配对,求 K K K 、d \mathbf{d} d 、T C W T_{CW} T C W 。
min K , d , T C W ∑ i ∥ u i − π ( K , d , T C W , p i W ) ∥ 2 \min_{K,\, \mathbf{d},\, T_{CW}} \sum_i \big\| \mathbf{u}_i - \pi(K, \mathbf{d}, T_{CW}, \mathbf{p}_i^W)\big\|^2 K , d , T C W min i ∑ u i − π ( K , d , T C W , p i W ) 2
这就是最小二乘拟合,残差就是重投影误差 (见 B08)。求解用非线性优化迭代(Levenberg-Marquardt 等)。
为什么需要多帧、多姿态
单帧(一个相机位姿看一次板)只能给出 K K K 的部分约束(如用 DLT 得到投影矩阵再分解),且相机位姿 T C W T_{CW} T C W 和某些内参分量耦合(如沿光轴的平移和焦距 f f f 有尺度耦合)。要可靠解出全部内参,需要多帧、板子相对相机有多种姿态和距离 ——让约束来自不同方向,打破耦合。这也是采集时要”覆盖不同距离和角度”的数学原因。
外参的物理意义:把多相机融合到一系
多相机系统里,每个相机有自己的内参和相对世界(板)的位姿。外参 T C i C j T_{C_i C_j} T C i C j 让任一相机的观测都能变到统一坐标系,是 BEV 鸟瞰拼接、多传感器融合的基础。标定外参差一度,远处物体投影到统一系就偏出近一米——精度要求由此倒推。
易错点
T T T 的下标顺序是最常见的错 。T C W T_{CW} T C W 和 T W C T_{WC} T W C 互逆,含义完全不同。养成”相邻下标咬合”的习惯:T C 0 W = T C 0 C 1 T C 1 W T_{C_0 W} = T_{C_0 C_1}\,T_{C_1 W} T C 0 W = T C 0 C 1 T C 1 W 。
标定板的世界系是任意的(通常取板的一个角点)。改变世界系定义会改变 T C W T_{CW} T C W ,但相机间外参 T C 0 C 1 T_{C_0 C_1} T C 0 C 1 不变——后者才是多相机融合真正要的。
外参是相对某个参考系说的,不指明参考系的外参无意义。
参考
标定板 AprilGrid
标定需要一个几何已知 的参照物——标定板,板上每个特征点的 3D 坐标在板坐标系里精确已知。Kalibr 推荐 AprilGrid,它由 AprilTag 排成网格,每个 tag 自带唯一编码。
AprilTag:带 ID 的视觉基准
AprilTag 是一种视觉基准系统(visual fiducial),每个 tag 是一个黑白方块加内部编码图案,像一个二维条形码。它的关键能力:
唯一 ID :每个 tag 编码不同,解码后知道”这是网格里的第几格”,从而知道它在板坐标系里的精确 3D 位置。
鲁棒检测 :基于边缘检测 + 四边形拟合 + 解码,对光照、部分遮挡、视角倾斜都有较好鲁棒性。
AprilGrid vs 传统棋盘格
传统棋盘格(chessboard)依赖角点的规则网格结构识别角点,但有个硬约束:必须整块可见 。如果板子部分出了画面,整块检测失败,这一帧作废。
AprilGrid 的角点是每个 tag 的四个角,每个 tag 独立解码定位:
特性 棋盘格 AprilGrid 部分可见 失败 可用 (拍到几个 tag 就用几个)角点 ID 靠网格顺序推断 每个 tag 角点有唯一 3D 坐标 数据有效率 低(边缘帧作废) 高 检测精度 亚像素角点 亚像素角点
对多相机标定尤其有利:相邻相机视野只有部分重叠时,棋盘格很难同时整块出现在两个相机里,而 AprilGrid 只要重叠区有几个 tag 就能建立共视约束。
几何约束:每个角点一对 3D-2D 对应
AprilGrid 一旦生成,板上每个角点在板坐标系 T T T (target)里的 3D 坐标 p i T \mathbf{p}_i^T p i T 就精确已知(由 tagSize 和网格布局算出)。检测到它落在图像某像素 u i \mathbf{u}_i u i ,就得到一对对应关系 ( p i T , u i ) (\mathbf{p}_i^T, \mathbf{u}_i) ( p i T , u i ) ,这正是 B04 说的标定反问题的输入。一块板上有几十上百个角点,提供丰富的约束。
AprilGrid 的几何精度完全依赖 tagSize(单个 tag 的边长,单位毫米)填得准。Kalibr 的 target yaml 里要写真实打印出来的 tagSize:
target_type : 'aprilgrid'
tagCols : 6
tagRows : 6
tagSize : 0.038 # 真实边长(米),用尺子量!
tagSpacing : 0.012 # tag 间距
打印机会缩放,名义尺寸和实际尺寸常有 1-3% 偏差。tagSize 量错,整个标定的尺度 就系统性偏了——所有距离都被同一比例缩放,重投影误差(角度类)可能还正常,但重建出的 3D 尺寸全错。这是 AprilGrid 使用最常见、最隐蔽的坑。
参考
角点检测
角点检测把图像变成标定能用的观测数据 :每个角点产生一对对应关系——板上已知的 3D 点 p i T \mathbf{p}_i^T p i T 和图像里检测到的 2D 像素 u i \mathbf{u}_i u i 。AprilGrid 的检测流程比棋盘格更结构化。
AprilGrid 角点检测流程
Kalibr 检测 AprilGrid 的步骤:
解码每个 tag :对图像做边缘检测 + 四边形拟合,找到候选 tag 方块,解码其内部图案,得到 tag 的 ID。ID 决定了它在网格里的位置,从而知道它四个角在板坐标系里的 3D 坐标。
定位 tag 四角 :在每个 tag 方块上提取四个角的图像坐标,得到亚像素精度。
输出对应关系 :每个角点贡献一对 ( p i T , u i ) (\mathbf{p}_i^T, \mathbf{u}_i) ( p i T , u i ) 。
棋盘格的角点没有 ID,靠”在网格里的顺序”推断身份——所以必须整块可见,顺序才对得上。AprilTag 的 ID 让每个角点身份明确,部分可见也能用(见 B06)。
亚像素精度的意义
角点位置直接进入重投影误差(B08),它的精度是标定精度的天花板。整像素精度的角点会让重投影误差天然有 ±0.5 px 量级的抖动,无法做到亚像素标定。亚像素角点细化(subpixel refinement)把角点位置估到 0.01 px 量级,方法基于:
灰度梯度最小化 :角点的灰度分布特征(如 OpenCV 的 cornerSubPix),在一个窗口内最小化角点与相邻像素的灰度差。
空间矩 / 模板相关 :AprilTag 的 quad 角点拟合用边缘拟合 + 矩心化。
亚像素细化让重投影误差能压到 0.1-0.3 px,这是高质量标定的前提。
检测失败的常见原因
过曝 / 欠曝 :tag 的黑白对比丢失,解码失败。
运动模糊 :曝光期间板子(或相机)移动,tag 边缘糊掉,检测精度下降甚至失败。采集时板子要缓慢移动。
反光 :板子表面反光让局部过曝,覆盖 tag 编码区。
tagSize 实际与配置不符 :不影响检测,但影响 3D 坐标,导致尺度错误(B06)。
视角太斜 :tag 严重透视变形,解码鲁棒性下降。
对应关系是后续优化的输入
检测得到的 ( p i T , u i ) (\mathbf{p}_i^T, \mathbf{u}_i) ( p i T , u i ) 对集合,就是 B08 重投影误差和 B09 批量优化的观测数据。角点检测的质量(覆盖率、亚像素精度、无错检)直接决定标定上限——后面优化再好,也救不回坏的观测。
易错点
角点错检 / ID 误读 比”少检”更危险。少检只是约束变少,错检给的是错误对应关系,会把优化带偏。Kalibr 有误检剔除(用重投影残差剔除离群点),但不能完全依赖。
亚像素细化依赖角点附近灰度模型,焦外(out of focus)的角点 细化精度差。景深范围内的角点才有用。
不同帧检测到的角点 ID 必须一致。板子翻转、镜像有时会让 ID 顺序错乱,要核对 target yaml 的布局定义。
参考
重投影误差
重投影误差(reprojection error)是整个标定的核心概念。标定的目标函数、收敛判据、好坏评估,全都围绕它。
定义:预测像素与实测像素的差
取标定板上一个已知 3D 角点 p T \mathbf{p}^T p T (板坐标系下),用当前标定参数把它”投影”回图像:
世界系 → 相机系 :用外参 T C T T_{CT} T C T 变换
p C = R C T p T + t C T \mathbf{p}^C = R_{CT}\,\mathbf{p}^T + \mathbf{t}_{CT} p C = R C T p T + t C T
相机系 → 归一化像平面 :除以深度
p norm = [ p x C / p z C , p y C / p z C , 1 ] ⊤ \mathbf{p}_{\text{norm}} = [p_x^C/p_z^C,\; p_y^C/p_z^C,\; 1]^\top p norm = [ p x C / p z C , p y C / p z C , 1 ] ⊤
归一化 → 像素 :乘内参 K K K ,再加畸变 d \mathbf{d} d (先去/加畸变,见 B03)
u ^ = distort ( K p norm ; d ) \hat{\mathbf{u}} = \text{distort}(K\,\mathbf{p}_{\text{norm}};\, \mathbf{d}) u ^ = distort ( K p norm ; d )
这个 u ^ \hat{\mathbf{u}} u ^ 是预测像素 。而实际检测到的角点位置是实测像素 u \mathbf{u} u 。两者之差就是重投影误差:
e = u − u ^ \mathbf{e} = \mathbf{u} - \hat{\mathbf{u}} e = u − u ^
它的量纲是像素,是一个 2D 向量(图像 x , y x, y x , y 两个分量)。
标定目标:所有角点重投影误差平方和最小
一块板上有 N N N 个角点,每帧每相机都有这么多误差。把它们全部平方求和:
E ( K , d , T C T ) = ∑ k ∑ i ∥ u k , i − π ( K , d , T C T , k , p i T ) ∥ 2 E(K, \mathbf{d}, T_{CT}) = \sum_{k}\sum_{i} \big\| \mathbf{u}_{k,i} - \pi(K, \mathbf{d}, T_{CT,k}, \mathbf{p}_i^T)\big\|^2 E ( K , d , T C T ) = k ∑ i ∑ u k , i − π ( K , d , T C T , k , p i T ) 2
(k k k 索引帧,i i i 索引角点。)标定就是找一组参数 ( K , d , T C T , 1 , T C T , 2 , … ) (K, \mathbf{d}, T_{CT,1}, T_{CT,2}, \ldots) ( K , d , T C T , 1 , T C T , 2 , … ) 使 E E E 最小。这是非线性最小二乘问题,用 Levenberg-Marquardt / Gauss-Newton 迭代求解(见 B09)。
为什么用”重投影”而不是”反投影”
也可以反过来定义误差——把检测像素反投影成射线,看它与真实 3D 点的射线夹角。但重投影误差在像素空间度量 ,更直观(直接看偏了几像素),且权重自然(所有像素等权),所以工程上几乎都用重投影误差。
重投影误差的统计意义
如果模型正确、角点检测精度高、噪声是高斯的,重投影误差应该服从零均值高斯分布,标准差反映检测噪声水平(亚像素级)。任何系统性偏差(均值非零、有空间结构)都指向模型问题——畸变模型选错、外参错、或未建模效应。所以看重投影误差不只看数值大小,还要看分布(见 B19)。
易错点
重投影误差小 ≠ 标定对 。过拟合(参数过多、数据太少)也能让残差小。要看残差在新数据 (未参与标定的帧)上的表现,或看交叉验证。
单看平均重投影误差会被掩盖。要看 RMS、最大值、分布,少数大残差(误检角点)会拉高但被平均稀释。
重投影误差的”亚像素”标准(< 0.3 px)是经验值。鱼眼镜头边缘、低分辨率相机可能到 0.5-1 px 也算可接受,要看具体应用。
参考
批量优化
标定要调的未知数非常多——每个相机的内参和畸变(4 + N 个参数)、每一帧每个相机相对板的位姿(每帧 6 个自由度)、相机之间的相对外参(每对 6 个)。Kalibr 把它们全部放进同一个最小二乘问题 一起迭代求解,这就是批量优化(batch optimization)。
待估参数的总规模
设 C C C 个相机、F F F 帧、每相机内参 4 + 畸变 D D D 个参数:
内参:C × ( 4 + D ) C \times (4 + D) C × ( 4 + D )
每帧每相机的板位姿 T C T T_{CT} T C T :F × C × 6 F \times C \times 6 F × C × 6 (实际标定板位姿是全局共享的,但每相机观测一组,等价于每帧一个板位姿 6 自由度)
相机间外参 T C i C i + 1 T_{C_i C_{i+1}} T C i C i + 1 :( C − 1 ) × 6 (C-1) \times 6 ( C − 1 ) × 6
几百帧、几个相机时,未知数轻易上千。这正是”批量”的含义——不是分步标,而是把所有未知数当成一个大向量一起优化。
目标函数:所有重投影误差的平方和
min Θ ∑ c ∑ f ∑ i ∥ u c , f , i − π ( K c , d c , T C c T f , p i T ) ∥ Σ c , f , i 2 \min_{\Theta} \;\sum_{c}\sum_{f}\sum_{i} \big\| \mathbf{u}_{c,f,i} - \pi(K_c, \mathbf{d}_c, T_{C_c T_f}, \mathbf{p}_i^T)\big\|^2_{\Sigma_{c,f,i}} Θ min c ∑ f ∑ i ∑ u c , f , i − π ( K c , d c , T C c T f , p i T ) Σ c , f , i 2
其中 Θ \Theta Θ 是所有未知数的集合(内参、每帧板位姿、相机间外参),Σ \Sigma Σ 是协方差权重(通常各向同性像素噪声,退化为等权)。这是非线性最小二乘,用 Levenberg-Marquardt 迭代:
Θ k + 1 = Θ k + Δ Θ , ( J ⊤ Σ − 1 J + λ I ) Δ Θ = − J ⊤ Σ − 1 r \Theta_{k+1} = \Theta_k + \Delta\Theta, \qquad
(J^\top \Sigma^{-1} J + \lambda I)\,\Delta\Theta = -J^\top \Sigma^{-1}\,\mathbf{r} Θ k + 1 = Θ k + ΔΘ , ( J ⊤ Σ − 1 J + λ I ) ΔΘ = − J ⊤ Σ − 1 r
J J J 是残差 r \mathbf{r} r 对参数的雅可比,λ \lambda λ 是阻尼因子。每步迭代解一个线性方程组,更新参数,重投影误差下降,直到收敛。
为什么联合优化比分步标好
分步标(先各相机单独标内参和外参,再串相机间外参)有两个问题:
误差累积 :单相机标定的外参误差会传到相机间外参。
约束浪费 :某些参数(如相机间外参)只在联合优化里才被所有观测同时约束。
联合优化让所有参数相互校准:一个相机的内参不仅被自己的观测约束,还通过相机间外参被其他相机的观测间接约束,反之亦然。结果更准、更一致。这是 SLAM 后端、Bundle Adjustment 的共同思想。
稀疏性:为什么批量优化算得动
上千参数的最小二乘看似计算量大,但雅可比 J J J 是稀疏 的:每个重投影残差只依赖一个相机的内参、一帧的板位姿、以及相邻相机的外参,对其他参数导数为零。利用稀疏结构(如 Schur 补消元先解位姿、再解内参),批量优化能在合理时间收敛。Kalibr 用 Google 的 Ceres Solver(g2o 类似)做这件事。
Kalibr 的实际流程
初始化 :先用单相机标定(DLT/PnP)给每个相机的内参和外参一个初值。
联合优化 :所有相机、所有帧、相机间外参一起迭代,最小化总重投影误差。
收敛 :重投影误差下降到稳定(亚像素),输出 camchain.yaml。
易错点
初值重要 。非线性优化有局部极小,初值太烂会陷进去。Kalibr 的初始化(先单相机标定)就是为了给一个好的起点。
畸变模型选错会让优化收敛到一个把畸变塞进其他参数的”最优”解,残差压不下去(见 B11)。
数据覆盖不足(角度/距离单一)会让某些参数欠约束,优化数值上收敛但参数不可信。
参考
多相机外参怎么求
多相机标定里,相机之间的相对外参 T C i C i + 1 T_{C_i C_{i+1}} T C i C i + 1 是 BEV 拼接、多传感器融合的真正产物。它的求解依赖一个关键条件:相邻相机必须同时看到同一块板 (共视)。
串联求外参的原理
设某一时刻标定板在世界(板)系 T T T 里位姿固定。cam0 看到板,得到 T C 0 T T_{C_0 T} T C 0 T ;同一时刻 cam1 也看到板,得到 T C 1 T T_{C_1 T} T C 1 T 。把这两个变换写出:
p C 0 = T C 0 T p T , p C 1 = T C 1 T p T \mathbf{p}^{C_0} = T_{C_0 T}\,\mathbf{p}^T, \qquad
\mathbf{p}^{C_1} = T_{C_1 T}\,\mathbf{p}^T p C 0 = T C 0 T p T , p C 1 = T C 1 T p T
消去板坐标系 p T \mathbf{p}^T p T ,得到两相机之间的关系:
T C 1 C 0 = T C 1 T T T C 0 = T C 1 T T C 0 T − 1 T_{C_1 C_0} = T_{C_1 T}\,T_{T C_0} = T_{C_1 T}\,T_{C_0 T}^{-1} T C 1 C 0 = T C 1 T T T C 0 = T C 1 T T C 0 T − 1
板的位姿在串联中被消掉了 ,剩下的就是两相机之间恒定的相对外参 T C 1 C 0 T_{C_1 C_0} T C 1 C 0 (与板当前在哪无关,因为两相机刚性连在一起)。这是多相机外参标定的核心数学。
为什么需要重叠视野
上述推导的前提是同一时刻两个相机都观测到板,即共视 (common view)。没有重叠视野,两个相机永远看不到同一块板同时出现,无法建立 T C i T T_{C_i T} T C i T 和 T C j T T_{C_j T} T C j T 在同一时刻的关联,外参无法串联。
所以多相机布局设计时,相邻相机要有足够重叠区——哪怕只够一块 AprilGrid 同时进两个画面(AprilGrid 部分可见也能用,见 B06)。
多帧加权:减小单帧噪声
单帧算出的 T C 1 C 0 T_{C_1 C_0} T C 1 C 0 受板位姿估计噪声影响。用多帧(板在共视区移动多个位置),每帧算一个 T C 1 C 0 ( k ) T_{C_1 C_0}^{(k)} T C 1 C 0 ( k ) ,理论上都应相等(相机刚性连接),实际因噪声略有差异。批量优化(B09)把它们当未知数一起优化,用所有帧的约束联合求一个最优 T C 1 C 0 T_{C_1 C_0} T C 1 C 0 ,比单帧平均更准。
相机链与基准相机
多相机系统通常指定一个相机为基准(cam0),其余相机的位姿都相对它表达:T C 0 C 1 , T C 0 C 2 , … T_{C_0 C_1}, T_{C_0 C_2}, \ldots T C 0 C 1 , T C 0 C 2 , … 。Kalibr 输出的 camchain.yaml 里 T_cn_cnm1 字段就是”第 n n n 个相机相对第 n − 1 n-1 n − 1 个相机”的外参(链式),需要时可乘起来得到相对 cam0 的外参:
T C 0 C 2 = T C 0 C 1 T C 1 C 2 T_{C_0 C_2} = T_{C_0 C_1}\,T_{C_1 C_2} T C 0 C 2 = T C 0 C 1 T C 1 C 2
易错点
共视不足是外参标不出的头号原因 。相邻相机视野完全不重叠时,再好的优化也解不出 T C i C i + 1 T_{C_i C_{i+1}} T C i C i + 1 ,只能靠机械测量(精度差)或外部参照。多相机系统设计阶段就要规划重叠区。
链式串联会累积误差 。T C 0 C 2 = T C 0 C 1 T C 1 C 2 T_{C_0 C_2} = T_{C_0 C_1}T_{C_1 C_2} T C 0 C 2 = T C 0 C 1 T C 1 C 2 把两段误差相乘,远端相机的累积误差更大。布局上让关键相机(如前视)直接做基准,减少它到链端的跳数。
外参的平移部分靠视差约束(板在不同距离),旋转部分靠板的不同朝向。共视但姿态单一,旋转外参仍可能欠约束。
参考
选对畸变模型
畸变模型描述真实镜头偏离针孔模型的程度。模型选错,再好的优化也压不下重投影误差——尤其在画面边缘。Kalibr 支持多种模型,按镜头视场角(FOV)和畸变类型选用。
几种畸变模型的适用范围
Kalibr 模型 全称 适用镜头 畸变建模能力 pinhole-radtan针孔 + 径向/切向 普通前视、窄角(FOV < ~70°) 桶形/枕形 + 轻度切向 pinhole-equi针孔 + 等距 鱼眼、环视(FOV ~90–140°) 大畸变,等距投影 omni / Kannala-Brandt全向 超广角鱼眼(FOV > 150°) 中心 + 全向畸变 dsDouble Sphere 大 FOV(~180–190°) 双球面模型 eucmExtended Unified Camera Model 大 FOV 解析可逆,适合 GPU
径向+切向模型(radtan):标准窄角
Brown-Conrady 模型,OpenCV 默认。径向畸变(k 1 , k 2 , k 3 k_1, k_2, k_3 k 1 , k 2 , k 3 )让直线变弯(桶形/枕形),切向畸变(p 1 , p 2 p_1, p_2 p 1 , p 2 )建模镜头与传感器不平行:
x dist = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) y dist = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y \begin{aligned}
x_{\text{dist}} &= x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + 2 p_1 x y + p_2(r^2 + 2x^2) \\
y_{\text{dist}} &= y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6) + p_1(r^2 + 2y^2) + 2 p_2 x y
\end{aligned} x dist y dist = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + 2 p 1 x y + p 2 ( r 2 + 2 x 2 ) = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) + p 1 ( r 2 + 2 y 2 ) + 2 p 2 x y
其中 ( x , y ) (x, y) ( x , y ) 是归一化坐标,r 2 = x 2 + y 2 r^2 = x^2 + y^2 r 2 = x 2 + y 2 。它在窄角镜头上很准,但鱼眼镜头边缘 r r r 很大,多项式拟合需要更高阶项甚至发散 ——这时要换模型。
等距模型(equi):鱼眼首选
Kannala-Brandt 等距投影模型,用畸变半径的奇数次多项式直接建模入射角与像半径的关系:
r ( θ ) = k 1 θ + k 2 θ 3 + k 3 θ 5 + k 4 θ 7 r(\theta) = k_1\theta + k_2\theta^3 + k_3\theta^5 + k_4\theta^7 r ( θ ) = k 1 θ + k 2 θ 3 + k 3 θ 5 + k 4 θ 7
θ \theta θ 是入射光线与光轴的夹角,r r r 是像点到主点的距离。它不假设透视投影(不像 radtan 先透视再加畸变),所以对大入射角(鱼眼)建模更稳。Kalibr 里 pinhole-equi 就是先针孔再套等距畸变,是鱼眼/环视相机的标准选择。
Double Sphere / EUCM:超大 FOV
视场接近或超过 180° 时,equi 也吃力。Double Sphere 模型假设光线依次经过两个球面折射,参数少(4 个)且解析可逆,适合 VSAG、自动驾驶环视。EUCM(Extended Unified)类似,且前向投影和反投影都有闭式解,适合 GPU 实时。
怎么选:看边缘重投影误差
选择标准很实际:用哪个模型能让画面边缘的重投影误差也压到亚像素,就用哪个。 具体经验:
普通工业相机、前视窄角:pinhole-radtan。
鱼眼、环视、广角(FOV > 90°):pinhole-equi。
超广角(FOV 接近 180°):ds 或 eucm。
模型选错的症状很统一:重投影误差在画面中心小、边缘大 ,怎么优化都压不下边缘。看到这个模式就该换畸变模型。
易错点
鱼眼硬套 radtan 是最常见的翻车。边缘误差爆炸,且畸变系数被估得不合理(极大补偿值)。
模型不是越复杂越好。鱼眼镜头数据如果只覆盖中心区域,用 equi 反而欠拟合。模型选择要和实际覆盖的 FOV 匹配。
models 参数是每个相机一个 ,多个相机可以各用不同模型(如前视 radtan、环视 equi)。命令行 --models 的顺序要和 --topics 一一对应。
参考
判断标定好坏
标定跑完不等于标定对。要靠一组互相印证的指标验收,单独看任何一个都可能误判。
指标一:重投影误差散点图
Kalibr 报告 PDF 里有关键的散点图——每个角点的重投影误差向量画在图像坐标系里。看它要同时看两点:
大小 :点离原点的距离就是误差像素数。好的标定点又小又密,集中在原点附近。一般 RMS < 0.3 px 算优秀 ,< 0.5 px 合格。
结构 :点应是各向同性、随机分布,像噪声。如果点有明显方向性(系统性偏向某方向)或空间结构(边缘环状、中心偏置),说明模型或数据有问题。
有结构的残差比”数值偏大”更能说明问题:
残差图案 可能原因 边缘系统性大、中心小 畸变模型选错(鱼眼用 radtan)或畸变参数不足 整体偏向某方向 主点 c x , c y c_x, c_y c x , c y 偏,或外参平移有偏置 某些帧突然变大 那些帧运动模糊或角点误检 呈放射状对称 径向畸变阶数不够
指标二:误差分布的随机性
把重投影误差按时间、按图像位置、按板姿态分组建图。无偏估计的残差应该是零均值高斯噪声。如果有非零均值、自相关、或与某个变量(如距图像中心的距离)相关,就是模型不完备。这一步比单看 RMS 更严格。
指标三:参数的物理合理性
解出来的参数本身要合理:
焦距 f x , f y f_x, f_y f x , f y 接近镜头标称值(按像元换算后),且 f x ≈ f y f_x \approx f_y f x ≈ f y (除非已知非正方形像元)。
主点 ( c x , c y ) (c_x, c_y) ( c x , c y ) 接近图像中心(偏离几个像素正常,偏离几十像素可疑)。
畸变系数符号、量级符合镜头类型(鱼眼畸变大且通常负径向)。
相机间外参 T C i C i + 1 T_{C_i C_{i+1}} T C i C i + 1 的平移接近物理安装距离,旋转符合安装朝向。
参数反常(如焦距差几倍、畸变系数极大)往往是优化跑飞或初值烂的信号,即便残差小也不可信。
指标四:多次标定的一致性
同一组数据用不同初值、或不同子集标定多次,结果应一致。退化数据(覆盖不足)下,多次标定结果差异大——这是参数不可观的直接证据。一致性比单次残差更能反映可靠性。
指标五:实际应用的端到端验证
最可信的验收:把标定结果用于真实下游任务(BEV 拼接、测距、3D 重建),看输出是否正确。比如把一个已知尺寸的物体放在已知距离,用多相机重建其大小和位置。端到端验证通过,标定才算真正可用。
验收清单
指标 通过标准 重投影误差 RMS < 0.3 px 优秀,< 0.5 px 合格 残差随机性 无系统性结构、无偏置 参数合理性 符合镜头/安装的物理预期 多次一致性 不同初值/子集结果稳定 端到端验证 下游任务输出正确
任何一项异常都不要凑合——重采数据、换模型、查共视,直到所有指标都过。
易错点
只看 RMS 会被掩盖 。少数大残差被多数小残差平均。要看散点图、最大值、直方图。
过拟合让残差虚低 。畸变模型参数过多 + 数据少,能拟合出极低残差但参数荒谬。交叉验证(用未参与标定的帧测残差)能暴露。
模型选错的残差结构最容易被误读为”数据质量问题”。先核对畸变模型是否匹配镜头,再看数据。
参考