ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

相机标定从原理到落地:内参外参与误差排查实战

相机标定从原理到落地:内参外参与误差排查实战

相机标定(camera-calibration)这件事,我第一次接触时以为它就是个"跑个函数、出一组参数"的体力活,直到拿着那组参数去做测距,发现两米外的目标偏差接近三厘米,才明白标定是个从物理世界到数学模型反复对账的过程。它在做的事情,本质上是给相机建立一套"翻译规则":把图像上的像素坐标,翻译成现实世界里的角度、距离和三维位置。凡是需要"从一张图里量出真东西"的场景,都绕不开它——工业视觉测量、机器人抓取、双目深度、AR叠加、无人机降落、产线缺陷定位,甚至手机里那颗广角镜头的边缘校正。这篇内容面向的是刚接手标定任务、或者标完之后发现"参数看着挺好但一用就飘"的工程人员,也会补齐坐标系、畸变模型、误差判读这些新手最容易糊过去的环节。我会把标定拆成"标什么、怎么选、怎么干、怎么查、怎么落地"五段,每一段都给出可以直接抄的参数和判据。

1. 相机标定到底在标什么:从现象到模型的拆解

1.1 为什么同一个世界在相机里长得不一样

先不谈公式,谈现象。你拿手机对着走廊拍一张,会发现靠近画面的直线是直的,画面边缘的门框却是弯的;你把同一个杯子放在画面正中和放在画面角落,它的形状会发生变化;你用广角拍合照,站边上的人脸会被拉宽。这些现象的根源有三层:镜头是球面透镜,光线在不同位置的折射角度不一样;感光元件是平面,而成像的几何关系是投影;装配时镜头光轴和感光面法线不严格重合。三层叠加,就产生了我们能观察到的畸变、偏心、主点偏移。

相机标定要做的,就是把这三种偏差各自量化成一个可计算的模型,再把模型里的系数求出来。求解出来的这一组数,就是标定结果。这里面没有玄学,全是几何:一个三维点经过外参变换进入相机坐标系,再经过内参投影落到归一化平面,再经过畸变模型偏移到实际成像位置,最后加上主点平移到像素坐标。整条链路每一环都有一个参数集,标定的目标就是让"预测的像素位置"和"实际检测到的像素位置"尽可能重合。重合程度用重投影误差衡量,这个指标后面会反复出现,它是判断标定质量最直接的一把尺子。

之所以要强调"现象先行",是因为很多新手一上来就抄代码,跑完输出一个 rms 值就以为完事。实际上你只有先明白误差来自哪里,才知道采集图像时应该规避什么:画面边缘有畸变,所以标定板必须摆到边缘;镜头对不同距离的成像不同,所以标定距离要覆盖工作距离;装配偏心在特定角度才会暴露,所以姿态要足够杂。这些都是几何现象反推出来的操作要求,不是凭经验拍脑袋。

还有一点容易被忽略:标定从来不是"标相机",而是"标整个成像链路"。镜头、感光元件、图像采集卡、甚至图像缩放算法(比如某些模组内部做了裁切或缩放),都会进入这条链路。如果你标完参数后,软件里又把图像 resize 了一遍,那组参数立刻失效。这一点在用工业模组或者某些带 ISP 的方案时特别常见,我见过有人查了两天精度问题,最后发现是驱动里默认开了一个 2 倍降采样。

1.2 内参、外参与畸变:三张表说清核心对象

标定输出的参数,工程上一般分成三块。用一张表先把它们摆清楚,后面的所有操作都是围绕这三块展开的。

参数类别具体内容物理含义是否随安装位置变化
内参fx、fy、cx、cy、skew焦距折算到像素单位、主点像素坐标、坐标轴倾斜不随安装位置变,但随对焦、变焦变化
畸变系数k1、k2、k3(径向)、p1、p2(切向)镜头径向弯曲与装配偏心造成的像素偏移与镜头绑定,一般固定
外参旋转矩阵 R、平移向量 t相机相对于某个世界坐标系的姿态和位置每次安装、震动、重新固定都会变

