双目相机、SLAM、极线几何与相机标定总结

双目相机、SLAM、极线几何与相机标定总结

双目相机、SLAM、极线几何与相机标定总结

目录

  1. 为什么 SLAM 常把图像变成灰度图
  2. 双目深度中的 d、f、B、Z
  3. 什么是极平面、极线和图像平面
  4. 什么是去畸变、双目矫正和极线对齐
  5. ORB-SLAM3 双目特征匹配的大致流程
  6. 为什么右目畸变错误会影响 SLAM 和手部轨迹
  7. 矫正后没有黑边是否代表操作错误
  8. 焦距到底决定什么
  9. 为什么焦距更大时深度分辨能力更好
  10. 标定误差、重投影误差与极线误差
  11. 如何在真实场景验证极线误差
  12. 标定阶段与运行阶段的正确参数组合
  13. AprilTag 检测与 solvePnP 的关系
  14. 最常见的错误与检查清单

1. 为什么 SLAM 常把图像变成灰度图

传统几何 SLAM 主要关心的是角点、边缘、局部纹理、明暗变化、同一特征在前后帧的位置变化,以及同一特征在左右目图像中的位置差。

这些信息通常用单通道灰度图就能表达,因此很多 SLAM 系统会把彩色图:

B、G、R 三个通道

转换成灰度图:

Gray 一个通道

OpenCV 中常见写法:

gray=cv2.cvtColor(color_image,cv2.COLOR_BGR2GRAY)

灰度图并不是只有纯黑和纯白,而是:

  • 0:黑色
  • 255:白色
  • 中间数值:不同程度的灰色

常见灰度转换近似为:

[
Gray=0.299R+0.587G+0.114B
]

灰度化的主要优点:

  • 数据量约降为彩色图的三分之一
  • 特征提取和匹配更快
  • 内存占用更小
  • 左右相机色彩差异影响更小
  • 传统 ORB、FAST 等特征本身主要依赖亮度结构

颜色并不是完全没用。语义 SLAM、目标检测、手部识别、彩色指套识别等任务仍可使用彩色图。

推荐并行流程:

灰度左右图 → 双目 SLAM → 相机轨迹、地图点、深度 彩色左目图 → 手部、AprilTag、物体检测

2. 双目深度中的 d、f、B、Z

双目矫正后,某个空间点在左右图中的位置分别为:

[
p_L=(u_L,v_L)
]

[
p_R=(u_R,v_R)
]

理想情况下:

[
v_L\approx v_R
]

视差定义为:

[
d=u_L-u_R
]

双目深度公式:

[
Z=\frac{fB}{d}
]

符号含义常见单位
(d)左右图中同一真实点的水平像素差px
(f)矫正图像对应的像素焦距px
(B)左右相机光心之间的物理距离,即基线m、mm
(Z)该点沿相机前方方向的深度m、mm

2.1 视差 d 的直观含义

例如同一个桌角:

左图横坐标:uL = 450 右图横坐标:uR = 370.4

则:

[
d=450-370.4=79.6\text{ px}
]

意思是:同一个真实桌角,在左右两张矫正后的图像中,水平方向错开了 79.6 个像素。

它不是左右相机的真实距离。

2.2 基线 B 的含义

例如:

[
B=0.1\text{ m}=100\text{ mm}
]

它是左右相机光心之间的真实物理距离,可以通过机械尺寸或双目标定外参获得。

因此:

  • (d) 是图像中的像素错位
  • (B) 是现实世界中的相机间距

二者不是同一个量,但存在关系:

[
d=\frac{fB}{Z}
]

2.3 近大远小

在焦距和基线固定时:

[
d\text{ 大}\Rightarrow Z\text{ 小}
]

[
d\text{ 小}\Rightarrow Z\text{ 大}
]

通俗地说:近处物体在左右图中“跳得多”,远处物体“跳得少”。


3. 什么是极平面、极线和图像平面

3.1 左右图像平面

图像平面是相机模型中的几何平面。

通俗理解:

  • 左相机有一张“成像画布”
  • 右相机有一张“成像画布”

