3. 标定板设计与制作

标定板这东西,说白了就是相机的“尺子”。你尺子不准,后面标出来的参数全是废的。我见过太多项目,算法调了半天,最后发现是标定板打印尺寸错了——这种坑,踩一次就够了。

这一章我们聊聊三种主流标定板:棋盘格、圆点阵、ChArUco板。怎么选?怎么生成?打印和安装时要注意什么?我把这些年踩过的坑和总结的经验都摊开来讲。

3.1 三种标定板的对比与选型

先看一张对比表,心里有个底:

类型 特征点提取 鲁棒性 适用场景 我的推荐指数
棋盘格 角点(亚像素) 中等 常规单目/双目标定 ★★★★☆
圆点阵 圆心(椭圆拟合) 较高 大视场、畸变较大场景 ★★★★☆
ChArUco板 角点 + ArUco码 高 多相机、部分遮挡、大角度 ★★★★★

棋盘格是最经典的。角点检测算法成熟,OpenCV里直接调findChessboardCorners就行。但有个问题——棋盘格必须完全可见,缺一个角就全废了。我在做四相机环视标定时,经常有一两个相机只能拍到部分标定板,棋盘格直接罢工。

圆点阵就好很多。圆心检测对畸变不敏感,哪怕图像边缘的圆变成了椭圆,拟合出来的圆心位置依然很准。不过要注意,圆点阵需要标定板背景和圆点有高对比度,否则二值化那一步就容易翻车。

ChArUco板是我现在的主力。它把棋盘格和ArUco码结合起来——每个格子角落都有一个唯一的ID。哪怕只拍到一部分,系统也能通过ID知道“我拍的是标定板的哪一块”。四相机标定里,这个特性太香了。

核心结论:单相机或简单双目,棋盘格够用。多相机、大角度、部分遮挡场景,直接上ChArUco板。圆点阵作为备选,适合畸变特别大的鱼眼镜头。

3.2 标定板的生成方法

生成标定板,我推荐两种方式:代码生成和在线工具。代码生成更可控,适合批量生产。

3.2.1 用OpenCV生成棋盘格

OpenCV自带了cv2.aruco.drawChessboardCorners,但那是画检测结果的。生成标定板图片,我习惯自己写:

import cv2
import numpy as np

# 棋盘格内角点数量(列,行)
pattern_size = (9, 6)
# 每个格子的实际尺寸(毫米)
square_size = 30.0
# 生成图像尺寸(像素)
img_size = (1200, 800)

# 创建白色背景
board = np.ones((img_size[1], img_size[0]), dtype=np.uint8) * 255

# 计算格子像素尺寸
cols, rows = pattern_size
cell_w = img_size[0] // (cols + 1)
cell_h = img_size[1] // (rows + 1)

# 绘制黑白格子
for i in range(rows + 1):
    for j in range(cols + 1):
        if (i + j) % 2 == 0:
            x1, y1 = j * cell_w, i * cell_h
            x2, y2 = (j + 1) * cell_w, (i + 1) * cell_h
            cv2.rectangle(board, (x1, y1), (x2, y2), 0, -1)

cv2.imwrite('chessboard_9x6_30mm.png', board)

这里有个细节:pattern_size是内角点数量,不是格子数量。9x6的内角点,对应10x7个格子。我第一次做的时候搞反了,打印出来怎么检测都不对——嗯,这种低级错误犯一次就够了。

3.2.2 生成ChArUco板

ChArUco板的生成稍微复杂一点,但OpenCV的cv2.aruco模块已经封装好了:

import cv2
import cv2.aruco as aruco

# 定义字典和参数
dictionary = aruco.getPredefinedDictionary(aruco.DICT_6X6_250)
board = aruco.CharucoBoard_create(
    squaresX=11,          # 列方向格子数
    squaresY=8,           # 行方向格子数
    squareLength=0.03,    # 格子边长(米)
    markerLength=0.022,   # ArUco码边长(米)
    dictionary=dictionary
)

# 生成图像
img = board.draw((1200, 800))
cv2.imwrite('charuco_11x8_30mm.png', img)

个人习惯:我一般把squareLength和markerLength的比例控制在1.3~1.5之间。太接近的话,ArUco码和格子边界容易混淆;差太多又浪费空间。

3.2.3 在线工具推荐