内参里最常被误读的是 fx 和 fy。它们不是"焦距",而是焦距除以像元尺寸之后得到的像素当量。举个具体数字:一个镜头标称焦距 8mm,感光元件像元 3.45μm,那么 fx 的理论值大约是 8000/3.45≈2319 像素。如果你标出来的 fx 是 1800 或者 3000,那基本可以判定有问题——要么是图像被缩放过了,要么是采集姿态太单一导致焦距和其他参数耦合了。这个反算习惯我建议每个人都养成,标完先拿理论值对一下,比看重投影误差更早发现问题。

cx、cy 是主点,理论上接近图像中心,但经常偏出几十个像素。偏一点是正常的,装配公差决定的;偏得离谱(比如超过图像尺寸的 5%)就要怀疑是不是标定板姿态覆盖不全,导致主点和畸变中心互相拉扯。至于 skew,现代相机的像元基本是正交排列,这个值通常接近 0,标定时可以固定为 0 以减少自由度,只有在用某些特殊扫描方式成像时才需要放开。

畸变这块,径向畸变是主角,表现为"桶形"或"枕形";切向畸变是配角,来自镜头与感光面不平行,量级通常小一个数量级,但广角镜头下不能忽略。这里有个实操判断:如果你做 undistort 之后发现画面在某个角上"拉扯"得特别厉害,而其他三个角正常,那大概率是切向畸变没标好,或者那个角的标定板图像质量太差被错误地参与了优化。

1.3 标定的本质是求一个最优映射而不是精确解

很多人对标定有个误解,以为存在一组"绝对正确"的参数,标定的任务是把它找出来。实际不是。真实的镜头不满足针孔模型,畸变模型只是对真实光学行为的近似,参数之间存在耦合(比如 fx 和 tz 在单目里就难以完全解耦),图像有噪声,角点检测有亚像素误差。所以标定求出来的永远是一组"在当前模型下、当前数据上最优"的解。

理解这一点非常重要,因为它决定了两件事。第一,不要迷信重投影误差。0.1 像素的重投影误差不代表测量精度是 0.1 像素,它只说明模型在你的标定数据上拟合得很好。如果你的标定板只在一个距离上拍过,模型可能在这个距离上极其精确,换个距离就崩。第二,标定的评价必须用"未参与优化的数据"。这就是为什么我后面会强调验证集:留几张图不参与标定,用它们算误差,才是真实水平。

也正是因为"最优映射"这个定位,标定的很多决策就变成了工程折中:姿态要多杂、图像要多少张、畸变模型取几阶、要不要固定某些参数。这些没有标准答案,取决于你的精度要求和算力预算。后面几节我会给出常用的经验区间,但每个项目最终都得用自己的验证数据去收敛。

2. 标定方案选型:不同场景该走哪条路

2.1 棋盘格、圆点板与编码标记的取舍

标定板的选择直接决定了角点检测的稳定性和后续流程的复杂度。三种主流方案的差异,我整理成下面这张表,这些结论来自实际反复对比,不是照抄手册。

类型角点检测稳定性遮挡鲁棒性姿态解算适合场景
棋盘格高,亚像素可达 0.1px 级差,缺一角就整板失效需要完整可见实验室标定、单目/双目首选
对称圆点中,圆心受透视影响有偏心误差中需处理偏心补偿大视野、远距离
非对称圆点中高中方向可判定易混淆方向的产线场景
编码标记板(如 AprilTag 类)高好,可部分可见单标记即可给姿态现场标定、大空间、多相机

棋盘格的优势是角点定义清晰——黑白方格的交点,几何位置无歧义,亚像素优化收敛快,这也是它成为事实标准的原因。它的软肋是必须整板可见,一旦被遮挡或者出画,那张图就废了。在产线或者大设备上,很难保证每张图都完整拍到板子,这时候编码标记板的价值就出来了:每个标记自带 ID,部分可见也能提供约束。

圆点板有个坑必须提:透视投影下,圆的成像中心并不等于圆心投影点,会往远离图像中心的方向偏。距离越近、视场角越大,这个偏心越明显。所以用圆点板做高精度标定时,必须开启偏心补偿,否则会引入系统误差。有人图省事直接把圆心当特征点用,短焦下误差能到零点几个像素,肉眼看不出,测距时就体现出来了。