空间中的一个点会分别投影到两张画布上。

实际数字图像是该连续平面被像素化后的结果:

三维空间 ↓ 投影 连续图像平面 ↓ 采样 数字图像中的像素坐标 (u, v)

工程上可以近似理解为:

  • 左图像平面对应左目图像
  • 右图像平面对应右目图像

3.2 极平面

设:

  • 左相机光心为 (O_L)
  • 右相机光心为 (O_R)
  • 空间点为 (P)

三点:

[
O_L,\ O_R,\ P
]

确定一个平面,这个平面叫做极平面。

3.3 极线

极平面分别与左右图像平面相交,产生两条直线:

  • 左极线
  • 右极线

极线的含义是:已知左图中的一个点后,它在右图中的对应点理论上只能出现在对应极线上,而不是出现在右图任意位置。

未经双目矫正时,极线可能是斜线;经过双目矫正后,极线通常变成水平线。


4. 什么是去畸变、双目矫正和极线对齐

这三个概念要分开。

4.1 去畸变 Undistortion

单个镜头会产生径向畸变、切向畸变等。

去畸变使用该相机的:

  • 内参 (K)
  • 畸变参数 (D)

把弯曲的直线和错误的像素位置修正到理想针孔相机模型中。

左右相机各有自己的:

[
K_L,D_L
]

[
K_R,D_R
]

4.2 双目矫正 Stereo Rectification

双目矫正还要使用左右相机之间的外参:

[
R,T
]

它把左右相机虚拟地旋转到一个更方便匹配的姿态,使得:

  • 两个图像平面共面
  • 两个水平轴平行
  • 同一个空间点在左右图中的纵坐标接近
  • 极线变成水平线

OpenCV 常见流程:

R1,R2,P1,P2,Q,roi1,roi2=cv2.stereoRectify(K1,D1,K2,D2,image_size,R,T)

然后生成映射表:

map1_x,map1_y=cv2.initUndistortRectifyMap(K1,D1,R1,P1,image_size,cv2.CV_32FC1)map2_x,map2_y=cv2.initUndistortRectifyMap(K2,D2,R2,P2,image_size,cv2.CV_32FC1)

执行重映射:

left_rect=cv2.remap(left_raw,map1_x,map1_y,cv2.INTER_LINEAR)right_rect=cv2.remap(right_raw,map2_x,map2_y,cv2.INTER_LINEAR)

4.3 极线对齐

双目矫正后的理想条件:

[
v_L^{rect}\approx v_R^{rect}
]

于是左右匹配只需沿水平方向寻找:

二维搜索:上、下、左、右都找 变成 一维搜索:只在同一水平行附近左右找

这就是极线对齐带来的主要价值。


5. ORB-SLAM3 双目特征匹配的大致流程

双目 SLAM 通常不是只检测左目,也不是简单在右图中寻找“相同亮度的像素”。

更准确的流程是:

左图提取 ORB 特征点和描述子 右图提取 ORB 特征点和描述子 ↓ 以左目特征为主建立匹配 ↓ 在右图相同水平极线附近筛选候选 ↓ 比较 ORB 描述子 ↓ 局部灰度块进一步细化 ↓ 得到亚像素右目坐标 ↓ 计算视差和深度

5.1 为什么不是“相同亮度”

单个像素亮度相同,并不代表是同一个空间点。

ORB 描述子比较的是特征点周围的局部明暗结构。

例如:

左目描述子:101101001011... 右目候选A:101001111011... 右目候选B:101101001001...

通过汉明距离判断谁更相似。

通常还会限制:

  • 极线位置
  • 合理视差范围
  • 特征尺度
  • 描述子距离
  • 局部图块差异

5.2 为什么常常只显示左目特征

很多系统以左目为参考相机。

最后保存的信息可能是:

左目像素坐标 右目匹配坐标 视差 深度

因此查看器只显示左目,不代表右目没有参与计算。


6. 为什么右目畸变错误会影响 SLAM 和手部轨迹

误差链路可以写成:

