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的晃动,在远距离标定时都会造成像素级的误差。
我的安装流程:
- 选择支架:用重型三脚架或定制夹具。不要用手持,不要用轻型自拍杆。
- 调整水平:在标定板背面贴一个气泡水平仪。每次移动后检查一下。
- 标记位置:在地面或工作台上用胶带标记标定板的放置位置。四相机标定时,你需要多次移动标定板,标记位置能保证重复精度。
- 环境光控制:避免强光直射标定板。均匀的漫反射光最好。我一般在阴天或室内荧光灯下做标定。
3.4 标定板设计的关键参数
设计标定板时,有几个参数需要根据你的相机系统来调整:
| 参数 | 推荐范围 | 我的经验 |
|---|---|---|
| 格子/圆点数量 | 棋盘格:9x6 ~ 12x9 圆点阵:7x7 ~ 11x11 |
数量太少,标定精度不够;太多,检测容易失败。四相机标定我常用11x8的ChArUco板 |
| 格子实际尺寸 | 10mm ~ 50mm | 根据视场角来定。大视场用大格子,小视场用小格子。保证标定板在图像中占30%~60%的面积 |
| 标定板整体尺寸 | 相机视场的1/3 ~ 2/3 | 太小了特征点不够,太大了边缘畸变严重。我一般让标定板在图像中占40%左右 |
| 背景色 | 白色或浅灰色 | 不要用彩色背景。黑白对比最稳定,彩色背景在去畸变后颜色会偏移 |
3.5 本章知识体系
下面这张图总结了标定板设计与制作的核心流程和决策点:
3.6 实战建议
最后,给你几个可以直接用的建议:
- 备两块标定板:一块大的(覆盖全视场),一块小的(用于近距和边缘)。四相机标定时,大板用来做全局优化,小板用来补拍角落。
- 标定板不要用太久:打印的标定板用3~6个月就换一次。边角磨损、纸张变形都会引入误差。
- 记录实际尺寸:每次打印后,用游标卡尺量10个格子的总宽度,取平均值。这个值要精确到0.1mm。
- 做一张“标定板身份证”:在标定板背面写上类型、格子尺寸、生成日期。项目多了你根本记不住哪块板对应什么参数。
一个小技巧:在标定板的四个角贴上反光标记点。这样在图像中能快速定位标定板的位置,辅助后续的ROI裁剪和特征点筛选。我所有项目的标定板都这么干,省了不少预处理的时间。