还有一个现实因素:板子的平整度。打印在 A4 纸上贴到硬板上,纸张的起伏、胶带的厚度、板子本身的翘曲,都会变成"标定板不共面"的误差,而模型假设它是完美平面。这个误差不会体现在重投影误差里(因为优化会把它吸收到参数里),但会在实际测量中暴露。我的做法是:高精度场合用铝基板或者陶瓷基板,普通场合至少把打印纸裱在厚玻璃上压平,别用泡沫板。

2.2 单目标定、双目标定与多相机系统的差异

单目标定是最基础的一层,输出内参和畸变,顺带每张图的外参。它解决的问题是"这台相机怎么把三维投成二维"。单目标定做完,你可以做畸变校正、可以做基于已知平面的位姿估计(比如把板子放在工作台上,算出相机相对板子的位置)。

双目标定是在单目的基础上再加一层:两台相机之间的相对位姿。关键点是必须先把两台相机各自的内参标好,再用同一时刻、同一块板子的图像去求 R 和 T。如果两台相机的内参是用不同批次、不同距离的图像标的,那求出来的外参会带着系统偏差。工程上的标准流程是:先各自单目标定、固定内参,再联合 stereoCalibrate。这一步的参数设置里,把内参设为固定(CALIB_FIX_INTRINSIC),只优化 R、T 和少量畸变耦合项,结果会稳定得多。

多相机系统(三台以上、或者相机加投影的结构光系统)复杂度会跳一个台阶。难点不在求解,而在"共同观测":所有相机必须在同一时刻看到同一个标定物,才能建立统一的世界坐标系。这里就牵出时间同步的问题——如果相机是软触发、曝光时刻差几十毫秒,板子又在动,那外参必然错。我见过一个四相机环视的项目,外参怎么标都不收敛,最后发现是其中一路相机的采集有固定延迟。所以多相机标定前,先把硬触发或者时间戳对齐做好,比调算法有用得多。

2.3 标定板尺寸与工作距离的参数计算

标定板做多大、拍多远,这两个数不是随便定的,可以直接算。核心原则是:标定板在图像中的占比要够大,通常建议占图像短边的 1/3 到 1/2,最低不要低于 1/4。占比太小,角点像素间距小,亚像素误差的相对影响就大。

反过来推板子尺寸。假设你的工作距离是 D,相机水平视场角是 FOV,那么在距离 D 处的视野宽度 W ≈ 2·D·tan(FOV/2)。标定板宽度取 W 的 40% 到 60% 比较合适。举个具体的:工作距离 800mm,水平视场角 50 度,那 W ≈ 2×800×tan25° ≈ 746mm,板子宽度选 300 到 450mm。方格边长则按"图像上每个方格至少 25 到 40 像素"来定,方格太小角点定位精度上不去,太大则一张图里格子数太少,约束不足。

方格数量也有讲究。常见规格是 9×6、11×8 这种。格子太少(比如 5×4),单张图提供的约束不足,需要更多姿态;格子太多(比如 20×15),远端格子在小视场下糊成一片,检测反而失败。我的经验是 9×6 到 11×8 这个区间最稳,既能保证约束,又不容易检测失败。

还有一条常被忽视:标定距离要覆盖实际工作距离。如果你的相机在 300mm 到 1200mm 之间工作,标定图像就应该在这个范围内分布,而不是全挤在 600mm 一个距离上。原因前面说过,参数之间存在耦合,单一距离的标定会让模型"过拟合"到那个距离。实际操作里,我会按近、中、远三档各拍若干张,近端多一点,因为近端畸变大、信息量大。

3. 从拍照到出参:完整实操流程拆解

3.1 采集图像:姿态覆盖比数量更重要

采集这一步决定了标定质量的上限,后面所有算法都只是逼近这个上限。最典型的错误是"拍 30 张,但 30 张都是正对板子、只是位置平移"。这种数据在数学上是严重退化的:板子平面始终平行于像平面,无法约束焦距和主点的耦合,标出来的 fx 可能偏差百分之十几,而重投影误差看起来还很漂亮。

