张正友相机标定法原理与OpenCV实践指南

📅 2026/7/24 10:06:41 👁️ 阅读次数 📝 编程学习
张正友相机标定法原理与OpenCV实践指南

1. 项目概述

相机标定是计算机视觉领域的基础性工作,就像给相机做一次"体检",通过测量和计算确定相机的"视力参数"。张正友标定法作为最经典的标定方法之一,其巧妙之处在于仅需使用一个平面棋盘格图案,通过多角度拍摄就能完成标定。这种方法不需要昂贵的标定设备,操作简单但精度可靠,特别适合实验室和工业现场应用。

在实际项目中,我们经常需要根据图像中的像素坐标反推物体的真实世界坐标。比如在自动驾驶中,需要知道前方障碍物的实际距离;在工业检测中,需要测量产品缺陷的实际尺寸。这些应用都依赖于准确的相机参数,而张正友标定法正是获取这些参数的黄金标准。

2. 核心原理解析

2.1 相机成像模型

相机的成像过程可以理解为将三维世界中的点投影到二维图像平面的过程。这个过程涉及四个坐标系:

  1. 世界坐标系(X_w, Y_w, Z_w):物体在真实世界中的位置
  2. 相机坐标系(X_c, Y_c, Z_c):以相机光心为原点的坐标系
  3. 图像坐标系(x, y):以图像中心为原点的二维坐标系
  4. 像素坐标系(u, v):以图像左上角为原点的离散坐标系

它们之间的转换关系构成了相机标定的数学基础:

[u] [f_x s u_0] [r11 r12 r13 t_x] [X_w] [v] = [0 f_y v_0] [r21 r22 r23 t_y] [Y_w] [1] [0 0 1 ] [r31 r32 r33 t_z] [Z_w] [0 0 0 1 ] [1 ]

其中内参矩阵K包含相机的固有参数:

  • f_x, f_y:x和y方向的焦距(像素单位)
  • u_0, v_0:主点坐标(图像中心)
  • s:倾斜系数(现代相机通常为0)

外参矩阵[R|t]描述相机在世界坐标系中的位置和方向:

  • R:3×3旋转矩阵
  • t:3×1平移向量

2.2 张正友标定法的创新点

传统标定方法需要精密的三维标定物,而张正友教授在1998年提出的方法有三大突破:

  1. 只需要一个平面棋盘格(如A4纸打印的棋盘)
  2. 允许棋盘在不同方向自由移动
  3. 通过多幅图像实现高精度标定

其核心思想是利用平面棋盘格的共面特性,建立简化的单应性矩阵关系,再通过非线性优化求解所有参数。这种方法大大降低了标定的门槛,使得普通实验室也能获得专业级的标定结果。

3. 实操步骤详解

3.1 准备标定板

理想的标定板应满足:

  • 棋盘格黑白分明,对比度高
  • 角点清晰易检测
  • 尺寸精确已知(如每个方格2cm×2cm)

提示:使用激光打印机在哑光铜版纸上打印棋盘格,可以避免反光干扰。建议棋盘格不少于8×8个内角点。

3.2 采集标定图像

拍摄时需注意:

  1. 从不同角度拍摄15-20张图像
  2. 确保棋盘在每张图像中完整可见
  3. 覆盖图像的不同区域(中心、四角)
  4. 包含不同倾斜角度(平面、倾斜、旋转)
  5. 保持适度的透视变形

常见错误:所有拍摄角度过于相似,导致标定结果不稳定。

3.3 角点检测

使用OpenCV的findChessboardCorners函数自动检测角点:

import cv2 import numpy as np # 设置棋盘格尺寸(内角点数量) pattern_size = (7, 7) # 读取图像 img = cv2.imread('calib01.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 角点检测 ret, corners = cv2.findChessboardCorners(gray, pattern_size, None) if ret: # 亚像素级精确化 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria) # 可视化 cv2.drawChessboardCorners(img, pattern_size, corners2, ret) cv2.imshow('Corners', img) cv2.waitKey(0)

3.4 标定计算

通过多组图像计算相机参数:

# 准备对象点(假设棋盘格方格大小为2cm) objp = np.zeros((7*7, 3), np.float32) objp[:,:2] = np.mgrid[0:7, 0:7].T.reshape(-1, 2) * 2.0 # 存储所有图像的对象点和图像点 objpoints = [] # 3D点 imgpoints = [] # 2D点 # 对所有图像进行处理... # ...(角点检测代码如上) objpoints.append(objp) imgpoints.append(corners2) # 相机标定 ret, K, dist, rvecs, tvecs = cv2.calibrateCamera( objpoints, imgpoints, gray.shape[::-1], None, None)

3.5 参数解析

标定结果包含:

  • ret:重投影误差(应小于0.5像素)
  • K:内参矩阵
