第二章 相机选型与标定:工业相机分类与选型、镜头参数与选型、相机标定原理与实战(张正友标定法)
各位同学,咱们今天聊点硬核的。相机选型与标定,说白了就是给贴片机装上一双「看得准」的眼睛。我做了这么多年自动化设备,见过太多因为相机没选好、标定没做对,导致整条产线报废的案例。嗯,咱们一步步来。
2.1 工业相机分类与选型
工业相机,不是咱们手机上的摄像头。它要扛得住产线上的震动、灰尘、温度变化。我个人习惯把工业相机分成三大类:
- CCD相机:成像质量高,噪声低,适合高精度视觉引导。缺点是贵,帧率一般。
- CMOS相机:功耗低,帧率高,现在技术成熟了,很多贴片机都在用。性价比不错。
- 线阵相机:适合检测连续移动的物体,比如PCB板在传送带上跑,线阵相机一行一行扫过去。
选型时,我建议你重点关注这几个参数:
| 参数 | 说明 | 贴片机场景建议 |
|---|---|---|
| 分辨率 | 像素数量,比如500万、1200万 | 贴片机一般500万~1200万够用,太高了数据量大,处理慢 |
| 帧率 | 每秒采集多少帧图像 | 高速贴片机需要30fps以上,低速的15fps也行 |
| 靶面尺寸 | 感光芯片的物理大小,常见1/1.8英寸、2/3英寸 | 靶面越大,视野越大,但镜头也要匹配 |
| 接口 | USB3.0、GigE、Camera Link等 | GigE最常用,传输距离远,抗干扰好 |
2.2 镜头参数与选型
镜头是相机的「眼镜」。选错了镜头,再好的相机也白搭。你想想看,镜头决定了两件事:看得多宽和看得多清楚。
核心参数有这些:
- 焦距:比如8mm、12mm、25mm。焦距越短,视野越宽;焦距越长,看得越远越清楚。
- 光圈:用F值表示,F值越小光圈越大,进光量越多。但光圈太大,景深会变浅。
- 景深:能清晰成像的深度范围。贴片机里,元件高度不一,景深太浅会导致边缘模糊。
- 畸变:镜头带来的图像变形。普通镜头有桶形畸变或枕形畸变,高精度场景要用远心镜头。
视野决定焦距,精度决定分辨率,景深决定光圈,畸变决定镜头类型。
举个例子:你要检测一个20mm×20mm的PCB区域,精度要求0.01mm。那相机分辨率至少需要2000像素×2000像素,也就是400万像素。镜头焦距怎么算?用公式:焦距 = 工作距离 × 靶面尺寸 / 视野。假设工作距离100mm,靶面尺寸6.4mm(1/1.8英寸),视野20mm,那焦距就是32mm。嗯,选个35mm的镜头就差不多。
2.3 相机标定原理与实战(张正友标定法)
相机标定,说白了就是告诉电脑:「你看到的图像和真实世界之间,差了多少?」。没有标定,你测出来的坐标全是错的。
张正友标定法是目前最主流的标定方法。它的核心思想是:拍一张棋盘格,然后算。为什么用棋盘格?因为棋盘格的角点(黑白格子交界处)很容易被算法检测到,而且位置关系是已知的。
2.3.1 标定的数学原理(简单版)
相机成像可以简化成三步:
- 世界坐标系 → 相机坐标系(旋转+平移,这叫外参)
- 相机坐标系 → 图像坐标系(透视投影,这叫内参)
- 图像坐标系 → 像素坐标系(缩放+偏移)
标定要算的就是:内参矩阵(焦距、主点坐标、畸变系数)和外参矩阵(相机相对于标定板的位置)。
张正友标定法的巧妙之处在于:它只需要你从不同角度拍几张棋盘格照片,然后通过数学推导,把内参和外参一起解出来。不需要知道标定板的精确三维位置,省事很多。
2.3.2 实战步骤
我带你走一遍完整的标定流程。这是我做过的项目里最常用的方法:
- 准备标定板:打印一张棋盘格,格子大小要精确测量(比如每个格子5mm×5mm)。贴在平整的板上。
- 采集图像:从不同角度拍10~20张照片。注意:角度要多样,有倾斜、有旋转、有远近。我习惯拍15张左右,太少精度不够,太多浪费时间。
- 检测角点:用OpenCV的
cv2.findChessboardCorners()找到所有角点。 - 标定计算:调用
cv2.calibrateCamera(),输入角点坐标和真实世界坐标,得到内参和畸变系数。 - 评估结果:看重投影误差,一般小于0.5像素就算合格。
代码示例(Python + OpenCV):
import cv2
import numpy as np
import glob
# 棋盘格参数
chessboard_size = (9, 6) # 内角点数量
square_size = 5.0 # 格子大小,单位mm
# 准备真实世界坐标
objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
objp *= square_size
# 存储所有图片的角点
objpoints = [] # 真实世界坐标
imgpoints = [] # 图像坐标
images = glob.glob('calib_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测角点
ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
if ret:
objpoints.append(objp)
imgpoints.append(corners)
# 可视化角点
cv2.drawChessboardCorners(img, chessboard_size, corners, ret)
cv2.imshow('img', img)
cv2.waitKey(500)
cv2.destroyAllWindows()
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
print("内参矩阵:\n", mtx)
print("畸变系数:\n", dist)
print("重投影误差:", ret)
2.3.3 标定结果怎么用?
标定完,你得到了内参矩阵和畸变系数。接下来要做两件事:
- 畸变校正:用
cv2.undistort()把每帧图像校正,消除镜头畸变。 - 坐标转换:把图像上的像素坐标,通过内参和外参,转换成真实世界的物理坐标。这样贴片机才知道元件该往哪里放。
举个例子:你在图像上检测到一个元件的中心在(320, 240)像素位置,经过标定转换后,得到它在世界坐标系中的位置是(12.5mm, 8.3mm)。贴片机的吸嘴就根据这个坐标去抓取和放置。
2.4 知识体系总览
下面这张图,是我自己画的相机选型与标定的知识体系。你看一眼,心里就有数了:
嗯,这张图把咱们今天讲的内容串起来了。相机选型、镜头选型、相机标定,这三件事是环环相扣的。选错了相机,标定再准也没用;标定没做好,再好的相机也是白搭。
最后说一句:做视觉引导定位,别怕花时间在标定上。我见过太多工程师急着调算法,结果定位不准,回头才发现是标定没做对。磨刀不误砍柴工,标定这一步,值得你认真对待。
公众号:蓝海资料掘金营,微信deep3321