正确的姿态分布,我总结成四条:一是倾斜,每张图的板子平面与像平面夹角在 15 到 45 度之间,至少要有两个互相垂直方向的倾斜(绕横轴、绕纵轴);二是覆盖,板子要出现在画面的四角、四边、中心,尤其是四个角,因为畸变在边缘最大,那里没有数据就约束不到高次畸变项;三是尺度,板子在不同距离上出现,近端板子占画面一半,远端占三分之一;四是数量,单目 15 到 25 张是比较舒服的区间,少于 10 张容易欠约束,多于 40 张边际收益很小,反而增加人工筛选成本。

质量比数量更重要。每张图我都要求:不模糊(手持拍摄要开高快门或者补光,运动模糊会让角点亚像素定位直接失效)、不过曝(棋盘格白格过曝会吃掉角点边界梯度)、不反光(亚克力或者覆膜板在斜射光下会出现镜面反射斑,直接盖住角点)。我踩过最亏的一次坑,是在灯光正下方拍了一组图,白格边缘出现一圈光晕,角点检测全部偏了半个像素,重投影误差 0.6,怎么调参数都下不来,换位置重拍立刻降到 0.15。

还有一个硬件层面的注意点:标定时的对焦状态必须和实际使用时一致。很多镜头是手动对焦环,标定时拧到一个位置,用的时候又拧了,那焦距就变了,内参直接失效。变焦镜头更是如此——只要焦段动了,就必须重标。这条我在项目里会写进作业规范,因为它是那种"不注意就全盘作废"的问题。

3.2 角点检测与亚像素优化

角点检测分两步:粗定位和亚像素精化。粗定位找棋盘格的大致角点位置和排列顺序,精化则在一个小窗口内用灰度梯度拟合出角点的亚像素坐标。这一步的精度直接决定标定上限。

用 OpenCV 的话,主流程大概是这样,我把关键参数和注释都写进去:

import cv2 import numpy as np pattern_size = (11, 8) # 内角点数量,注意不是方格数 square_size = 30.0 # 方格实际边长,单位 mm criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-4) objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32) objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) objp *= square_size # 用真实物理尺寸,后面做三维测量才有意义 obj_points, img_points = [], [] gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 优先用 SB 版本,鲁棒性和速度都更好 found, corners = cv2.findChessboardCornersSB( gray, pattern_size, flags=cv2.CALIB_CB_EXHAUSTIVE | cv2.CALIB_CB_ACCURACY) if found: corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners)

几个参数值得说清楚。内角点数量这个参数最容易被搞错:一张 12×9 个方格的棋盘,内角点是 11×8。传错的话函数永远返回 False,新手常在这里卡住。cornerSubPix 的窗口我一般用 11×11 或者 9×9,窗口太大容易把相邻角点的梯度混进来,太小则噪声抑制不足;迭代终止条件设到 1e-4 已经足够,因为受限于图像噪声,再往下收敛也没有物理意义。

关于 SB(Stable Board)版本,如果你的 OpenCV 版本支持,我强烈建议用它。它对光照不均、轻微模糊、低对比度的容忍度明显更好,而且不需要提前做二值化。传统 findChessboardCorners 在背景杂乱或者板子边缘有干扰时容易错检,返回一个"看起来检测到了但角点顺序乱了"的结果,这种错误最难发现——因为后面标定照样能跑出参数,只是全错。所以我现在都会做一次可视化,把检测到的角点画在图上逐张过一遍,30 张图看下来大概两分钟,能省掉半天的排查。

如果是圆点板,用的是 findCirclesGrid,注意选对称还是非对称模式,这决定能不能判定方向。另外圆点板检测完同做亚像素精化,但圆点的亚像素优化目标不是梯度交点而是灰度重心,实现上不同,别直接套棋盘的函数。

3.3 求解与重投影误差判读

数据齐了就进入求解。单目标定调用 calibrateCamera,输入是每张图的三维物点和二维像点,输出内参矩阵、畸变系数、每张图的外参,以及总体的重投影误差 RMS。

ret, K, D, rvecs, tvecs = cv2.calibrateCamera( obj_points, img_points, gray.shape[::-1], None, None, flags=cv2.CALIB_FIX_K3 | cv2.CALIB_ZERO_TANGENT_DIST) # 视情况固定