右目畸变参数错误 → 右图去畸变位置错误 → 双目矫正后无法正确极线对齐 → 左右特征匹配减少或错配 → 视差错误 → 三角化深度和地图点错误 → ORB-SLAM3 相机位姿优化受影响 → SLAM 世界坐标系中的手部轨迹继承相机位姿误差

例如本来应该:

左图点:(450, 320) 右图点:(370, 320)

右目畸变错误后可能变成:

右图错误位置:(373, 326)

此时:

  • 水平位置错误,导致视差错误
  • 垂直位置错误,导致极线误差

水平视差:

[
d=450-373=77
]

纵向极线误差:

[
e_{\text{epi}}=|320-326|=6\text{ px}
]

如果 SLAM 输出的相机世界位姿为:

[
T_{\text{world}\leftarrow\text{camera}}
]

手部相机坐标为:

[
P_{\text{camera}}
]

则:

[
P_{\text{world}}

T_{\text{world}\leftarrow\text{camera}}
P_{\text{camera}}
]

相机位姿错了,手部世界坐标也会继承该误差。


7. 矫正后没有黑边是否代表操作错误

不一定。

去畸变或双目矫正后,理论上可能出现黑边,因为输出画布中的某些位置在原图中没有对应像素。

但如果使用了以下方式,图像可以没有黑边:

  • 放大
  • 自动裁剪
  • 有效 ROI
  • alpha=0
  • 公共有效区域
  • 查看器只显示有效部分

OpenCV 中:

new_K,roi=cv2.getOptimalNewCameraMatrix(K,D,image_size,alpha,image_size)

常见含义:

alpha效果
0尽量没有黑边,但会裁剪或放大
1尽量保留完整视野,但可能有黑边
0~1折中

双目矫正中也有类似的alpha参数。

7.1 裁剪是否破坏几何真值

正确理解:

裁剪会损失边缘视野,但不必然破坏保留区域的几何正确性。

只要后续参数同步更新:

  • 新焦距正确
  • 新主点正确
  • 新投影矩阵正确
  • 左右坐标系一致

极线仍可对齐。

7.2 真正危险的情况

图像已经经过:

  • 去畸变
  • 双目矫正
  • 裁剪
  • 缩放

却仍然使用原始:

[
K,D
]

这会造成参数与图像坐标不一致。

例如左边裁掉 50 像素:

[
c_x’=c_x-50
]

如果又缩放 0.8 倍:

[
f_x’=0.8f_x
]

[
f_y’=0.8f_y
]

[
c_x’=0.8(c_x-50)
]

[
c_y’=0.8(c_y-y_{\text{crop}})
]


8. 焦距到底决定什么

相机投影公式:

[
u=f_x\frac{X}{Z}+c_x
]

[
v=f_y\frac{Y}{Z}+c_y
]

焦距决定:

  • 空间角度对应多少像素
  • 目标在图像中占多大
  • 相机视场角有多宽
  • 同样空间运动产生多大像素位移
  • 双目视差有多明显

8.1 焦距与视场角

近似关系:

[
\mathrm{FOV}

2\arctan\left(\frac{\text{传感器尺寸}}{2f}\right)
]

因此:

  • 焦距大:视野窄,目标更大
  • 焦距小:视野宽,目标更小

8.2 物理焦距与像素焦距

物理焦距:

2.8 mm 4 mm 8 mm

像素焦距:

fx = 800 px fy = 798 px

二者关系大致为:

[
f_x=
\frac{f_{\text{mm}}}
{\text{单像素物理宽度}_{\text{mm}}}
]

SLAM、双目深度和solvePnP通常使用像素焦距。


9. 为什么焦距更大时深度分辨能力更好

物体真实深度不会因为焦距改变而改变。

“深度分辨能力更好”是指:同样的真实深度变化,在图像中会造成更明显的视差变化,因此更容易区分相近的两个深度。

双目关系:

[
d=\frac{fB}{Z}
]

在 (B) 和 (Z) 固定时:

[
f\text{ 越大}\Rightarrow d\text{ 越大}
]

9.1 数值例子

设:

[
B=0.1\text{ m}
]

[
Z=2\text{ m}
]

焦距 400 px

