第4章:标定板设计——棋盘格、AprilTag、ChArUco的选择与制作规范

做外参标定这么多年,我见过太多人栽在标定板上。

有人拿着打印店随便打的棋盘格,标出来结果一塌糊涂。有人用AprilTag标定,精度倒是上去了,但角点检测总出问题。还有人用ChArUco,觉得它万能,结果制作不规范,标定误差大得离谱。

说白了,标定板是外参标定的“基准尺”。它不准,后面所有工作都是白费。今天我就把这三类标定板的选型思路和制作规范,一次性讲清楚。

核心观点:没有最好的标定板,只有最适合你场景的标定板。选对了,事半功倍;选错了,事倍功半。

4.1 三类标定板的底层逻辑

先看一张图,帮你快速建立知识框架:

标定板选型决策 棋盘格 AprilTag ChArUco 特性 特性 特性 ✅ 角点精度高 ✅ 实现简单 ❌ 无ID信息 ❌ 需完全可见 ✅ 有唯一ID ✅ 部分遮挡可用 ✅ 6DoF位姿 ❌ 角点精度一般 ✅ 结合两者优点 ✅ 部分遮挡可用 ✅ 角点精度高 ❌ 制作要求高 推荐:单目/双目标定 推荐:多相机/大场景 推荐:高精度/复杂场景

4.2 棋盘格——经典但别乱用

棋盘格是标定界的“老黄牛”。它靠黑白格子交替产生的角点,精度非常高。我早期做相机标定时,一直用它。

优点:

  • 角点检测精度高,亚像素级别
  • 实现简单,OpenCV一行代码搞定
  • 制作成本低,打印即可

缺点:

  • 没有ID信息,无法区分不同区域
  • 必须完整可见,遮挡一点就废
  • 对光照敏感,反光时角点检测会飘

我的经验:棋盘格最适合单目内参标定和双目外参标定。但如果你要做多相机系统(比如3个以上相机),棋盘格会让你崩溃——因为每个相机看到的棋盘格区域不同,你很难建立对应关系。

制作规范:

  • 格子数量:建议 9×6 或 11×8(内角点),别用太少的
  • 格子大小:30mm-50mm 为宜,太小了检测不准,太大了边缘畸变严重
  • 打印材质:哑光纸或铝板,千万别用反光材料
  • 平整度:贴在刚性背板上,软纸板绝对不行

避坑指南:我曾经用普通A4纸打印棋盘格,贴在纸箱上标定。结果标定出来的焦距偏差了5%以上。后来换成3mm铝板,误差直接降到0.3%。记住:标定板的平整度,直接决定了你的标定精度上限。

4.3 AprilTag——灵活但别迷信

AprilTag是MIT开发的二维码家族成员。它最大的特点是每个Tag都有唯一ID,而且支持部分遮挡检测。

我为什么喜欢它?因为做多相机标定时,你只需要在场景里贴几个AprilTag,每个相机看到不同的Tag组合,系统就能自动匹配对应关系。这比棋盘格方便太多了。

优点:

  • 每个Tag有唯一ID,自动匹配
  • 支持部分遮挡,哪怕只看到一半也能用
  • 可以估计6DoF位姿

缺点:

  • 角点精度不如棋盘格(因为角点是Tag边界,不是亚像素级)
  • 检测算法相对复杂,计算量大
  • Tag大小和距离需要匹配,否则检测失败

关键点:AprilTag的精度瓶颈不在检测算法,而在Tag的打印质量和安装平整度。我见过有人用手机屏幕显示AprilTag来标定,结果屏幕的曲面导致位姿误差巨大。

制作规范:

  • Tag家族:推荐使用 Tag36h11(检测鲁棒性好)或 TagStandard41h12(精度更高)
  • Tag大小:根据标定距离选择,一般边长50-100mm
  • 黑白对比度:打印时黑色要足够黑,白色要足够白,灰度打印不行
  • 边框:保留至少1个格子的白色边框,否则检测会失败
# AprilTag生成示例(Python)
import cv2
import numpy as np
from apriltag import apriltag

# 生成Tag36h11家族的ID 0
tag_size = 50  # mm
tag_image = apriltag.generate('tag36h11', 0)
cv2.imwrite('tag_0.png', tag_image)