flag 的选择是个经验活。CALIB_FIX_K3 表示固定三阶径向畸变,适合普通镜头,能减少一个自由度让结果更稳;如果用的是大广角或者鱼眼,就不能固定,得放开 k3 甚至换鱼眼模型。CALIB_ZERO_TANGENT_DIST 适合装配质量较好的镜头,如果校正后边缘还是有单侧拉扯,再去掉这个固定。我的习惯是先全放开跑一次看结果,如果 k3 的量级很小(比如 1e-3 以下),再固定它重跑,两次结果对比一下。

重投影误差怎么判读,这是能不能用好的分水岭。经验区间是这样的:

RMS 重投影误差水平判断常见原因
< 0.1 px优秀,接近噪声极限数据质量好,多为合成或高对比度图像
0.1 ~ 0.3 px良好,多数工业场景够用正常水平
0.3 ~ 0.5 px可用,需检查是否有系统性偏差姿态覆盖不足、板子不平、少量模糊图
0.5 ~ 1.0 px偏差偏大存在错误检测的图、镜头畸变模型不匹配
> 1.0 px不可用数据或参数设置存在明显问题

但这里要泼一盆冷水:RMS 是平均值,会掩盖单张图的异常。所以一定要看逐张误差。做法是拿每张图的 rvecs、tvecs 和 K、D 做 projectPoints,算该图的平均误差,然后排序。如果 20 张图里 19 张都是 0.15,只有 1 张是 1.2,那这张图基本是错的(可能角点检测错位、可能板子弯了),把它剔掉重标,整体 RMS 会明显下降。相反,如果所有图都在 0.4 左右,那就是系统性问题,剔除没用,得从采集和板子平整度上找原因。

还有一个进阶判据是参数稳定性。同一组数据,随机抽样 80% 的图重复标定 10 次,看 fx、cx 的波动。如果 fx 的波动超过 1%,说明数据约束不足,得补姿态。这个做法比单看 RMS 更能反映参数的可信度,尤其在单目测距场景里,fx 不稳就意味着距离不准。

3.4 参数验收与保存规范

标出来的参数不能直接扔进项目,要做三件事:验证、格式固化、版本管理。

验证要用独立的图。留 3 到 5 张没参与标定的图像,用标定结果做畸变校正,然后观察画面边缘的直线是否变直。更量化一点的做法是:在板子上取两个角点,用 solvePnP 反算它们在相机坐标系下的距离,和实际测得的物理距离对比。如果物理距离是 200mm,反算是 198.5mm,相对误差 0.75%,这个量级对普通工业场景是可以接受的。这个"已知尺寸反算"的验证方式比任何软件指标都实在,因为它走的是和实际应用完全相同的链路。

保存格式上,我一般同时存两份:一份是矩阵的原始数值(K、D、图像尺寸、标定日期、使用的板子规格),用 JSON 或者 YAML 存;一份是校正映射表,用 initUndistortRectifyMap 预计算,存成二进制。后者在实际运行时能省掉每次重建映射的时间,实时系统里这点开销不小。

版本管理经常被忽略。同一台设备,换了镜头、动了对焦、拆装过支架,参数就作废了。我的做法是给每组参数打上三元标识:设备序列号、镜头型号、标定日期,写进文件名和文件头。项目里出现过两次"用错参数"的事故,都是因为同一台设备存了三组参数,没人知道哪组是最新的。后来加了这套标识,再没出过问题。

4. 常见问题排查与避坑实录

4.1 重投影误差很小但实测精度很差

这是最让人抓狂的一类问题,也是我被问得最多的。现象是:标定 RMS 只有 0.12,参数看起来完美;实际拿去测距或者测尺寸,误差却是厘米级。

原因通常有四个,按出现频率排序。

第一是参数耦合造成的假精度。单目针孔模型里,fx 和 tz(沿光轴方向的平移)在单一距离下高度相关。你把板子只在一个距离上拍,优化器可以用"fx 偏大一点、距离偏小一点"的组合拿到同样的重投影误差。所以 RMS 很漂亮,但换一个距离立刻崩。解决办法就是前面说的多距离采集,近中远分布,让耦合无法形成。