[
d=\frac{400\times0.1}{2}=20\text{ px}
]

若视差误差为 1 px:

[
Z_{19}=\frac{400\times0.1}{19}\approx2.105\text{ m}
]

[
Z_{21}=\frac{400\times0.1}{21}\approx1.905\text{ m}
]

深度误差约 10 cm 量级。

焦距 800 px

[
d=\frac{800\times0.1}{2}=40\text{ px}
]

若视差误差同样为 1 px:

[
Z_{39}=\frac{800\times0.1}{39}\approx2.051\text{ m}
]

[
Z_{41}=\frac{800\times0.1}{41}\approx1.951\text{ m}
]

深度误差约 5 cm 量级。

近似误差传播:

[
|\Delta Z|
\approx
\frac{Z^2}{fB}|\Delta d|
]

因此:

[
f\text{ 越大}\Rightarrow |\Delta Z|\text{ 越小}
]

前提是视差匹配误差 (\Delta d) 基本相同。

注意:单纯把数字图像resize放大,并不会产生新的真实光学信息,也不会真正提升深度精度。


10. 标定误差、重投影误差与极线误差

10.1 标定中的重投影误差

标定时使用的是原始带畸变图像。

流程:

原始畸变图像 → 检测棋盘格、ChArUco 等角点 → 得到观测像素位置 → 拟合 K、D、每张图的 R、t → 把已知三维角点重新投回原始图像 → 比较预测点和检测点

检测到的实际图像角点:

[
p_{\text{obs}}=(u_{\text{obs}},v_{\text{obs}})
]

标定模型预测位置:

[
p_{\text{proj}}=(u_{\text{proj}},v_{\text{proj}})
]

单点重投影误差:

[
e=
\sqrt{
(u_{\text{obs}}-u_{\text{proj}})^2+
(v_{\text{obs}}-v_{\text{proj}})^2
}
]

例如:

[
p_{\text{obs}}=(500.3,320.7)
]

[
p_{\text{proj}}=(499.8,321.1)
]

则:

[
e\approx0.64\text{ px}
]

总 RMS 重投影误差:

[
\mathrm{RMS}

\sqrt{
\frac{1}{N}
\sum_{i=1}{N}e_i2
}
]

它表示标定模型与标定图中检测结果的一致程度。

它不直接等于真实三维测量误差,也不等于双目极线误差。

10.2 极线误差

双目矫正后,对应点应该满足:

[
v_L^{rect}\approx v_R^{rect}
]

每个点的垂直极线误差可定义为:

[
e_{\text{epi},i}

|v_{L,i}{rect}-v_{R,i}{rect}|
]

例如:

左图角点:(450.3, 320.2) 右图角点:(372.8, 320.7)

则:

[
e_{\text{epi}}=|320.2-320.7|=0.5\text{ px}
]

横坐标差 (u_L-u_R) 是视差,纵坐标差 (v_L-v_R) 才是矫正后的极线误差。


11. 如何在真实场景验证极线误差

应该使用没有参加标定的独立数据。

推荐:

  • 30~100 对同步左右图
  • 棋盘格
  • ChArUco
  • 多 AprilTag 板
  • 覆盖中央、四角、近中远距离和不同倾角

完整流程:

独立左右同步图 → 使用标定参数做双目矫正 → 在左右矫正图中检测相同编号角点 → 计算每个点的纵坐标差 → 统计 mean、median、RMS、P95、max → 按图像位置画误差分布

11.1 计算代码

from__future__importannotationsimportnumpyasnpdefcompute_rectified_epipolar_error(points_left:np.ndarray,points_right:np.ndarray,)->dict[str,float]:""" 输入: points_left: (N, 2),左矫正图中的 (u, v) points_right: (N, 2),右矫正图中的对应 (u, v) 输出: 极线误差统计,单位为像素 """points_left=np.asarray(points_left,dtype=np.float64)points_right=np.asarray(points_right,dtype=np.float64)ifpoints_left.shape!=points_right.shape:raiseValueError("左右对应点数量或形状不一致")ifpoints_left.ndim!=2orpoints_left.shape[1]!=2:raiseValueError("输入必须为形状 (N, 2)")iflen(points_left)==0:raiseValueError("没有有效对应点")errors=np.abs(points_left[:,1]-points_right[:,1])return{"count":float(len(errors)),"mean_px":float(np.mean(errors)),"median_px":float(np.median(errors)),"rms_px":float(np.sqrt(np.mean(errors**2))),"p95_px":float(np.percentile(errors,95)),"max_px":float(np.max(errors)),}