[f_x 0 u_0] [ 0 f_y v_0] [ 0 0 1 ]
  • dist:畸变系数(k1,k2,p1,p2,k3)
  • rvecs:每幅图像的外参旋转向量(可通过Rodrigues转换为矩阵)
  • tvecs:每幅图像的外参平移向量

4. 坐标反推实现

4.1 单目测距原理

已知内参K和外参[R|t],对于平面物体(Z_w=0),可以建立如下关系:

s[u] [f_x s u_0] [r11 r12 t_x] [X_w] s[v] = [0 f_y v_0] [r21 r22 t_y] [Y_w] s[1] [0 0 1 ] [r31 r32 t_z] [1 ]

通过求解这个方程组,可以从像素坐标(u,v)计算出世界坐标(X_w,Y_w)。

4.2 Python实现代码

def pixel_to_world(pixel_points, K, dist, rvec, tvec): # 去畸变 undistorted = cv2.undistortPoints( np.array(pixel_points, dtype=np.float32), K, dist) # 构建外参矩阵 R, _ = cv2.Rodrigues(rvec) RT = np.hstack((R, tvec)) # 计算投影矩阵 P = K @ RT # 选取平面Z=0的约束 P_flat = np.delete(P, 2, axis=1) # 求解世界坐标 world_points = [] for up in undistorted: up_h = np.append(up[0], 1) world_h = np.linalg.inv(P_flat) @ up_h world_points.append(world_h[:2] / world_h[2]) return np.array(world_points)

4.3 实际应用示例

假设我们已经标定好相机,需要测量工作台上零件的实际位置:

  1. 在工作台上定义坐标系(如以某个角为原点)
  2. 拍摄包含棋盘格和零件的图像
  3. 检测棋盘格角点获取当前外参
  4. 标注零件在图像中的位置
  5. 调用pixel_to_world函数转换坐标
# 已知参数 K = np.array([[1200, 0, 640], [0, 1200, 360], [0, 0, 1]]) # 内参 dist = np.array([-0.1, 0.01, 0, 0]) # 畸变系数 # 当前图像的外参 rvec = np.array([0.1, -0.2, 0.3]) # 旋转向量 tvec = np.array([0, 0, 500]) # 平移向量(mm) # 零件在图像中的像素坐标 pixel_points = [(700, 400), (750, 420)] # 转换为世界坐标 world_points = pixel_to_world(pixel_points, K, dist, rvec, tvec) print(f"实际坐标(mm): {world_points}")

5. 精度优化与问题排查

5.1 提高标定精度的技巧

  1. 图像数量:至少15张高质量图像,覆盖不同角度
  2. 棋盘格质量
    • 使用高精度打印(误差<0.1mm)
    • 粘贴在平整刚性表面(如亚克力板)
  3. 拍摄技巧
    • 避免运动模糊(快门速度>1/500s)
    • 均匀照明避免阴影
  4. 参数优化
    • 使用所有图像联合优化
    • 考虑高阶畸变系数(k3,k4,k5,k6)

5.2 常见问题与解决方案

问题现象可能原因解决方案
重投影误差大角点检测不准确使用亚像素优化,手动验证角点
内参不合理图像角度变化不足增加倾斜角度拍摄
畸变校正异常畸变模型不匹配尝试不同畸变模型组合
坐标反推误差大棋盘格平面与测量平面不平行确保测量时外参准确

5.3 标定结果验证

  1. 重投影误差检查

    • 理想值应<0.5像素
    • 查看各图像误差分布是否均匀
  2. 标定板测量验证

    • 测量已知尺寸物体的实际坐标
    • 比较计算值与真实值的偏差
  3. 极线几何验证(双目系统):

    • 检查极线约束是否满足
    • 验证三维重建精度

6. 进阶应用与扩展

6.1 多相机系统标定

对于双目或多相机系统,除了单相机标定外,还需要:

  1. 计算相机间的相对位置(R,t)
  2. 统一所有相机到同一世界坐标系
  3. 使用cv2.stereoCalibrate进行联合标定
ret, K1, dist1, K2, dist2, R, T, E, F = cv2.stereoCalibrate( objpoints, imgpoints1, imgpoints2, K1, dist1, K2, dist2, image_size, flags=cv2.CALIB_FIX_INTRINSIC)

6.2 手眼标定

当相机安装在机械臂上时,需要解决"手眼标定"问题:

AX = XB

其中:

  • A:机械臂运动
  • B:相机运动
  • X:手眼变换矩阵

OpenCV提供cv2.calibrateHandEye函数专门解决这个问题。

6.3 自动标定系统设计

对于产线应用,可以开发自动标定系统:

  1. 机械装置自动移动标定板
  2. 图像自动采集与角点检测
  3. 异常自动判断与重拍
  4. 参数自动计算与存储

这种系统可以实现每日或每班的自动标定,确保测量精度稳定。