第二是标定板实际不共面。打印纸贴在板子上,中间鼓起 0.3mm,模型假设它是绝对平面。这个形变会被优化吸收到畸变参数里,导致畸变系数偏大,边缘校正过度。验证方法很简单:把板子贴到玻璃上重标一次,对比两次的 k1、k2,如果差异超过 10%,那说明之前的板子确实不平。

第三是使用链路和标定链路不一致。标定用的是原始分辨率图像,实际运行时做了 crop 或者 resize;标定用的是某个对焦位置,运行时自动对焦动了。这类问题不会体现在参数里,只会在应用端暴露。排查办法是:拿一张实际运行时保存的原始图,走一遍完整的校正流程,看结果是否符合预期。

第四是坐标系搞错。标定输出的外参是相对标定板坐标系的,实际使用时世界坐标系可能是机器人基座或者传送带。中间要做一次手眼标定或者坐标系转换。如果直接把相机外参当成"相机在世界中的位置",那所有测量都会差一个刚体变换。这个错误在新手项目里极常见,而且因为偏差是刚体变换,看起来"有规律",很容易被误认为是标定精度问题。

4.2 畸变校正后边缘拉伸和黑边怎么处理

做 undistort 之后,很多人会发现两个问题:一是画面边缘出现黑色空洞,二是边缘被拉伸变形。

黑边的来源是校正后有效像素区域超出了原图像范围。处理方式有两个方向。如果你能接受裁剪,用 getOptimalNewCameraMatrix 计算一个新的内参矩阵,它会自动缩放并平移,让有效区域刚好填满图像,代价是损失一部分边缘视野。alpha 参数控制这个折中:alpha=0 表示全裁剪无黑边,alpha=1 表示保留全部像素但有黑边。实际项目里我一般取 0.3 到 0.5,兼顾视野和画面完整性。

如果不能接受裁剪(比如边缘的检测目标很重要),那就得保留映射表并接受黑边,后续算法要做边界判断。这时候要注意,黑边区域的像素值是 0 或者重复边缘值,千万别让后续的检测算法把黑边当成真实景物。

边缘拉伸是另一个话题。根本原因是径向畸变校正本质上是像素重采样,边缘区域被"压缩"回来的时候,采样密度不均匀,会出现信息丢失和插值模糊。这是物理上的必然损失,只能缓解不能消除。缓解手段:一是提高原始图像分辨率再校正,二是校正时用高质量插值(双线性够用,双三次更平滑但更慢),三是尽量让关心的目标落在画面中部。

这里补充一个容易忽略的点:如果你在标定时用了 CALIB_FIX_K3,但镜头的畸变实际上需要 k3 才能描述好,那校正后边缘会残留弯曲。验证方法是校正后的图上放一把直尺,贴着画面边缘拍,看直尺是否笔直。这个土办法比看参数直观得多。

4.3 典型问题速查表

把实际踩过的坑整理成一张表,遇到问题时按症状查,能省掉大量试错时间。

症状最可能原因排查动作处理方式
检测角点返回 False内角点数量传错数一遍方格数减一修正 pattern_size
角点顺序错乱背景干扰或板子出画可视化逐张检查换 SB 检测,重拍
RMS 很高但个别图误差突出单张图模糊或过曝逐张算 projectPoints 误差剔除异常图重标
RMS 很低但测距不准标定距离单一,参数耦合检查距离分布补近远距离图像重标
校正后边缘仍有弯曲畸变模型阶数不足放开 k3 重标增大模型阶数或换鱼眼模型
校正后四角拉扯不对称板子不平或切向畸变异常换平整基板重标开启切向畸变,检查板子
双目视差图有大块空洞外参不准或极线校正失败看 rectified 图像极线是否水平重做 stereoCalibrate
换一个距离测量偏差跳变焦距与距离耦合对比多距离反算结果多距离标定,增大姿态差异
参数用了一段时间后失效对焦、温度、震动导致漂移用验证板复测建立定期复标机制

表里最后一条最值得展开说。标定不是一个一次性动作,而是一个有"保质期"的状态。温度变化会让镜筒和支架产生热胀冷缩,机械震动会让紧固件松动,装配应力释放会让光轴发生微小偏转。这些变化累积起来,内参可能变化不大,但外参会漂。所以只要项目对精度有要求,就必须设计复标策略:要么定期用标准件验证,要么在设备上留一个可重复定位的标定基准。