# 检测时指定Tag大小
detector = apriltag('tag36h11')
results = detector.detect(gray_image)
for r in results:
    # r.tag_id: Tag的ID
    # r.center: 中心点坐标
    # r.corners: 四个角点坐标(像素)
    # r.homography: 单应性矩阵
    print(f"Tag ID: {r.tag_id}, Center: {r.center}")

4.4 ChArUco——棋盘格和AprilTag的“混血儿”

ChArUco是我现在最常用的标定板。它把棋盘格的角点精度和AprilTag的ID信息结合在了一起。

说白了,ChArUco就是在棋盘格的每个格子交叉点附近,嵌入一个小的ArUco标记。这样你既能得到棋盘格的高精度角点,又能通过ArUco标记获得ID信息。

优点:

  • 角点精度高(继承棋盘格)
  • 有ID信息,支持部分遮挡(继承ArUco)
  • 鲁棒性好,光照变化影响小

缺点:

  • 制作要求高,ArUco标记太小了检测不到,太大了影响角点精度
  • 检测算法更复杂,计算量更大
  • 需要仔细调参

我的建议:如果你做的是高精度外参标定(比如激光雷达-相机标定),ChArUco是首选。我在一个项目中用ChArUco标定Lidar-Camera外参,重投影误差从1.2像素降到了0.3像素。但前提是——你得把ChArUco板做对。

制作规范:

  • 棋盘格尺寸:建议 7×5 或 9×6 个格子
  • ArUco标记大小:占格子边长的 60%-70%,太小了检测不到,太大了角点被遮挡
  • ArUco字典:推荐 DICT_6X6_250 或 DICT_5X5_100
  • 打印分辨率:至少 300 DPI,确保ArUco标记清晰
# ChArUco板生成示例(Python + OpenCV)
import cv2
import cv2.aruco as aruco

# 参数设置
squares_x = 7      # 横向格子数
squares_y = 5      # 纵向格子数
square_length = 40 # 格子边长(mm)
marker_length = 25 # ArUco标记边长(mm)

# 创建字典和棋盘
dictionary = aruco.getPredefinedDictionary(aruco.DICT_6X6_250)
board = aruco.CharucoBoard_create(
    squares_x, squares_y,
    square_length / 1000.0,  # 转成米
    marker_length / 1000.0,
    dictionary
)

# 生成图像
img = board.draw((2000, 1500))
cv2.imwrite('charuco_board.png', img)

# 检测ChArUco角点
charucoCorners, charucoIds = detector.detectBoard(gray_image)
if charucoCorners is not None:
    print(f"检测到 {len(charucoCorners)} 个角点")

4.5 选型决策指南

说了这么多,到底怎么选?我整理了一个决策表:

场景 推荐标定板 理由
单目内参标定 棋盘格 简单、精度高、够用
双目外参标定 棋盘格 或 ChArUco 棋盘格够用,ChArUco更鲁棒
多相机系统(3+) AprilTag 或 ChArUco 需要ID信息自动匹配
激光雷达-相机标定 ChArUco 需要高精度角点+ID信息
大场景/远距离标定 AprilTag(大尺寸) Tag可以做大,检测距离远
快速原型验证 棋盘格 打印快、实现简单

重要提醒:不管你选哪种标定板,制作时都要注意以下几点:

  • 打印后测量实际尺寸,不要相信设计尺寸(打印机有缩放误差)
  • 贴在刚性背板上,确保平整
  • 保存时避免弯折、受潮
  • 定期检查标定板是否有磨损

4.6 实战经验总结

最后,分享几个我踩过的坑:

  • 别用手机屏幕显示标定板:屏幕有曲率,而且亮度不均匀,标定结果会带系统误差
  • 标定板尺寸要匹配视场:太小了角点太少,太大了边缘畸变严重。一般占图像面积的 1/4 到 1/3 比较合适
  • 多拍不同姿态:至少拍20-30张,覆盖不同角度、距离、位置。我习惯拍50张,然后挑出质量最好的30张
  • 检查角点检测结果:不要直接跑标定,先可视化角点检测结果,看看有没有漏检或误检

一句话总结:标定板是标定精度的基石。花1小时做好标定板,能省下10小时排查标定误差的时间。这个投入,绝对值。

可可精品资料库