11.2 推荐统计指标

至少报告:

  • 有效对应点数量
  • 平均误差 mean
  • 中位数 median
  • RMS
  • P95
  • 最大误差 max
  • 图像中央和四角分区误差
  • 静态状态
  • 动态状态

经验参考:

平均垂直极线误差大致评价
< 0.3 px很好
0.3~0.5 px较好
0.5~1.0 px通常可用
1~2 px建议排查
> 2 px双目匹配通常明显受影响

这只是经验值,最终要结合:

  • 图像分辨率
  • 焦距
  • 基线
  • 工作距离
  • SLAM 匹配阈值
  • 所需深度精度

11.3 静态与动态分开测试

静态测试主要反映:

  • 内参
  • 畸变参数
  • 双目外参
  • 矫正算法

动态测试还会包含:

  • 左右时间不同步
  • 滚动快门
  • 运动模糊
  • 支架形变
  • 自动曝光差异
  • 自动对焦变化

如果静态很好、动态变差,问题可能主要在同步和成像时序,而不是标定本身。


12. 标定阶段与运行阶段的正确参数组合

这是整个流程最容易混淆的地方。

12.1 标定阶段

第一次标定时还没有可靠的 (K,D),因此不能先正确去畸变再标定。

正确流程:

原始畸变图 → 直接检测标定板角点 → 拟合 K、D、R、t → 用 K、D 把三维角点投影回原始畸变图 → 计算重投影误差

检测棋盘格角点本身通常不需要输入相机参数:

ret,corners=cv2.findChessboardCorners(gray,pattern_size)

12.2 运行阶段

运行时可以选两种正确做法。

方案 A:在原始图上检测

原始畸变图 → 检测 Tag 四角 → 使用原始 K、D 做 solvePnP
success,rvec,tvec=cv2.solvePnP(object_points,image_points_raw,K_original,D_original,)

方案 B:先去畸变或双目矫正,再检测

原始图 → 使用 K、D 去畸变或双目矫正 → 在处理后的图上检测 Tag → 使用处理后图像对应的新内参 → 畸变设为零
success,rvec,tvec=cv2.solvePnP(object_points,image_points_rectified,K_rectified,None,)

核心对应关系:

图像坐标类型应使用的相机参数
原始畸变图原始 (K) + 原始 (D)
单目去畸变图(K_{\text{new}}) + 零畸变
双目矫正左图从 (P_1) 提取的新内参 + 零畸变
双目矫正右图从 (P_2) 提取的新内参 + 零畸变

双目矫正左图的新内参通常可写为:

[
K_{\text{rect,left}}=P_1[:,0:3]
]


13. AprilTag 检测与 solvePnP 的关系

要区分两个步骤。

13.1 Tag 检测

输入图像,输出:

  • Tag ID
  • 四个角点像素坐标

这一阶段主要依赖:

  • 黑白边缘
  • 方形轮廓
  • 编码内容
  • 局部纹理

通常不需要输入 (K,D)。

13.2 姿态估计

根据:

  • Tag 四个图像角点
  • Tag 真实边长
  • 对应图像坐标系的相机内参
  • 图像是否还包含畸变

计算:

[
rvec,\ tvec
]

如果 Tag 物体坐标以中心为原点,tvec表示 Tag 中心在相机坐标系中的位置。

OpenCVsolvePnP的关系:

[
X_c=RX_o+t
]

其中:

  • (X_o):Tag 坐标系中的点
  • (X_c):相机坐标系中的点
  • (R,t):Tag 到相机的位姿

如果物体点单位是 mm,则tvec也是 mm。

13.3 关键原则