我在一个长期运行的场景里用过"标准件验证法":做一个已知尺寸的金属块,每天开机时拍一次,反算尺寸,偏差超过阈值就报警复标。这套机制比定期盲目重标有效得多,因为它是按实际漂移量触发的。

5. 标定结果怎么落到实际项目里

5.1 手眼标定与坐标链路的串联

单目标定出来的外参,描述的是相机相对标定板的位置。在真实系统里,你关心的通常是"目标相对机器人末端"或者"目标相对传送带"。中间隔着的这一段,就是手眼标定要解决的事。

手眼标定分两种构型:眼在手上(相机装在机械臂末端)和眼在手外(相机固定,看工作区)。两种构型的求解方程不同,但核心思路一样:让机械臂带着标定板或者带着相机走多个位姿,每个位姿下都能建立一组相机与机械臂之间的约束,联立起来求出相机坐标系和机械臂坐标系之间的固定变换。

实操上有几个关键点。一是位姿数量,通常 10 到 15 个位姿,且旋转轴要绕着不同的方向转,如果全绕一个轴转,旋转部分解不出来。二是位姿质量,旋转角度建议在 30 到 60 度之间,太小则旋转约束弱,太大则标定板容易出画或者姿态解算出错。三是精度验证,标完之后让机械臂走一个已知点,看相机算出来的位置和机械臂末端实际位置差多少,这个差值才是整个系统的手眼精度。

坐标链路一旦串联起来,就要画一张完整的变换图:像素坐标 → 相机坐标(内参)→ 标定板/世界坐标(外参)→ 机械臂基座坐标(手眼)→ 末端坐标(运动学)。这条链路上任何一环出错,最终结果都会错,而且误差会累积。我建议在项目里把这张图画出来贴在工位上,每次排查问题先对链路,比盲调参数效率高得多。

还有一个实践细节:链路中的每个变换都要单独验证。相机内参用已知尺寸板验证,外参用已知位置的板验证,手眼用已知点验证。分段验证的意义在于,出问题时能快速定位到哪一段。一上来就做端到端验证,误差是叠加的,你根本不知道问题出在哪。

5.2 长期漂移与复标策略的设计

精度要求高的场景,标定参数是要持续维护的资产,不是一次性交付物。设计复标策略时,我通常考虑三个层次。

第一层是日常自检。每天或者每个班次开机,用固定的验证件(已知尺寸的标准块、固定在工位的标定板)做一次快速验证,只算一个关键指标(比如标准块的反算尺寸,或者固定点的重投影位置)。这个动作耗时不到一分钟,能及时捕获温度漂移和机械松动。阈值设多少?我的经验是:如果日常波动超过标称精度的 30%,就该安排复标了。

第二层是周期复标。按使用强度和环境条件设定周期,比如温变大的车间两周一次,恒温实验室三个月一次。周期不是拍脑袋,而是从历史漂移数据里统计出来的:连续记录每次复标前后的参数差异,看它随时间的变化曲线,再定周期。

第三层是事件触发。换镜头、动对焦、拆装支架、发生碰撞、软件升级改了图像流水线,这些事件之后必须重标。这条要写进作业规范,因为出事的时候往往没人记得自己动过什么。

另外分享一个降低漂移的工程手段:把相机和标定板的安装做成"可重复定位"结构,比如加定位销、用刚性一体支架、避免用长悬臂。结构刚性上多花一点成本,能省掉后面大量的复标和排查工作。我见过一个项目,相机装在一个一米长的铝型材悬臂上,稍微碰一下就偏零点几度,最后不得不加斜撑才解决。这类问题不是算法能救的。

最后说一句关于参数管理的体会。同一套设备跑久了,会积累出多组参数:不同焦距的、不同工作距离的、不同镜头的。如果没有清晰的命名和版本记录,迟早会出事故。我的做法是在参数文件头强制写上设备编号、镜头编号、标定日期、操作人、验证误差这五项,任何一项缺失就视为无效参数。这个规矩看起来很笨,但它把"用错参数"这类低级错误的概率压到了接近零,比任何聪明的自动化方案都可靠。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进