如果只是临时用一下,不想写代码,可以用这些在线生成器:

  • Calib.io Pattern Generator:支持棋盘格、圆点阵、ChArUco,可导出PDF/SVG
  • OpenCV官方脚本:在OpenCV的doc/pattern_tools目录下,有现成的Python脚本
  • Mr. Calibrator:一个轻量级网页工具,适合快速验证

但我建议你至少用代码生成一次。为什么?因为你要理解参数的含义,后面调试时才能快速定位问题。

3.3 打印与安装的避坑指南

标定板生成好了,打印和安装才是真正的“翻车高发区”。

3.3.1 打印注意事项

我曾经在一个项目里,用普通A4纸打印了标定板,贴在纸板上用。结果标出来的内参,焦距偏差了5%以上。后来发现是纸张受潮变形了。

打印时记住这几点:

  • 材质:用哑光相纸或防水PP纸。不要用光面纸——反光会让角点检测失败。
  • 尺寸:打印前用尺子量一下实际格子尺寸,输入到标定程序里。打印机有缩放误差,别信软件里填的数字。
  • 平整度:贴在刚性背板上,比如亚克力板或铝塑板。软纸板不行,弯曲会引入额外畸变。
  • 对比度:黑白格子的灰度差要大于150(0~255范围)。打印出来后用手机拍一张,检查一下直方图。

警告:不要用激光打印在普通复印纸上。碳粉附着不牢,用几次就掉粉,角点边缘会变得模糊。我吃过这个亏,标定结果一天一个样。

3.3.2 安装与固定

标定板在标定过程中必须保持静止。哪怕0.1mm的晃动,在远距离标定时都会造成像素级的误差。

我的安装流程:

  1. 选择支架:用重型三脚架或定制夹具。不要用手持,不要用轻型自拍杆。
  2. 调整水平:在标定板背面贴一个气泡水平仪。每次移动后检查一下。
  3. 标记位置:在地面或工作台上用胶带标记标定板的放置位置。四相机标定时,你需要多次移动标定板,标记位置能保证重复精度。
  4. 环境光控制:避免强光直射标定板。均匀的漫反射光最好。我一般在阴天或室内荧光灯下做标定。

3.4 标定板设计的关键参数

设计标定板时,有几个参数需要根据你的相机系统来调整:

参数 推荐范围 我的经验
格子/圆点数量 棋盘格:9x6 ~ 12x9
圆点阵:7x7 ~ 11x11
数量太少,标定精度不够;太多,检测容易失败。四相机标定我常用11x8的ChArUco板
格子实际尺寸 10mm ~ 50mm 根据视场角来定。大视场用大格子,小视场用小格子。保证标定板在图像中占30%~60%的面积
标定板整体尺寸 相机视场的1/3 ~ 2/3 太小了特征点不够,太大了边缘畸变严重。我一般让标定板在图像中占40%左右
背景色 白色或浅灰色 不要用彩色背景。黑白对比最稳定,彩色背景在去畸变后颜色会偏移

3.5 本章知识体系

下面这张图总结了标定板设计与制作的核心流程和决策点:

标定板设计与制作知识体系 第一步:标定板选型 棋盘格 圆点阵 ChArUco板 第二步:标定板生成 代码生成(推荐) 在线工具(临时) 第三步:打印与安装(易翻车) 材质:哑光相纸 尺寸:实际测量 平整:刚性背板 固定:重型支架 选型 → 生成 → 打印安装,每一步都影响最终标定精度

3.6 实战建议

最后,给你几个可以直接用的建议:

  • 备两块标定板:一块大的(覆盖全视场),一块小的(用于近距和边缘)。四相机标定时,大板用来做全局优化,小板用来补拍角落。
  • 标定板不要用太久:打印的标定板用3~6个月就换一次。边角磨损、纸张变形都会引入误差。
  • 记录实际尺寸:每次打印后,用游标卡尺量10个格子的总宽度,取平均值。这个值要精确到0.1mm。
  • 做一张“标定板身份证”:在标定板背面写上类型、格子尺寸、生成日期。项目多了你根本记不住哪块板对应什么参数。

一个小技巧:在标定板的四个角贴上反光标记点。这样在图像中能快速定位标定板的位置,辅助后续的ROI裁剪和特征点筛选。我所有项目的标定板都这么干,省了不少预处理的时间。