Part III · 第 11 章 多视图几何 · 11.6Part III · Ch. 11 Multi-View Geometry · 11.6

光束法平差 (Bundle Adjustment)Bundle Adjustment

三角化在相机已知时求点,PnP 在点已知时求相机,可现实里两者都不知道。光束法平差把所有相机位姿和所有三维点放在一起,最小化全部观测的重投影误差:这是多视图几何的“金标准”,也是每个 SfM 和视觉 SLAM 系统精度的来源。它就是 6.9 节非线性最小二乘最大的一个应用:真正有意思的是它的稀疏结构、7 维规范自由度和对外点的处理。 Triangulation finds the points when the cameras are known; PnP finds a camera when the points are known; reality gives you neither. Bundle adjustment puts every camera pose and every 3D point into one problem and minimizes the total reprojection error: the "gold standard" of multi-view geometry and the source of the accuracy of every SfM and visual SLAM system. It is the largest application of the nonlinear least squares of Lesson 6.9, and what makes it interesting is its sparse structure, its 7-dimensional gauge freedom and its treatment of outliers.

约 35 分钟~35 min 前置:6.9 非线性最小二乘 · 6.10 流形优化 · 11.3 三角化 · 11.4 PnP · 11.5 RANSAC 与鲁棒损失Prereqs: 6.9 Nonlinear least squares · 6.10 Manifold optimization · 11.3 Triangulation · 11.4 PnP · 11.5 RANSAC & robust losses 5 个交互5 interactives

    1为什么机器人需要它Why robots need this

    用一台相机在房间里走一圈,再用 11.5 节的特征匹配找出各帧之间的对应点。现在你想同时知道两件事:相机每一帧在哪里(位姿),以及这些特征点在三维里的哪里(结构)。已有的工具各解决一半:三角化在相机位姿已知时算出点,PnP在点已知时算出相机。真实的建图两者都未知,只能自举:先用对极几何(11.1)和三角化得到一点粗糙的结构,再用 PnP 把新的一帧接进来,再三角化新的点……每一步都带着一点误差,误差会一层层叠上去,地图越走越歪。

    光束法平差(Bundle Adjustment,BA)的想法是:别再“固定一边、求另一边”。把所有相机位姿和所有三维点都当未知数,让每个三维点投影到每个看到它的相机上的像素,同时尽量贴近实际测得的像素,即最小化总的重投影误差。名字来自“光束”:从每个三维点射向各相机中心的光线束,要一起调整到同时穿过各自观测到的像素。在高斯像素噪声下(4.6 节),它就是最大似然估计,所以被称为多视图几何的“金标准”。

    COLMAP 和 ORB-SLAM 的地图之所以准,靠的就是它;NeRF 与 3D 高斯泼溅训练前需要的相机位姿,也几乎都来自它(12.4 节)。从算法上看,BA 没有新东西:它就是 6.9 节的非线性最小二乘,残差是重投影误差,用 Levenberg-Marquardt 求解。本节不重讲那套理论,只讲 BA 特有的几件事:残差与雅可比具体长什么样,为什么有 7 个多余的自由度,怎样利用稀疏结构(Schur 补)把规模推到百万个点,怎样抵御误匹配,以及解有多可靠。

    Walk a camera around a room and use the feature matching of Lesson 11.5 to find corresponding points across frames. Now you want two things at once: where the camera was at every frame (the poses) and where those feature points are in 3D (the structure). The tools you have each solve half: triangulation computes points when the poses are known, PnP computes a camera when the points are known. Real mapping knows neither, so it bootstraps: epipolar geometry (11.1) and triangulation give a rough structure, PnP registers the next frame, triangulation adds new points, and so on. Every step carries a little error, the errors stack up, and the map bends.

    The idea of bundle adjustment (BA) is to stop "fix one side, solve the other". Treat all camera poses and all 3D points as unknowns, and ask that the projection of every point into every camera that saw it lands close to the pixel actually measured: minimize the total reprojection error. The name comes from the "bundles" of light rays from each 3D point to the camera centres, which are adjusted until they all pass through the pixels they were observed at. Under Gaussian pixel noise (Lesson 4.6) this is the maximum-likelihood estimate, hence the title "gold standard" of multi-view geometry.

    It is why the maps of COLMAP and ORB-SLAM are accurate, and the camera poses needed before NeRF or 3D Gaussian splatting training mostly come out of it too (Lesson 12.4). As an algorithm BA has nothing new: it is the nonlinear least squares of Lesson 6.9 with the reprojection error as residual, solved by Levenberg-Marquardt. This lesson does not repeat that theory. It covers what is specific to BA: what the residual and Jacobian actually look like, why there are 7 superfluous degrees of freedom, how sparsity (the Schur complement) pushes the size to millions of points, how to survive mismatches, and how far to trust the answer.

    2直觉:每条观测是一根橡皮筋Intuition: every observation is a rubber band

    想象下面的场景:5 台相机排成一道弧,看着 30 个空间点。每台相机看到的点会在它的图像上形成一个像素,这是观测(紫色)。如果你对相机位姿和点位置的猜测是错的,把猜测的点投影回这台相机,就落在另一个像素上,这是重投影(琥珀色)。两者之间的向量就是残差\(\hl{res}{\mathbf r_{ij}}\)(红线),可以想成一根橡皮筋;总代价 \(F\) 是所有橡皮筋的弹性势能。

    未知数是 5 台相机的位姿(每台 6 个自由度)和 30 个点(每个 3 个坐标),共 120 个;观测有 111 条,每条给出两个方程(\(u\) 和 \(v\)),共 222 个。方程比未知数多,所以通常不存在一组解让所有橡皮筋同时松弛,但存在一组最好的折中:BA 就是去找它。下图让你自己把猜测弄坏,看橡皮筋怎样绷紧。

    Picture this: 5 cameras on an arc looking at 30 points in space. Each camera sees every point (or most of them) as a pixel in its image: the observation (purple). If your guess for the camera poses and the point positions is wrong, projecting the guessed point back into that camera lands on a different pixel: the reprojection (amber). The vector between them is the residual \(\hl{res}{\mathbf r_{ij}}\) (red line); think of it as a rubber band. The total cost \(F\) is the elastic energy of all the bands.

    The unknowns are the poses of 5 cameras (6 degrees of freedom each) and 30 points (3 coordinates each): 120 numbers. There are 111 observations, each giving two equations (\(u\) and \(v\)): 222 in total. More equations than unknowns, so in general no setting relaxes every band at once, but there is a best compromise, and BA is the search for it. Below you can ruin the guess yourself and watch the bands stretch.

    FIG 1代价:把猜测弄坏,橡皮筋就绷紧The cost: ruin the guess and the bands stretch
    上:三维视图(z 向上),紫色是真值(相机锥体和点),琥珀色是当前猜测,青线连接同一个点的猜测与真值。下:相机 1、3、5 的图像(像素坐标,v 本来向下,这里画成 −v 使图像是正的):紫点为观测,琥珀点为重投影,红线是残差。最右是代价 \(\log F\) 沿“真值 → 猜测”这条直线的切片,橙点是当前位置,紫点是真值(噪声使它的代价不为零)。滑块越右,猜测越差;真值附近的代价曲线像一个碗,远处很快变陡。观测带 σ = 0.5 px 的噪声。 Top: the 3D view (z up); purple is the truth (camera frusta and points), amber is the current guess, cyan lines join each guessed point to its true position. Bottom: the images of cameras 1, 3 and 5 (pixel coordinates; v really points down, drawn as −v so the picture is upright): purple dots are observations, amber dots reprojections, red lines the residuals. Rightmost is a slice of the cost \(\log F\) along the line "truth → guess": the amber dot is the current guess, the purple dot the truth (its cost is not zero because of the noise). Further right on the slider means a worse guess; near the truth the cost looks like a bowl and steepens quickly away from it. Observations carry σ = 0.5 px noise.
    试一试Try this
    • 把扰动水平拉到 1 以上:残差是几十个像素,\(F\) 比真值处大几个数量级。Push the perturbation to 1 or more: residuals become tens of pixels and \(F\) is orders of magnitude above its value at the truth.
    • 分别选“只扰动相机”和“只扰动点”(水平 ≥ 0.5):只动相机时,同一台相机里所有残差朝着相近方向;只动点时,残差各指各的。Choose "cameras only" and "points only" (level ≥ 0.5): with only cameras moved, all residuals inside one camera point roughly the same way; with only points moved, each points its own way.
    核心洞见Key insight

    BA 的残差只是“预测像素减观测像素”。一个未知数(比如某个点)只出现在看到它的那几条橡皮筋里,一台相机只出现在它自己看到的点的橡皮筋里。这个“谁牵着谁”的结构,决定了 BA 的一切:矩阵为什么是稀疏的、能被 Schur 补加速、规范自由度为什么存在。

    The BA residual is just "predicted pixel minus observed pixel". An unknown, say one point, appears only in the few bands of the cameras that saw it; a camera appears only in the bands of the points it saw. This "who pulls on whom" structure decides everything about BA: why the matrices are sparse, why the Schur complement speeds them up, why a gauge freedom exists.

    3动手:真的跑一遍 LMPlay: run Levenberg-Marquardt for real

    下面的图运行的就是真正的算法,没有预先算好的动画。每一步:(1) 在当前估计处线性化所有残差,得到每条观测的 \(2\times6\) 相机雅可比块和 \(2\times3\) 点雅可比块(解析公式见第 5 节,页面里还用有限差分验证过);(2) 解阻尼正规方程 \((J^\top J+\lambda D)\boldsymbol\delta=-J^\top\mathbf r\);(3) 相机位姿用 \(T\leftarrow\Exp(\boldsymbol\delta_i)T\) 更新(6.10 节:在 \(\SE\) 上用指数映射而不是加法),点直接相加;(4) 代价下降就接受并把 \(\lambda\) 缩小为三分之一,否则拒绝并把 \(\lambda\) 乘 4(6.6、6.9 节的 LM)。

    有两处新东西。第一,规范:只有图像像素没有绝对坐标,把整个场景刚体移动、再整体放大缩小,所有重投影都不变,所以有 \(6+1=7\) 个方向代价不变。要得到唯一解必须“固定规范”:这里冻结相机 1 的位姿(6 个量),并固定相机 1、2 之间的基线长度(1 个量:实现上是冻结基线方向上的一个平移分量,每步后再把基线长度归一化,这是对代价严格不变的相似变换)。切到“不固定”看会发生什么,并打开 \(J^\top J\) 的谱。第二,线性求解器:“稠密”把全部 \(6c+3p\) 个未知数一起解,“Schur 补”先消去点(第 4 节),两者给出同一个步长。

    The figure below runs the genuine algorithm, no pre-computed animation. Each step: (1) linearize all residuals at the current estimate, giving for every observation a \(2\times6\) camera Jacobian block and a \(2\times3\) point block (analytic formulas in Section 5, also checked against finite differences in the page); (2) solve the damped normal equations \((J^\top J+\lambda D)\boldsymbol\delta=-J^\top\mathbf r\); (3) update the camera poses by \(T\leftarrow\Exp(\boldsymbol\delta_i)T\) (Lesson 6.10: the exponential map on \(\SE\) instead of addition) and add the step to the points; (4) if the cost drops, accept and divide \(\lambda\) by 3, otherwise reject and multiply \(\lambda\) by 4 (the LM of Lessons 6.6 and 6.9).

    Two new things. First, the gauge: images have pixels but no absolute coordinates, so moving the whole scene rigidly and then scaling it up or down changes no reprojection. There are \(6+1=7\) directions along which the cost is flat. A unique answer requires "fixing the gauge": here we freeze the pose of camera 1 (6 numbers) and hold the baseline length between cameras 1 and 2 (1 number: in the code, one translation component along the baseline is frozen and the baseline is renormalized after each step, a similarity transform that leaves the cost exactly unchanged). Switch to "free" to see what happens, and open the spectrum of \(J^\top J\). Second, the linear solver: "dense" solves all \(6c+3p\) unknowns at once, "Schur" eliminates the points first (Section 4); both give the same step.

    FIG 2光束法平差:LM 逐步收敛Bundle adjustment: LM step by step
    左:琥珀色是当前估计,紫色是真值。因为规范不唯一,估计要先做最佳相似变换(Umeyama 对齐)才能和真值叠在一起比较;“结构误差”也是对齐之后的点位置均方根误差(单位 m,场景约 3 m 宽)。右:相机 3 的图像与残差,下面是代价的对数曲线(绿虚线是噪声水平 \(\tfrac12\sigma^2(2m-n)\),\(n\) 为扣除 7 个规范方向后的未知数个数)或 \(J^\top J\) 的特征值。读数里“规范漂移”给出相机 1 相对初始位置移动了多少、基线长度变了几倍;“稠密 / Schur”给出两种求解器的乘加次数、耗时和两者步长的最大差。 Left: amber is the current estimate, purple the truth. Because the gauge is not unique, the estimate is first brought onto the truth by the best similarity transform (Umeyama alignment) before the two are compared; the "structure error" is the RMS point error after that alignment (in metres; the scene is about 3 m wide). Right: the image of camera 3 with its residuals, then the log cost curve (the green dashed line is the noise floor \(\tfrac12\sigma^2(2m-n)\) with \(n\) the unknown count after removing the 7 gauge directions) or the eigenvalues of \(J^\top J\). In the readout "gauge drift" tells how far camera 1 moved from its initial position and how many times the baseline length changed; "dense / Schur" gives multiply-add counts, run time and the largest difference between the two solvers' steps.
    • 保持默认设置按“运行”:不到 10 步收敛,RMS 降到噪声水平(约 0.5 px)。Keep the defaults and press "Run": it converges in fewer than 10 steps and the RMS settles at the noise level (about 0.5 px).
    • 选“Gauss-Newton (λ = 0)”,规范固定,运行:几步(5 到 7 步)就收敛,因为初值已在盆地里(有噪声时是快速的线性收敛,只有噪声为 0 时才是二次收敛)。Select "Gauss-Newton (λ = 0)" with the gauge fixed and run: it converges in a handful of steps (5 to 7) because the start is inside the basin (fast linear convergence with noise; quadratic only when the noise is 0).
    • 把规范改成“不固定”,仍用 Gauss-Newton,按“单步”:\(J^\top J\) 奇异,Cholesky 直接失败。Set the gauge to "free", keep Gauss-Newton and press "Step": \(J^\top J\) is singular and Cholesky fails at once.
    • 规范“不固定”并用 LM 运行到收敛:代价照样降到最低,但相机 1 跑掉了,基线长度也变了:解沿 7 维“平坦谷”随便落在了哪里。With the gauge "free" run LM to convergence: the cost still reaches its minimum, but camera 1 has wandered off and the baseline has changed: the solution landed somewhere along a 7-dimensional flat valley.
    • 规范“不固定”,图选“JᵀJ 的谱”:恰有 7 个特征值是数值零(图里红色);切到“固定 7 维”,它们消失。With the gauge "free" choose the plot "spectrum of JᵀJ": exactly 7 eigenvalues are numerically zero (red); switch to "fixed (7)" and they vanish.
    • 线性求解选“Schur 补”并按“单步”:读数里 max|δ稠密 − δSchur| 在 1e-9 以下,Schur 的乘加次数少了约一个数量级。Choose "Schur" as the linear solver and press "Step": max|δ_dense − δ_Schur| in the readout is below 1e-9 and Schur needs about ten times fewer multiply-adds.
    有意思的细节Things worth noticing

    (1) 有噪声时,收敛处的代价(约 14)低于真值处的代价(约 26):优化器拟合了噪声,最优解并不等于真值,这是“估计”而不是“还原”。 (2) 噪声调为 0 时,代价在 7 步内降到 \(10^{-13}\) 以下(程序在此停下),结构误差只有 \(10^{-10}\) m 量级,说明解析雅可比和更新规则确实是对的。 (3) “固定”规范只是在 7 维平坦谷里钉一个点,所以它对尺度的选择完全是任意的:对真值的尺度读数偏离 1 多少,就是你初始基线定得多不准。

    (1) With noise, the cost at convergence (about 14) is lower than at the truth (about 26): the optimizer fits the noise, the optimum is not the truth; this is "estimation", not "recovery". (2) With the noise set to 0 the cost falls below \(10^{-13}\) within 7 steps (where the program stops) and the structure error is only of order \(10^{-10}\) m, which shows that the analytic Jacobians and the update rule are right. (3) "Fixing" the gauge only pins one point in the 7-dimensional flat valley, so its choice of scale is arbitrary: how far the scale-vs-truth readout is from 1 is how inaccurate your initial baseline was.

    4另外两个视角:稀疏结构与外点Two more views: sparse structure and outliers

    稀疏:箭头形的 H 与 Schur 补Sparsity: the arrowhead H and the Schur complement

    FIG 2 的 120 个未知数还好,真实的 SfM 有几千台相机、几百万个点,未知数上千万,稠密矩阵连存都存不下。但 BA 的雅可比极其稀疏:每个观测(\(J\) 的一个 \(2\times n\) 行块)只牵涉一台相机的 6 列和一个点的 3 列,其余全是零。于是正规方程矩阵 \(H=J^\top J\) 有固定的“箭头”形状:

    The 120 unknowns of FIG 2 are fine, but a real SfM job has thousands of cameras and millions of points, tens of millions of unknowns, too many even to store as a dense matrix. Yet the BA Jacobian is extremely sparse: each observation (a \(2\times n\) row block of \(J\)) touches only the 6 columns of one camera and the 3 columns of one point; everything else is zero. So the normal-equation matrix \(H=J^\top J\) has a fixed "arrowhead" shape:

    $$ H=\begin{bmatrix} B & E\\ E^\top & C\end{bmatrix},\quad B=\operatorname{blkdiag}_i(B_i)\ (6\times6),\quad C=\operatorname{blkdiag}_j(C_j)\ (3\times3),\quad E_{ij}\neq 0\iff \text{camera } i \text{ sees point } j $$

    注意 \(C\) 是块对角的:两个不同的点从不出现在同一条观测里,彼此不直接耦合。所以消去全部点只需要对 \(p\) 个 \(3\times3\) 的小矩阵求逆(每个约 30 次运算),再解一个只含相机的 \(6c\times6c\) 小方程组:这就是 Schur 补(第 5 节推导)。下面把它的规模摆出来比较。矩阵按“相机在前、点在后”排列,虚线框是 Schur 补 \(\hl{schur}{S}\) 所在的相机块;相机很多时,一个点被多台相机共同看到,才会让这些相机之间的块 \(S_{ik}\) 非零,轨迹越长,\(S\) 越接近带状。

    Notice that \(C\) is block diagonal: two different points never appear in the same observation, so they are not directly coupled. Eliminating all the points therefore needs only the inversion of \(p\) small \(3\times3\) matrices (about 30 operations each) followed by one small camera-only system of size \(6c\times6c\): this is the Schur complement (derived in Section 5). Below, compare the sizes. The matrix is ordered "cameras first, points last"; the dashed box marks where the Schur complement \(\hl{schur}{S}\) lives. Cameras with a shared point are the only ones whose block \(S_{ik}\) is nonzero, so on a long trajectory \(S\) is close to banded.

    FIG 3稀疏结构:J 的非零图案、箭头形 H 与 Schur 补的代价Sparse structure: the pattern of J, the arrowhead H, and the cost of the Schur complement
    左:雅可比 \(J\) 的非零图案,行是观测(每个代表 2 行像素),列先是 \(c\) 个 6 列的相机块,再是 \(p\) 个 3 列的点块。每一行恰好 1 + 1 个块非零。右:\(H=J^\top J\) 的块图案,蓝是相机块 \(B\)(不同相机因共同看到某个点而相连),琥珀是 \(E\) 和 \(E^\top\),绿是点的对角块 \(C\)。每个点被相邻的 \(m\) 台相机看到(沿轨迹的带状可见性,和真实的视觉里程计一样)。运算量按“稠密 Cholesky \(n^3/3\)”对“消点 + 解 \(6c\) 维约化系统”计;按钮会在你的浏览器里实际各解一次(随机雅可比块,只有结构真实),并比较两个解。 Left: the nonzero pattern of the Jacobian \(J\); rows are observations (each stands for 2 pixel rows), columns are first \(c\) camera blocks of 6 columns, then \(p\) point blocks of 3. Each row has exactly 1 + 1 nonzero blocks. Right: the block pattern of \(H=J^\top J\); blue is the camera block \(B\) (two cameras are linked when they share a point), amber is \(E\) and \(E^\top\), green the diagonal point blocks \(C\). Each point is seen by \(m\) neighbouring cameras (banded visibility along the trajectory, as in real visual odometry). Operation counts compare "dense Cholesky \(n^3/3\)" with "eliminate the points + solve the \(6c\)-dimensional reduced system"; the button actually solves each once in your browser (random Jacobian blocks, only the structure is real) and compares the two answers.
    • 把相机数拉到 20 以上、点数拉到 200 以上:Schur 比稠密求解省一百倍以上的运算,J 里非零的比例只有 1% 左右。Push cameras to 20 or more and points to 200 or more: Schur saves more than a hundred times the work and only about 1% of J is nonzero.

    外点与鲁棒损失Outliers and robust losses

    特征匹配(11.5 节)总会混进错误对应。BA 里一个错误观测意味着一根拉向错误方向的橡皮筋,平方损失里它的“拉力”与偏离成正比,几个这样的观测就能把整张地图拽歪。解决办法是把 \(\tfrac12e^2\) 换成增长更慢的 \(\hl{rho}{\rho(e)}\),并用 IRLS 求解:每条观测按当前残差得到权重 \(w=\rho'(e)/e\),外点的残差大,权重就小(6.9、11.5 节)。下图在 111 条观测里随机替换若干条为错误像素(偏离 35 到 90 px),同样的初值、同样的算法,只换损失函数。

    Feature matching (Lesson 11.5) always lets some wrong correspondences through. In BA a wrong observation is a rubber band pulling the wrong way; under the squared loss its pull grows with the error, and a few of them are enough to bend the whole map. The cure is to replace \(\tfrac12e^2\) with a slower-growing \(\hl{rho}{\rho(e)}\) and solve by IRLS: each observation gets a weight \(w=\rho'(e)/e\) from its current residual, so a large residual means a small weight (Lessons 6.9 and 11.5). Below, a few of the 111 observations are replaced at random by wrong pixels (displaced by 35 to 90 px); same start, same algorithm, only the loss changes.

    FIG 4外点:平方损失被拽歪,鲁棒核把它们降权Outliers: the squared loss gets dragged, robust kernels down-weight them
    每次改动都从同一个初值重新运行 LM 到收敛(用 Schur 求解)。左上:估计(琥珀)对齐到真值(紫)后的样子。中:相机 3 的图像,点的大小和颜色是 IRLS 权重(大而绿 = 权重 1,小而红 = 权重 < 0.5),✕ 标出真正的外点,灰线是残差。右:三种损失 \(\rho(e)\):平方增长到画面之外,Huber 线性增长,Cauchy 几乎不再增长;底部的短竖线是当前每条观测的残差(红色是外点)。尺度 \(c\) 以像素计。 Every change re-runs LM to convergence from the same start (using the Schur solver). Top: the estimate (amber) after alignment to the truth (purple). Middle: the image of camera 3; dot size and colour are the IRLS weight (big and green = weight 1, small and red = weight < 0.5), ✕ marks the true outliers, grey lines are the residuals. Right: the three losses \(\rho(e)\): the squared one leaves the picture, Huber grows linearly, Cauchy barely grows; the short ticks at the bottom are the current residual of every observation (red for outliers). The scale \(c\) is in pixels.
    • 先选“平方损失”,把外点加到 6 个以上,记下结构误差;再换成 Cauchy(c = 2):误差降到原来的四分之一以下,被降权的几乎都是真外点。Start with the "squared" loss and at least 6 outliers and note the structure error; then switch to Cauchy (c = 2): the error drops below a quarter, and almost everything that got down-weighted is a true outlier.
    一个想法,几种看法One idea, several views

    优化:一个有 \(6c+3p\) 个变量的稀疏非线性最小二乘(6.9 节)。 概率:高斯像素噪声下的最大似然估计,逆 Hessian 给出协方差(4.6 节;FIG 5)。 因子图:变量是相机和点,每条观测是连接一个相机和一个点的因子;Schur 补就是按“先消点”的顺序做消元(6.9 节的 FIG 2)。 交替:它同时包含“已知相机求点”(三角化)和“已知点求相机”(PnP),只是同时、且彼此纠正。

    Optimization: a sparse nonlinear least-squares problem in \(6c+3p\) variables (Lesson 6.9). Probability: the maximum-likelihood estimate under Gaussian pixel noise, with the inverse Hessian as covariance (Lesson 4.6; FIG 5). Factor graph: variables are cameras and points, each observation is a factor linking one camera and one point; the Schur complement is elimination in the order "points first" (FIG 2 of Lesson 6.9). Alternation: it contains both "cameras known, find points" (triangulation) and "points known, find cameras" (PnP), only done simultaneously and with each correcting the other.

    5再看数学The math

    提示:把鼠标悬停在公式中带底色的符号上,图中对应的元素会被高亮(残差、相机、点、Schur 补、损失曲线)。Tip: hover the highlighted symbols in the formulas; matching elements in the figures light up (residuals, cameras, points, the Schur complement, the loss curve).

    代价与参数化The cost and the parametrization

    相机 \(i\) 的外参是 \({}^{c_i}T_w=(R_i,\mathbf t_i)\in\SE\),把世界点变到相机系:\(\mathbf x_c=R_i X_j+\mathbf t_i\)(注意这是相机位姿 \({}^{w}T_{c}\) 的逆;画图时我们再取逆得到相机在世界里的位置 \(C_i=-R_i^\top\mathbf t_i\))。内参 \(f_x,f_y,c_x,c_y\) 已知并共享。点 \(X_j\in\R^3\),观测 \(\mathbf x_{ij}\in\R^2\)(像素),\(\mathcal O\) 是所有观测的集合。重投影残差与总代价是

    The extrinsics of camera \(i\) are \({}^{c_i}T_w=(R_i,\mathbf t_i)\in\SE\), taking a world point to the camera frame: \(\mathbf x_c=R_i X_j+\mathbf t_i\) (note this is the inverse of the camera pose \({}^{w}T_{c}\); for drawing we invert it to get the camera position \(C_i=-R_i^\top\mathbf t_i\) in the world). The intrinsics \(f_x,f_y,c_x,c_y\) are known and shared. Points are \(X_j\in\R^3\), observations \(\mathbf x_{ij}\in\R^2\) (pixels), and \(\mathcal O\) is the set of all observations. The reprojection residual and the total cost are

    $$ \hl{res}{\mathbf r_{ij}}=\pi(R_iX_j+\mathbf t_i)-\mathbf x_{ij},\quad \pi(x,y,z)=\begin{bmatrix} f_x\,x/z+c_x\\ f_y\,y/z+c_y\end{bmatrix},\qquad F=\sum_{(i,j)\in\mathcal O}\hl{rho}{\rho}\big(\|\mathbf r_{ij}\|_{\Sigma}\big) $$

    \(\|\mathbf r\|_\Sigma=\sqrt{\mathbf r^\top\Sigma^{-1}\mathbf r}\) 是按像素噪声协方差“白化”后的长度(本页取 \(\Sigma=I\),即 1 px 为单位);\(\rho(e)=\tfrac12e^2\) 就是高斯噪声的最大似然(4.6 节),换成 Huber 或 Cauchy 就是鲁棒版。未知数共 \(6c+3p\) 个,方程 \(2|\mathcal O|\) 个;要有解需要 \(2|\mathcal O|\ge 6c+3p-7\),其中 7 是下面的规范自由度。

    \(\|\mathbf r\|_\Sigma=\sqrt{\mathbf r^\top\Sigma^{-1}\mathbf r}\) is the length "whitened" by the pixel-noise covariance (this page uses \(\Sigma=I\), i.e. 1 px units); \(\rho(e)=\tfrac12e^2\) is the Gaussian maximum likelihood (Lesson 4.6), and Huber or Cauchy give the robust versions. There are \(6c+3p\) unknowns and \(2|\mathcal O|\) equations; a solution needs \(2|\mathcal O|\ge 6c+3p-7\), where 7 is the gauge freedom below.

    雅可比:两块,各自很小The Jacobian: two small blocks

    相机用左扰动 \(T\leftarrow\Exp(\boldsymbol\delta)T\),\(\boldsymbol\delta=(\boldsymbol\omega,\mathbf v)\)(角速度在前,和全课程一致)。5.7 节告诉我们小扰动下 \(\Exp(\boldsymbol\delta)\mathbf x_c\approx\mathbf x_c+\boldsymbol\omega\times\mathbf x_c+\mathbf v=\mathbf x_c-[\mathbf x_c]_\times\boldsymbol\omega+\mathbf v\),所以相机坐标系下的点对扰动的导数是 \([-[\mathbf x_c]_\times\ \ I]\)(\(3\times6\))。点的扰动直接加在 \(X_j\) 上,\(\mathbf x_c\) 对它的导数是 \(R_i\)。再乘上投影的导数(令 \(\mathbf x_c=(x,y,z)\)):

    Cameras use the left perturbation \(T\leftarrow\Exp(\boldsymbol\delta)T\) with \(\boldsymbol\delta=(\boldsymbol\omega,\mathbf v)\) (angular part first, as everywhere in the course). Lesson 5.7 says that for small perturbations \(\Exp(\boldsymbol\delta)\mathbf x_c\approx\mathbf x_c+\boldsymbol\omega\times\mathbf x_c+\mathbf v=\mathbf x_c-[\mathbf x_c]_\times\boldsymbol\omega+\mathbf v\), so the derivative of the camera-frame point with respect to the perturbation is \([-[\mathbf x_c]_\times\ \ I]\) (\(3\times6\)). A point perturbation is added straight onto \(X_j\), and \(\mathbf x_c\) changes by \(R_i\) per unit. Multiplying by the derivative of the projection (with \(\mathbf x_c=(x,y,z)\)):

    $$ \hl{cam}{J_c}=\frac{\partial\mathbf r_{ij}}{\partial\boldsymbol\delta_i}=\underbrace{\begin{bmatrix} f_x/z & 0 & -f_xx/z^2\\ 0 & f_y/z & -f_yy/z^2\end{bmatrix}}_{\partial\pi/\partial\mathbf x_c}\begin{bmatrix}-[\mathbf x_c]_\times & I\end{bmatrix}\in\R^{2\times6},\qquad \hl{pt}{J_p}=\frac{\partial\mathbf r_{ij}}{\partial X_j}=\frac{\partial\pi}{\partial\mathbf x_c}\,R_i\in\R^{2\times3} $$

    上面这一行是页面加载时现算的:把 \(J_c\)、\(J_p\) 和中心有限差分逐元素比较。注意 \(1/z\) 和 \(1/z^2\):深度 \(z\) 越大,同样的移动引起的像素变化越小,这就是远处的点、小视差的点不好约束的根源。

    The line above is computed when the page loads: it compares every entry of \(J_c\) and \(J_p\) with central finite differences. Note the \(1/z\) and \(1/z^2\): the larger the depth \(z\), the smaller the pixel change per unit motion, which is the root of why far points and low-parallax points are poorly constrained.

    正规方程、Schur 补Normal equations and the Schur complement

    把所有观测堆起来(IRLS 权重 \(w_k\) 放进对角矩阵 \(W\)),LM 的一步解 \((J^\top WJ+\lambda D)\boldsymbol\delta=-J^\top W\mathbf r\)。把未知数分成相机部分 \(\boldsymbol\delta_c\) 和点部分 \(\boldsymbol\delta_p\),方程变成分块形式(\(\mathbf g=J^\top W\mathbf r\) 是梯度,阻尼已并入 \(B\) 和 \(C\) 的对角):

    Stack all observations (the IRLS weights \(w_k\) go into a diagonal matrix \(W\)); one LM step solves \((J^\top WJ+\lambda D)\boldsymbol\delta=-J^\top W\mathbf r\). Split the unknowns into the camera part \(\boldsymbol\delta_c\) and the point part \(\boldsymbol\delta_p\) and the equations take block form (\(\mathbf g=J^\top W\mathbf r\) is the gradient; the damping is absorbed in the diagonals of \(B\) and \(C\)):

    $$ \begin{bmatrix}B&E\\E^\top&C\end{bmatrix}\begin{bmatrix}\boldsymbol\delta_c\\\boldsymbol\delta_p\end{bmatrix}=-\begin{bmatrix}\mathbf g_c\\\mathbf g_p\end{bmatrix}\ \Longrightarrow\ \underbrace{(B-EC^{-1}E^\top)}_{\hl{schur}{S}}\boldsymbol\delta_c=-\mathbf g_c+EC^{-1}\mathbf g_p $$ $$ \boldsymbol\delta_{p,j}=-C_j^{-1}\big(\mathbf g_{p,j}+E_j^\top\boldsymbol\delta_c\big)\quad\text{(back-substitution, one }3\times3\text{ solve per point)} $$

    推导就是对第二块行做高斯消元:由 \(E^\top\boldsymbol\delta_c+C\boldsymbol\delta_p=-\mathbf g_p\) 得 \(\boldsymbol\delta_p=-C^{-1}(\mathbf g_p+E^\top\boldsymbol\delta_c)\),代入第一块行 \(B\boldsymbol\delta_c+E\boldsymbol\delta_p=-\mathbf g_c\) 即得关于 \(\boldsymbol\delta_c\) 的方程。\(S\) 叫约化相机系统(reduced camera system),大小 \(6c\times6c\),与点数无关;它正是把所有点“边缘化”掉之后相机的信息矩阵。FIG 2 里“稠密”与“Schur”两条路给出同一步长(差 \(10^{-13}\) 量级),因为它们只是同一个线性方程的两种消元顺序。

    The derivation is Gaussian elimination on the second block row: from \(E^\top\boldsymbol\delta_c+C\boldsymbol\delta_p=-\mathbf g_p\) we get \(\boldsymbol\delta_p=-C^{-1}(\mathbf g_p+E^\top\boldsymbol\delta_c)\), and substituting into the first block row \(B\boldsymbol\delta_c+E\boldsymbol\delta_p=-\mathbf g_c\) gives the equation for \(\boldsymbol\delta_c\) alone. \(S\) is called the reduced camera system; its size \(6c\times6c\) does not depend on the number of points, and it is exactly the information matrix of the cameras after marginalizing all points out. In FIG 2 the "dense" and "Schur" routes give the same step (difference of order \(10^{-13}\)) because they are just two elimination orders of the same linear equation.

    规范自由度与可观测性Gauge freedom and observability

    取任意相似变换:旋转 \(R_g\)、平移 \(\mathbf t_g\)、尺度 \(s>0\),令 \(X_j'=sR_gX_j+\mathbf t_g\),\(R_i'=R_iR_g^\top\),\(\mathbf t_i'=s\mathbf t_i-R_iR_g^\top\mathbf t_g\)。则 \(R_i'X_j'+\mathbf t_i'=s(R_iX_j+\mathbf t_i)\),相机坐标整体放大 \(s\) 倍,而 \(\pi\) 对这样的缩放不变(\(x/z\) 不变),所以每一个重投影残差都不变,\(F\) 也不变。这个 7 参数族 \((R_g,\mathbf t_g,s)\) 是代价函数的一条 7 维“平坦谷”,\(H\) 因此有 7 维零空间,FIG 2 的谱画的就是这个。固定规范的常见做法:冻结第一台相机的位姿(6 维),再冻结一个量定尺度(这里是基线长度,也可以固定某一点的深度,或者把第二台相机平移的某一分量固定)。如果有别的信息,就能多固定几维:双目或带已知基线的系统自带尺度,只剩 6 维;加 IMU(重力方向)、GPS 或已知尺寸的物体,也能固定其中一部分。

    单目 BA 无法恢复绝对尺度,这是物理上的限制,不是算法的缺陷:把整个世界放大两倍,照片一模一样。能否恢复结构还取决于几何:每个点至少要被两台相机看到,并且视线有足够的夹角(视差);纯旋转的相机对所有点都没有视差,深度完全不可观测,\(C_j\) 奇异。

    Take any similarity transform with rotation \(R_g\), translation \(\mathbf t_g\) and scale \(s>0\), and set \(X_j'=sR_gX_j+\mathbf t_g\), \(R_i'=R_iR_g^\top\), \(\mathbf t_i'=s\mathbf t_i-R_iR_g^\top\mathbf t_g\). Then \(R_i'X_j'+\mathbf t_i'=s(R_iX_j+\mathbf t_i)\): the camera-frame coordinates are all scaled by \(s\), and \(\pi\) is invariant to such a scaling (\(x/z\) does not change), so every reprojection residual is unchanged and so is \(F\). This 7-parameter family \((R_g,\mathbf t_g,s)\) is a 7-dimensional flat valley of the cost, \(H\) therefore has a 7-dimensional null space, and the spectrum in FIG 2 shows exactly that. The usual way to fix the gauge: freeze the pose of the first camera (6 dimensions), then freeze one more quantity to set the scale (here the baseline length; one could also fix the depth of one point, or one component of the second camera's translation). Extra information fixes more: a stereo rig or any system with a known baseline brings its own scale and leaves 6; an IMU (gravity direction), GPS or an object of known size fixes part of the rest.

    Monocular BA cannot recover absolute scale; this is a physical limit, not a defect of the algorithm: scale the whole world by two and the photographs are identical. Whether the structure can be recovered also depends on geometry: each point must be seen by at least two cameras with enough angle between the rays (parallax); a purely rotating camera has no parallax on any point, depths are completely unobservable and \(C_j\) is singular.

    不确定度:逆 HessianUncertainty: the inverse Hessian

    在最优点附近,参数估计的协方差近似是 \(\operatorname{Cov}(\hat{\boldsymbol\theta})\approx\sigma^2(J^\top J)^{-1}\)(4.4、4.6 节;它也是 Cramér-Rao 下界)。只有固定了规范 \(J^\top J\) 才可逆,所以协方差的数值依赖于你选了哪种规范:“点相对于相机 1 的不确定度”和“点云去掉整体相似变换之后的不确定度”是不同的量。FIG 5 用后者:先取固定规范下的逆,再把点云的 7 个相似方向投影掉(最小迹规范)。我们用 1500 次蒙特卡洛重复核对了它:预测的点位误差方差与实测之比为 0.99。

    与位姿图的关系:消去所有点之后只剩相机,约化系统 \(S\) 就是相机位姿的信息矩阵,它在共视的相机之间有非零块。位姿图(Part IV 的 SLAM 后端)进一步用相机之间的相对位姿测量代替这些块,相当于把结构信息“边缘化后近似”掉:代价小得多,但丢掉了结构的细节,所以回环后常常再跑一次完整的 BA。

    Near the optimum the covariance of the estimate is approximately \(\operatorname{Cov}(\hat{\boldsymbol\theta})\approx\sigma^2(J^\top J)^{-1}\) (Lessons 4.4 and 4.6; it is also the Cramér-Rao bound). \(J^\top J\) is invertible only once the gauge is fixed, so the numbers depend on which gauge you picked: "uncertainty of a point relative to camera 1" and "uncertainty of the point cloud after removing the global similarity" are different quantities. FIG 5 uses the latter: invert in a fixed gauge, then project out the 7 similarity directions of the point cloud (the minimum-trace gauge). We checked it against 1500 Monte-Carlo repetitions: the ratio of measured to predicted point-error variance is 0.99.

    Relation to pose graphs: after eliminating all points only the cameras remain, and the reduced system \(S\) is the information matrix of the camera poses, with nonzero blocks between co-visible cameras. A pose graph (the SLAM back end of Part IV) goes further and replaces those blocks by relative-pose measurements between cameras, i.e. it marginalizes the structure and approximates the result: far cheaper, but the structure detail is lost, which is why a full BA is often run once more after a loop closure.

    BA 是局部方法BA is a local method

    LM 只保证找到离初值最近的局部极小。初值必须来自几何方法:11.1 的对极几何与 11.3 的三角化给出两视图的初始结构,11.4 的 PnP 把新相机接进来,11.5 的 RANSAC 先把外点挡在门外,BA 才能在“正确的盆地”里把误差压到噪声水平。

    LM only promises the local minimum closest to the start. The start must come from geometric methods: epipolar geometry (11.1) and triangulation (11.3) give the two-view initial structure, PnP (11.4) registers new cameras, RANSAC (11.5) keeps the outliers out first, and only then can BA squeeze the error down to the noise level inside the right basin.

    6机器人中的它:要多宽的基线?In robotics: how wide a baseline is enough?

    BA 给出的不只是一个解,还有这个解的可信程度。一台机器人沿墙走,每隔一段时间选一帧“关键帧”加入地图。关键帧选得太密,相邻帧之间几乎没有视差,新三角化的点深度很不准;选得稀一点,视差大了,但特征匹配会变难。下图用上面的 BA 反过来问这个问题:给定相机弧的宽度、相机数和像素噪声,每个点的位置不确定度(琥珀色椭球,3σ,已去掉整体相似变换)有多大?椭球是从 \(\sigma^2(J^\top J)^{-1}\) 里取出来的,没有跑任何随机试验。

    BA gives not only a solution but also how far to trust it. A robot walks along a wall and every so often picks a "keyframe" for the map. Pick them too densely and neighbouring frames have almost no parallax, so newly triangulated points have poor depth; pick them sparsely and parallax is big but feature matching gets harder. The figure turns the BA above around to ask this question: given the width of the camera arc, the number of cameras and the pixel noise, how large is the uncertainty of each point (amber ellipsoids, 3σ, with the global similarity removed)? The ellipsoids come from \(\sigma^2(J^\top J)^{-1}\); no random trials are run.

    FIG 5不确定度来自 Hessian:视差决定深度精度Uncertainty from the Hessian: parallax decides depth accuracy
    24 个点,所有相机都看到所有点。椭球是每个点位置的 3σ 协方差椭球(最小迹规范),为了看得见,画出时放大了 10 倍(读数是真实大小)。相机弧很窄时椭球沿视线方向被拉得很长:深度比横向差几倍;弧变宽或相机变多,椭球收缩并趋于球形。深度 / 横向读数是各点沿“平均相机到该点”方向的标准差与垂直方向标准差之比。 24 points, every camera sees every point. Each ellipsoid is the 3σ covariance ellipsoid of a point position (minimum-trace gauge), drawn 10 times enlarged so it can be seen (the readout gives the true size). With a narrow camera arc the ellipsoids are stretched along the line of sight: depth is several times worse than the lateral directions; widen the arc or add cameras and they shrink and become round. The depth / lateral readout is the ratio of a point's standard deviation along the "mean camera to point" direction to the one across it.
    • 先把弧半角压到 12° 以下:深度不确定度是横向的 2.5 倍以上;再把弧半角拉到 50° 以上:深度误差降到 4 mm 以下,椭球几乎变成球。First squeeze the arc half-angle below 12°: depth uncertainty is more than 2.5 times the lateral one; then widen it beyond 50°: the depth error drops below 4 mm and the ellipsoids become nearly round.
    真实系统里In real systems

    SfM:COLMAP、Theia、OpenMVG 增量式地注册图像(PnP)、三角化新点,并周期性运行局部与全局 BA;COLMAP 的 BA 基于 Ceres Solver,用 Schur 型求解器。 视觉 SLAM:ORB-SLAM 系列在建图线程里对共视关键帧和它们的地图点做局部 BA(g2o,LM),回环后再做全局 BA;ORB-SLAM3 加入了 IMU。三角化的点要求两条视线之间有足够的夹角,正是 FIG 5 的道理。 滑动窗口:机器人要实时,就不能让问题越来越大,所以只对最近的若干关键帧做 BA,旧状态边缘化(Schur 补)成一个先验,VINS-Mono 就是这样做的视觉惯性滑窗优化(用 Ceres 求解),OpenVINS 则走滤波(MSCKF)路线。 标定也是 BA:OpenCV 的 calibrateCamera 在标定板上最小化重投影误差,未知数是内参、畸变和每张图的板位姿(10.4 节),Kalibr 对多相机加 IMU 做同样的事。 NeRF / 3DGS:训练前的相机位姿通常来自 COLMAP(Nerfstudio 的数据处理流程调用它),BARF(Lin 等,ICCV 2021)进一步把位姿和神经辐射场一起优化。 库:g2o、Ceres Solver、GTSAM、SymForce(自动生成雅可比);Agarwal 等的 “Bundle Adjustment in the Large” 在数千台相机、百万级点上用预条件共轭梯度解 Schur 约化系统,Ceres 里对应 ITERATIVE_SCHUR。

    SfM: COLMAP, Theia and OpenMVG register images incrementally (PnP), triangulate new points and run local and global BA periodically; COLMAP's BA is built on Ceres Solver with Schur-type solvers. Visual SLAM: the ORB-SLAM family runs local BA over co-visible keyframes and their map points in the mapping thread (g2o, LM) and a global BA after a loop closure; ORB-SLAM3 adds the IMU. Triangulated points need a sufficient angle between the two rays, which is the point of FIG 5. Sliding windows: a real-time robot cannot let the problem grow forever, so BA is run on the most recent keyframes only and old states are marginalized (Schur complement) into a prior; VINS-Mono does visual-inertial sliding-window optimization this way (solved with Ceres), whereas OpenVINS takes the filtering route (MSCKF). Calibration is BA too: OpenCV's calibrateCamera minimizes the reprojection error of a calibration board over intrinsics, distortion and the board pose of every image (Lesson 10.4), and Kalibr does the same for several cameras plus an IMU. NeRF / 3DGS: the camera poses needed before training usually come from COLMAP (Nerfstudio's data-processing pipeline calls it), and BARF (Lin et al., ICCV 2021) goes further and optimizes poses jointly with the radiance field. Libraries: g2o, Ceres Solver, GTSAM, SymForce (generates Jacobians automatically); the "Bundle Adjustment in the Large" paper of Agarwal et al. solves the Schur reduced system by preconditioned conjugate gradients for thousands of cameras and millions of points, which Ceres offers as ITERATIVE_SCHUR.

    7常见误区Common pitfalls

    误区 1:没固定规范,或固定得不对Pitfall 1: not fixing the gauge, or fixing it wrongly

    FIG 2 已经演示:不固定时 \(J^\top J\) 有 7 维零空间,Gauss-Newton 的 Cholesky 直接失败;LM 的阻尼能让它“跑得通”,但解落在 7 维平坦谷里任意一点,每次运行世界坐标系和尺度都不同,没法与别的地图拼接。还有一种常见错法:只固定了相机位姿,忘了尺度,或者把位置先验(GPS、已知的标定板尺寸)写成残差却又同时冻结了相机,使得问题过约束且互相矛盾。固定几维就是几维,不要多也不要少。

    FIG 2 showed it: with the gauge free, \(J^\top J\) has a 7-dimensional null space and the Cholesky of Gauss-Newton simply fails; LM's damping lets it "run", but the solution lands at an arbitrary point of the flat valley, with a different world frame and scale on every run, so it cannot be stitched to another map. Another common mistake is to freeze the camera poses but forget the scale, or to write a position prior (GPS, a known board size) as a residual while also freezing the cameras, so the problem is over-constrained and contradicts itself. Fix as many dimensions as there are, neither more nor fewer.

    误区 2:初值太差,或以为 BA 能自己找到初值Pitfall 2: a poor start, or expecting BA to find its own

    BA 是局部优化。本页的初值是“真值加扰动”,所以连扰动水平 3 也能收敛;真实流程里初值来自对极几何、三角化、PnP,质量参差不齐,一个相机被错误地注册到镜像或错误的位置,就可能陷入离正解很远的局部极小。用鲁棒的几何方法初始化,增量式地加相机并频繁做局部 BA,是 SfM 稳健的关键。

    BA is local optimization. The starts on this page are "truth plus perturbation", which is why even perturbation level 3 converges; in a real pipeline the start comes from epipolar geometry, triangulation and PnP, and its quality varies: a camera registered to a mirror image or to a wrong position can trap BA in a local minimum far from the truth. Robust geometric initialization, adding cameras incrementally and running local BA frequently is what makes SfM stable.

    误区 3:不用鲁棒核,或把鲁棒核当万能药Pitfall 3: no robust loss, or treating it as a cure-all

    FIG 4:6 个外点就让平方损失的结构误差放大十几倍。但鲁棒核也有代价:Huber 的影响函数有界却不为零,外点多时仍会偏;Cauchy 这类“回落”核几乎能消除外点,但代价非凸,对初值更挑剔;尺度 \(c\) 取小了会把正常的大噪声也降权。更好的顺序是:先用几何验证(RANSAC、11.5 节)筛一遍,再用鲁棒 BA 兜底,最后可以去掉被降权的观测再跑一次普通 BA。

    FIG 4: six outliers inflate the structure error of the squared loss by more than ten times. But robust losses have costs: Huber's influence function is bounded but not zero, so many outliers still bias it; a redescending loss like Cauchy nearly removes them but makes the cost non-convex and the start more critical; a small scale \(c\) also down-weights perfectly normal large noise. A better order: screen with geometric verification first (RANSAC, Lesson 11.5), use robust BA as a safety net, and finally drop the down-weighted observations and run a plain BA once more.

    误区 4:小视差的点和远处的点Pitfall 4: low-parallax and far-away points

    视线夹角越小,点的深度越不确定(FIG 5),\(C_j\) 越接近奇异,\(J^\top J\) 越病态。更糟的是 BA 最小化的是像素误差,一个很远的点深度错了几十米,像素几乎不变,所以代价对它几乎没有约束。要么给每个点设最小视差、最小三角化角度的门槛,要么用逆深度参数化(\(1/z\) 对远点是良态的),要么把这些点只当方向约束使用。别拿“重投影误差很小”当作“这个点可靠”的证据。

    The smaller the angle between the rays, the more uncertain the point's depth (FIG 5), the closer \(C_j\) is to singular and the more ill-conditioned \(J^\top J\) becomes. Worse, BA minimizes pixel error: a very far point whose depth is wrong by tens of metres barely changes any pixel, so the cost constrains it hardly at all. Either set thresholds on minimum parallax and triangulation angle per point, or use an inverse-depth parametrization (\(1/z\) is well behaved for far points), or use such points only as direction constraints. Never take a small reprojection error as proof that a point is reliable.

    误区 5:阻尼太小、旋转直接加法、以为 BA 能恢复尺度Pitfall 5: too little damping, rotations updated by addition, expecting BA to recover scale

    三个小坑。(a) \(\lambda\) 太小时,病态的 \(J^\top J\) 让步长爆炸,要靠 LM 的“拒绝再增大 \(\lambda\)”兜底,不要把 \(\lambda\) 写死成 0。 (b) 旋转不能用 \(R\leftarrow R+\Delta R\) 或欧拉角直接相加:用指数映射 \(\Exp\) 更新(6.10 节),否则会出现万向节锁或不再是旋转矩阵。 (c) 单目 BA 的尺度由你固定的基线决定(FIG 2 里“对真值的尺度”读数),要得到公制尺度,需要双目基线、IMU、已知尺寸的物体或 GPS 之类的外部信息。

    Three small traps. (a) With too small a \(\lambda\), the ill-conditioned \(J^\top J\) makes steps explode; LM's "reject and increase \(\lambda\)" is the safety net, so do not hard-code \(\lambda=0\). (b) Rotations must not be updated by \(R\leftarrow R+\Delta R\) or by adding Euler angles: update with the exponential map \(\Exp\) (Lesson 6.10), or gimbal lock appears and the matrix stops being a rotation. (c) The scale of monocular BA is whatever baseline you fixed (the "scale vs truth" readout in FIG 2); metric scale needs outside information such as a stereo baseline, an IMU, an object of known size or GPS.

    8小练习Check yourself

    1. 单目 BA:4 台相机、10 个点,每个点都被 4 台相机看到。固定规范之后,有多少个独立未知数?方程有多少个?1. Monocular BA with 4 cameras and 10 points, every point seen by all 4. After fixing the gauge, how many independent unknowns are there, and how many equations?
    未知数 \(6\cdot4+3\cdot10=54\),减去 7 个规范自由度得 47。观测 \(4\times10=40\) 条,每条给出 \(u\) 和 \(v\) 两个方程,共 80 个。冗余度 \(80-47=33\) 是 BA 能把噪声平均掉的原因。如果是带已知基线的双目系统,规范只有 6 维,未知数是 48。Unknowns \(6\cdot4+3\cdot10=54\), minus 7 gauge dimensions gives 47. There are \(4\times10=40\) observations, each giving two equations (\(u\) and \(v\)): 80. The redundancy \(80-47=33\) is what lets BA average the noise out. With a stereo system of known baseline the gauge is only 6-dimensional and the unknowns number 48.
    2. 把一个已收敛的单目重建(所有点和相机中心)整体放大 2 倍,相机朝向不变。重投影代价 \(F\) 会怎样?2. A converged monocular reconstruction (all points and camera centres) is scaled up by a factor of 2, with camera orientations unchanged. What happens to the reprojection cost \(F\)?
    相机坐标系下的点 \((x,y,z)\) 全部变成 \((2x,2y,2z)\),而 \(u=f_xx/z+c_x\)、\(v=f_yy/z+c_y\) 只与比值有关,所以每个像素都不变,\(F\) 不变。这就是尺度规范自由度:单目图像本身不含绝对尺度,必须固定一个。Every camera-frame point \((x,y,z)\) becomes \((2x,2y,2z)\), and \(u=f_xx/z+c_x\), \(v=f_yy/z+c_y\) depend only on the ratios, so every pixel is unchanged and so is \(F\). This is the scale gauge freedom: monocular images contain no absolute scale, so one must be fixed.
    3. Schur 补为什么能让 BA 快几个数量级?3. Why does the Schur complement make BA orders of magnitude faster?
    Schur 补是精确的(不是近似),步长和稠密求解完全相同(FIG 2 的读数差 \(10^{-13}\))。省下的是运算量:稠密 \(\sim(6c+3p)^3/3\),而 Schur 约 \(\sim p\cdot O(m^2)+(6c)^3/3\),点数 \(p\) 只以线性出现。The Schur complement is exact, not an approximation: the step equals the dense one (the FIG 2 readout shows a difference of order \(10^{-13}\)). What it saves is work: dense \(\sim(6c+3p)^3/3\) versus Schur \(\sim p\cdot O(m^2)+(6c)^3/3\), with the number of points \(p\) entering only linearly.
    4. 一台相机只旋转、不平移,拍了两帧。把这两帧加进 BA,点的深度会怎样?4. A camera only rotates, without translating, and takes two frames. Add them to BA: what happens to the depths of the points?
    视差来自相机中心的移动,与旋转无关:纯旋转时所有点的视线都从同一个中心出发,两帧对点的深度没有任何约束(相当于一个单应,10.5 节)。FIG 5 的极限情形(弧宽趋于 0)就是这个现象:深度方向的标准差趋于无穷。Parallax comes from motion of the camera centre, not from rotation: with pure rotation all rays start at the same centre and two frames put no constraint on the depth of any point (the pair is related by a homography, Lesson 10.5). The limit of FIG 5 (arc width going to 0) is this phenomenon: the depth standard deviation grows without bound.
    5. 数据有噪声时,BA 收敛处的代价 \(F\) 与“真值处的代价”相比?5. With noisy data, how does the cost \(F\) at the BA optimum compare with the cost at the ground truth?
    BA 最小化的是带噪声的观测上的代价,真值只是一个可行点,不是该代价的最小值。有 \(n\) 个自由未知数时,最优处的期望代价约为 \(\tfrac12\sigma^2(2m-n)\),而真值处约为 \(\tfrac12\sigma^2\cdot2m\)(FIG 2 里约 14 对 26)。这个差别就是“过拟合噪声”的量,也说明为什么每条观测的残差 RMS 收敛到 σ 附近,而不是真值处的 \(\sqrt2\,\sigma\)(这里约 0.5 px 对 0.71 px)。BA minimizes the cost over the noisy observations; the truth is just one feasible point, not the minimum of that cost. With \(n\) free unknowns the expected cost at the optimum is about \(\tfrac12\sigma^2(2m-n)\) while at the truth it is about \(\tfrac12\sigma^2\cdot2m\) (about 14 versus 26 in FIG 2). The difference is exactly the amount of noise being "overfitted", and it is also why the per-observation residual RMS comes out near σ rather than the \(\sqrt2\,\sigma\) you would see at the truth (here about 0.5 px versus 0.71 px).

    9延伸阅读Further reading

    • B. Triggs, P. McLauchlan, R. Hartley & A. Fitzgibbon, Bundle Adjustment — A Modern Synthesis, Vision Algorithms: Theory and Practice (LNCS 1883), 2000 — 权威综述:参数化、规范自由度、Schur 补、鲁棒核、协方差the authoritative survey: parametrization, gauge freedom, the Schur complement, robust losses, covariance
    • R. Hartley & A. Zisserman, Multiple View Geometry in Computer Vision, 2nd ed., Cambridge University Press, 2004, Appendix 6 (iterative estimation methods) and Ch. 18 — 稀疏 LM 与 n 视图重建sparse LM and n-view reconstruction
    • S. Agarwal, N. Snavely, S. Seitz & R. Szeliski, Bundle Adjustment in the Large, ECCV 2010 — 用预条件共轭梯度求解数千相机、百万点的 BABA with thousands of cameras and millions of points via preconditioned conjugate gradients
    • F. Dellaert & M. Kaess, Factor Graphs for Robot Perception, Foundations and Trends in Robotics, 2017 — 把 BA、SLAM 看成因子图与稀疏消元BA and SLAM seen as factor graphs and sparse elimination
    • J. L. Schönberger & J.-M. Frahm, Structure-from-Motion Revisited, CVPR 2016 — COLMAP:增量式 SfM 的工程细节COLMAP: engineering details of incremental SfM
    • C. Campos, R. Elvira, J. J. Gómez Rodríguez, J. M. M. Montiel & J. D. Tardós, ORB-SLAM3, IEEE Transactions on Robotics, 2021 — 视觉与视觉惯性 SLAM 中的局部与全局 BAlocal and global BA inside visual and visual-inertial SLAM
    • C.-H. Lin, W.-C. Ma, A. Torralba & S. Lucey, BARF: Bundle-Adjusting Neural Radiance Fields, ICCV 2021 — 把 BA 的思想用于神经辐射场的位姿联合优化carries the BA idea to joint pose optimization with neural radiance fields