角点检测: “角点在图像哪里?” 姿态估计: “Tag 离相机多远、朝向如何?”

检测阶段通常不依赖相机参数;姿态估计阶段必须使用和图像坐标一致的相机参数。


14. 最常见的错误与检查清单

14.1 常见错误

错误 1:矫正图继续使用原始 K、D

图像已去畸变 + solvePnP 仍使用原始 K、D

会产生二次畸变补偿和坐标不一致。

错误 2:图像裁剪后主点未修改

裁掉左侧 (x_0) 像素后:

[
c_x’=c_x-x_0
]

裁掉顶部 (y_0) 像素后:

[
c_y’=c_y-y_0
]

错误 3:图像缩放后焦距和主点未缩放

缩放比例为 (s_x,s_y):

[
f_x’=s_xf_x
]

[
f_y’=s_yf_y
]

[
c_x’=s_xc_x
]

[
c_y’=s_yc_y
]

错误 4:左右使用不同 ROI,但未同步坐标

左右图必须在统一的矫正坐标定义下做视差比较。

错误 5:只看重投影误差,不测真实极线误差

重投影误差小,不保证双目外参和真实使用状态一定好。

错误 6:只看平均误差

必须同时看:

  • median
  • RMS
  • P95
  • max
  • 图像位置分布

错误 7:只做静态测试

头部设备还应测试:

  • 转头
  • 快速平移
  • 动态光照
  • 运动模糊
  • 时间同步

14.2 推荐检查清单

相机状态

  • 分辨率与标定一致
  • 裁剪方式与标定一致
  • 自动对焦关闭或固定
  • 曝光和增益尽量固定
  • 电子防抖关闭
  • 数字变焦关闭
  • 左右时间同步正常
  • 机械支架无松动

标定参数

  • 左相机 (K_1,D_1) 对应左图
  • 右相机 (K_2,D_2) 对应右图
  • 双目外参 (R,T) 方向正确
  • 基线单位正确
  • 图像尺寸与标定尺寸一致
  • 矫正后使用 (P_1,P_2) 对应的新内参
  • 矫正图畸变设置为零

极线验证

  • 使用独立验证图像
  • 左右角点编号严格对应
  • 计算 (|v_L-v_R|)
  • 统计 mean、median、RMS、P95、max
  • 检查图像中央与四角
  • 静态和动态分别测试
  • 可视化水平线和误差热图

SLAM 验证

  • 左右匹配数量是否正常
  • 深度分布是否合理
  • 近距离与远距离深度是否稳定
  • 转头时轨迹是否异常抖动
  • 纯旋转时是否出现明显虚假平移
  • 地图点是否在边缘区域异常
  • 手部世界坐标是否继承头部轨迹漂移

最终核心总结

  1. 灰度化是为了减少计算量并保留几何特征。
  2. 视差 (d) 是同一真实点在左右图中的水平像素差。
  3. 基线 (B) 是左右相机光心之间的真实物理距离。
  4. 焦距 (f) 决定空间角度被映射成多少像素。
  5. 极线限定了对应点在另一张图中的可能位置。
  6. 双目矫正使对应极线水平化,满足 (v_L\approx v_R)。
  7. 右目畸变错误会破坏极线对齐、视差和三角化深度。
  8. 矫正后无黑边不一定是错误,可能只是裁剪或放大了有效区域。
  9. 裁剪可以保留几何正确性,但必须同步更新内参和主点。
  10. 焦距增大不会改变真实深度,但会让视差变化更明显,从而降低相同像素误差带来的深度误差。
  11. 重投影误差是模型预测像素和标定图中检测像素之间的距离。
  12. 极线误差是矫正后左右对应点的纵坐标差。
  13. 标定时在原始畸变图上检测角点。
  14. 运行时原始图使用原始 (K,D),矫正图使用新内参和零畸变。
  15. Tag 检测和 Tag 姿态估计是两个不同步骤。

最重要的参数一致性原则:

原始图坐标 ↔ 原始 K、D 去畸变图坐标 ↔ 新 K、零畸变 双目矫正图坐标 ↔ P1/P2 对应的新内参、零畸变