ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于MATLAB的张正友相机标定实验全流程解析

基于MATLAB的张正友相机标定实验全流程解析 1. 标定这件事到底在解决什么问题1.1 相机为什么需要标定先想一个特别基础的问题你用手机拍了一张照片照片上的某个像素点坐标为 (u, v)你能不能直接说出这个像素对应的物体在现实三维空间中的位置答案是不能。因为从三维世界到二维图像中间隔着一条完整的数学链路——针孔成像模型而这条链路上每一个参数都在影响最终的映射结果。焦距决定了成像的缩放比例主点决定了图像坐标系的原点偏移畸变系数则描述了镜头因为制造工艺和光学结构带来的非线性形变。这些参数统称为相机内参。此外相机在拍摄每一张图片时都有自己的位置和朝向这组参数叫做外参。所谓标定核心工作就是把这些内参和外参一并求出来。这个过程在工程里到底有多重要我做视觉测量项目时体会很深。无论是做三维重建、双目测距、AR叠加还是机械臂抓取定位只要算法输出的是带物理单位的坐标不是纯分类标签相机标定就是绕不开的第一步。标定精度差个一两个像素在近距离测量里可能只差几毫米但一旦物距拉长到两三米误差会被显著放大整个系统的可靠性都会垮掉。所以标定不是可选项而是视觉项目的基石。1.2 张正友标定法为什么能成为工业标准张正友标定法最早是1998年发表的论文《A Flexible New Technique for Camera Calibration》里提出的方法。它最大的贡献是把以前需要高精度3D标定块的标定流程简化成了打印一张棋盘格就能完成。以前传统标定方法需要一个加工精度非常高的三维标定块拍摄多组不同姿态的图片才能求解。这种设备在实验室里有但普通开发者、学生根本搞不到成本也高。张正友的方法只需要一个平面棋盘格让相机在不同角度下拍摄多张照片然后利用平面靶标上的已知角点坐标和对应像素坐标先通过单应性矩阵估算初始内参再通过非线性优化把所有参数放在一起精修。整个流程稳定、精度高、操作门槛低所以后来OpenCV的calibrateCamera、MATLAB的Camera Calibrator都采用了这个思路成了事实上的工业标准。我把这个实验放在MATLAB里跑主要是有两个考量第一MATLAB的Camera Calibrator图形化工具把角点检测、参数求解、误差展示全做成了可视化流程非常适合先建立感性认识第二MATLAB底层提供了detectCheckerboardPoints、estimateCameraParameters这类封装好的函数同时你也可以自己写核心求解代码来对照验证。两边结合既能快速出结果又能真正把原理吃透。2. 实验前的准备工作2.1 棋盘格打印与制作棋盘格是张正友标定法的核心靶标制作质量直接决定实验成败。第一次做实验的同学最容易忽视这步随便找一个网页上的棋盘格图片用A4纸打印就开始了结果角点检测成功率很低标定结果也飘。棋盘格有几个关键参数要提前定好格子数量、每个格子的物理尺寸、打印尺寸。以我常用的12x9棋盘格为例意思是内部角点是11x8个每一格的实际边长设为30mm整个棋盘打印出来大概占一张A4纸。打印之后一定要用直尺或者游标卡尺实测几个格子边长确认实际尺寸和设定尺寸一致。不要完全信任打印机的缩放比例有些打印软件默认“适应页面”会按比例缩放导致实际尺寸偏离设定值。另外两个容易被忽略的细节棋盘格必须贴在一个绝对平整的硬板上比如亚克力板、硬纸板不能用软纸直接拿在手里打印介质要尽量选择哑光纸避免表面反光导致角点检测出现高光干扰。我在实验中曾经用过普通亮面喷墨纸光线稍微偏一点图像上就会出现一片白色光斑那一块棋盘格根本无法检测。2.2 图像拍摄的规范与技巧图片的拍摄质量是标定误差的天花板。这一点无论你用多好的算法都改变不了。按张正友标定法的要求拍摄时只需要一个平面靶标但为了在数学上保证内参可解棋盘格必须在相机视野内呈现多种不同的姿态。我一般遵循几个原则第一数量上拍15到20张至少也要保证12张以上第二棋盘格与成像平面的夹角要有变化倾斜角度从0度到45度都有覆盖光拍正对相机的图是远远不够的第三棋盘格在画面中的占比保持在三分之一以上太小了角点检测精度差第四拍摄距离要变化让标定板在画面里有大有小这样求解时不同距离的信息能更好地约束焦距和畸变参数。实际操作时可以用手持棋盘格边转动边拍。手机、工业相机、普通USB摄像头都可以作为拍摄设备。如果是手机建议固定分辨率关闭自动对焦锁定焦距用后置主摄拍摄如果镜头有光学防抖OIS拍摄时要让手机尽量稳定防止运动模糊。另外每一张图片都要保证清晰模糊的图宁愿删掉重拍也不要用进标定集合里。2.3 MATLAB环境准备这个实验对MATLAB版本没有严格限制R2020a之后都可以。我自己常年在R2021b和R2023b之间切换操作逻辑基本一致。实验需要用到Computer Vision Toolbox装这个工具箱的原因主要是它提供了棋盘格检测函数和标定参数估计函数。如果你的License只有基础模块在命令行输入ver查看已安装工具箱列表没有的话就需要先安装。如果只是做实验验证原理手写求解核心部分只依赖基础的矩阵运算没有工具箱也能跑通但检测棋盘格角点这一步还是建议用工具箱函数替代。手工做亚像素角点检测不是不行但工程效率和鲁棒性远不如封装好的detectCheckerboardPoints。另外如果是学生或者评测用户可以用学校提供的校园License激活或者使用MATLAB Online不需要额外处理授权问题。3. 基于MATLAB的标定实验全流程3.1 用Camera Calibrator图形化完成标定MATLAB的Camera Calibrator是入门最快的路径我第一遍带学生做实验基本都走这个流程。启动方式很简单在命令行输入cameraCalibrator或者在App选项卡里找到Camera Calibrator点击打开。第一步导入图片。点击Add Images选择你拍好的所有棋盘格图片。如果是视频中截取的帧也可以直接加载视频文件。导入后MATLAB会自动尝试检测每张图里的棋盘格角点并在图上用彩色圆圈标出。如果某张图显示“Not detected”说明这张图质量不达标可以直接从列表里移除。第二步设置棋盘格尺寸。在标定板参数栏里有一个关键输入项——Checkerboard square size单位是毫米。这里填的就是每个格子的物理边长比如30。注意系统默认认为相邻两个内角点之间的距离等于这个值所以一定要填实际测量值。第三步选择畸变模型。MATLAB默认同时估计径向畸变和切向畸变一般保持默认的3 Coefficients3个径向畸变系数k1、k2、k3加2个切向系数p1、p2。对于普通工业镜头和手机镜头这个配置完全够用。只有当你使用鱼眼镜头或者广角畸变极大的镜头时才需要考虑切换到Fisheye模型否则默认选项即可。第四步点击Calibrate开始计算。计算过程通常几秒到几十秒不等取决于图片数量和分辨率。计算完成后界面会自动打开误差分析窗口显示每张图片的重投影误差。这个误差衡量的是利用当前估计的相机参数把棋盘格的3D物理角点重新投影到图像平面和实际检测到的像素坐标之间相差多少像素。误差越小说明参数拟合越好经验上平均重投影误差小于0.5像素就是合格结果。确认结果满意后点击Export Camera Parameters在工作区得到cameraParams对象。这个对象里包含了Camera Intrinsics内参矩阵、Radial Distortion径向畸变系数、Tangential Distortion切向畸变系数以及每张图片对应的Camera Extrinsics外参。后续不管是做畸变校正、还是做视觉测量都可以直接使用它。3.2 手写核心标定代码从单应性矩阵到内外参分解图形化工具能出结果但如果你不知道背后的求解细节换一个数据场景可能就不知道怎么调参数了。我更推荐在跑通App之后再自己写一遍核心算法这样才能真正理解张正友方法的本质。手写代码的第一步是角点检测仍然调用工具箱函数imageFiles imageDatastore(calib_images/); [imagePoints, boardSize] detectCheckerboardPoints(imageFiles.Files); squareSize 30; % 实际格边长单位mm worldPoints generateCheckerboardPoints(boardSize, squareSize);detectCheckerboardPoints返回的是每张图里所有内角点的像素坐标顺序和generateCheckerboardPoints生成的物理坐标一一对应。这里的worldPoints默认z坐标为0因为我们假设棋盘格位于世界坐标系的z0平面上这也是张正友标定法求解时的重要前提。有了多组匹配点之后核心求解可以分为几个步骤第一步估计单应性矩阵H。每一张棋盘格图像都可以建立起棋盘平面到图像平面的单应性变换。数学上世界坐标点(X, Y, 0)映射到像素坐标(u, v)满足s * [u; v; 1] K * [r1, r2, t] * [X; Y; 1]其中K是内参矩阵r1和r2是旋转矩阵的前两列t是平移向量。这个3x3的矩阵H K * [r1, r2, t]就是单应性矩阵每张图片可以估计一个H。估计H可以使用直接线性变换DLT方法将每一对匹配点转化为两个线性方程收集所有方程后利用SVD求解。第二步利用旋转向量的正交性约束求解内参。因为r1和r2是旋转矩阵的两列它们必须满足正交且长度相等。这两个约束条件可以对内参矩阵K的元素构建出齐次线性方程组形式是[ v12^T ] [ b ] 0 [ (v11 - v22)^T ] [ b ]这里的vij是由单应性矩阵元素构造的向量b是内参矩阵元素的二次组合。收集所有图片的约束后用SVD求解b再通过Cholesky分解恢复出内参矩阵K。这一步是整个算法的精华也是初学者最容易卡住的地方——它不直接求解K而是先求解一个由K的元素组合成的中间向量再用数学分解把K还原出来。我当时第一次推导到这里也绕了一会儿建议你拿纸笔把B K^{-T} K^{-1}的对称矩阵形式展开把6个未知数写出来很快就能理清。第三步求解每张图片的外参。一旦内参K已知对每一张图片的单应性矩阵H [h1, h2, h3]旋转向量r1 K^{-1} h1r2 K^{-1} h2r3 r1 x r2平移向量t K^{-1} h3。由于噪声影响实际求出来的R [r1, r2, r3]不一定满足旋转矩阵的严格正交性质通常要通过SVD或者正交化处理比如用[U,~,V] svd(R); R U*V把R拉回旋转矩阵的空间。第四步求解畸变系数。张正友方法采用了一种很实用技巧先用不含畸变的模型求出内外参初值然后基于重投影误差建立关于畸变系数的线性方程组最小二乘求解出k1、k2、p1、p2的初始估计。第五步非线性优化精修所有参数。最后把内外参、畸变系数全部作为优化变量用Levenberg-Marquardt算法最小化所有角点的总重投影误差% 伪代码框架 options optimoptions(lsqnonlin, Algorithm, levenberg-marquardt, Display, iter); params0 [fx, fy, cx, cy, k1, k2, p1, p2, ...]; % 初值拼接 [params_opt, resnorm] lsqnonlin(reprojectionError, params0, [], [], options);这一步虽然看起来只是数学上的精修但在实际标定中非常关键。不做非线性优化的话初值可能已经接近真值但畸变较大时残差依然偏高实验结果也不够理想。整个手写流程下来你会发现其实核心代码并不长关键是每一步的数学含义要清楚。3.3 畸变校正的数学细节标定完拿到畸变系数后最直接的应用就是把畸变图像校正成理想针孔成像的图像。这里的模型需要理解因为很多人在使用undistortImage时不清楚为什么有些图校正后边缘会被裁剪。畸变的数学表达分为两部分。径向畸变由k1、k2、k3描述校正公式为x_corrected x * (1 k1*r^2 k2*r^4 k3*r^6) y_corrected y * (1 k1*r^2 k2*r^4 k3*r^6)其中r是归一化坐标系下像素点到主点的距离。切向畸变由p1、p2描述校正公式为x_corrected x 2*p1*x*y p2*(r^2 2*x^2) y_corrected y p1*(r^2 2*y^2) 2*p2*x*y在MATLAB中可以直接调用[J, newOrigin] undistortImage(I, cameraParams, OutputView, full);OutputView参数有两个常用取值full表示输出完整校正后的图像但边缘可能因为拉伸出现黑色区域same表示保持原图尺寸但边缘区域会被裁掉。实际项目中做特征匹配、目标检测一般用full因为丢失信息更少如果上游算法对图像尺寸有固定要求再用same。4. 从标定结果里读出有用信息4.1 重投影误差怎么看重投影误差是整个标定实验中最重要的质量指标。我用一个简单标准来判断平均重投影误差小于0.1像素非常优秀0.1到0.3像素是正常水平0.3到0.5像素可以接受但需要检查拍摄数据超过0.5像素则说明标定数据里混入了影响较大的坏点建议排查一遍再重新标定。注意我这里说的是平均误差单张图片的误差波动也要关注。如果某张图片的误差明显高于其他图片比如其他图都在0.2像素左右某张图突然到了1像素以上这一般意味着标定板在该图片中出现了局部弯曲、运动模糊或者角点检测错位。处理方式很简单把这张图从集合里删掉重新标定。我在实验中还遇到过一种情况——所有图片的重投影误差都不高但内参明显不合理比如主点坐标偏离图像中心100多个像素这就说明图片集覆盖的姿态不够丰富特别是缺少足够的倾斜视角。4.2 内外参的物理意义解读标定结果里面的参数不是一堆干巴巴的数字每一个都有实际物理含义读懂它们能帮你快速判断标定是否成功。内参矩阵K的形式是K [fx, 0, cx; 0, fy, cy; 0, 0, 1]fx和fy是焦距在像素单位下的表示物理上等于镜头焦距乘以传感器每毫米对应的像素数。一个常见的检验标准是fx和fy的比值应该非常接近1。如果出现fx比fy大好几个百分点可能意味着传感器像素不是正方形或者标定板有透视角度导致的估计偏差。cx和cy是主点坐标理论上应该接近图像分辨率的一半。对1920x1080的图像来说主点应该在(960, 540)附近偏差在20到30像素以内都是正常的。如果偏差过大通常意味着还有部分畸变没有完全被模型拟合掉。外参描述了每张图片拍摄时相机与棋盘格的相对位姿也就是旋转矩阵R和平移向量t。在Camera Calibrator里可以用3D视图查看每个棋盘格在相机坐标系下的摆放位置这个可视化能直观地验证你的拍摄过程是否覆盖了足够多的角度也能检查外参估计有没有明显的跳变。4.3 标定结果对后续视觉任务的影响标定结果的最终价值是服务于后续的视觉任务。以我做过的一个二维码定位项目为例在畸变严重的广角镜头下二维码边缘在图像中呈弯曲状直接用角点检测算位姿误差很大。标定后先用undistortImage校正图像再跑透视变换二维码的定位精度立刻提升了一个量级。这就是标定最直观的价值。如果你是做双目视觉或者结构光测量还需要做双目立体标定即在单目标定基础上进一步求解两台相机之间的相对位姿。MATLAB里可以使用stereoCameraCalibrator完成。但无论单目还是双目单目标定这一步的质量都会直接传递到后面的三维重建里所以别急着赶进度标定这一步值得多花时间把误差降下来。5. 常见问题与排查技巧实录5.1 角点检测失败与对策实验中最常遇到的报错是detectCheckerboardPoints返回空值或者检测到的棋盘格数量不一致。如果命令行提示检测不到棋盘格优先检查三点图片亮度是否均匀、棋盘格在画面中是否太小、图片是否模糊。我曾经遇到过一批图灯光偏暖黄色棋盘格黑色方块在暗光下和深色背景融为一体检测失败率很高。解决办法是在拍摄时保证充足的白光照明尽量让黑色方块和白色底板的对比度高。检不出来的时候先用MATLAB的imshow看一下图片质量缩小棋盘格在画面中的占比往往能立竿见影提升检测成功率。另一个常见的问题是报错“检测到的棋盘格不是一致的尺寸”。这个报错通常出现在你用不同分辨率拍摄了图片或者图片中棋盘格被部分遮挡导致检测到的角点数量不一致。解决办法是把不合格的图片移除或者统一所有图片的分辨率再重新运行。5.2 高畸变广角镜头的特殊处理普通镜头标定很顺利但当你换上一颗广角镜头时问题就来了边缘畸变特别大棋盘格在画面边缘严重弯曲角点检测依然能成功但标定出来的重投影误差很高怎么优化都降不下来。针对这种情况我总结了几条可落地的策略。第一适当增加图片数量广角镜头建议拍到25张以上并且让棋盘格在画面中心和边缘均匀分布特别是要让角点覆盖到畸变最严重的边缘区域第二拍摄时加入更多大倾斜角度的图像让畸变参数有足够的约束条件第三检查畸变模型是否够用普通镜头用3个径向系数如果发现其中k3的置信区间非常大说明模型自由度超出数据表达能力可以改用2个径向系数试试。在MATLAB里可以通过修改Camera Calibrator的Coefficients设置来切换。如果畸变实在太严重比如鱼眼镜头MATLAB里建议直接选用Fisheye标定模式普通针孔模型拟合不了这种级别的非线性畸变。5.3 标定精度的实用建议结合我做过的一系列标定实验整理一个可以直接套用的自检清单检查项合格标准不合格时的处理平均重投影误差0.5像素删除误差大的图片检查棋盘格平整度fx与fy比例0.95-1.05之间检查图片分辨率是否统一传感器像素是否为正方形主点cx、cy接近图像中心偏差10%增加倾斜角度的图片重新标定畸变系数k1数量级在1e-2左右k2在1e-3左右系数异常大时检查畸变模型和图片质量标定板物理尺寸实测值与设定值误差1mm用游标卡尺重新测量修改squareSize参数还有一个容易忽略的点不要把所有图片一次性全部纳入标定集合建议先选6到8张覆盖不同角度的图片做一轮快速标定看单张误差分布再把质量好的图逐步加入。这样做的好处是当最终标定结果不理想时你能很清楚地判断是哪张图拉高了误差而不是面对20多张图一头雾水。我在实际使用中发现画面中棋盘格占比和拍摄姿态的多样性这两个因素对最终标定精度的影响远大于算法选择的影响。很多人总想换更复杂的畸变模型来提升精度其实更有效的方法是重新审视采集数据把姿态覆盖做全把图片拍清楚把标定板贴平整。这三点做好了哪怕用默认参数结果也不会差到哪去。这个实验做到最后我最大的体会是标定这件事原理上虽然绕但一旦亲手把代码流程跑通再把图形化工具和手写结果对照一遍整个相机模型就在脑子里扎根了后面再做任何视觉相关的项目心里都有底。
返回列表