Python+OpenCV相机标定全流程:从原理到实操搞定内参与畸变

Python+OpenCV相机标定全流程:从原理到实操搞定内参与畸变 做相机标定这件事我其实一开始是抗拒的。打印一张棋盘格对着屏幕举来举去拍几十张照片然后跑一段脚本等结果——听起来简单但真正上手之后才发现里面值得琢磨的细节非常多而且每一步的操作质量直接决定了最后那组内参能不能用。尤其是用Python来做这件事语言本身很简单但背后的成像原理、坐标系转换、畸变模型如果不弄清楚代码跑通也只是跑通而已换一个相机、换一个场景马上就会露馅。这篇文章把我自己从零开始用Python做相机标定的完整过程整理出来包括我怎么设计采集方案、怎么写标定脚本、怎么判断结果靠不靠谱以及我踩过的那些坑。内容适合正在入门视觉SLAM、做机器人抓取、搞双目测距或者做视觉测量的朋友哪怕你之前完全没接触过标定照着这篇文章的思路也能把内参标出来而且知道每一行代码在干什么。1. 内容整体设计与思路拆解1.1 为什么选择Python OpenCV做标定市面上做相机标定的工具其实不少MATLAB的Camera Calibrator做得非常精致ROS的camera_calibration用起来也方便工业上还有Halcon这种商业方案。但我最终还是用Python配合OpenCV自己写了一套原因是三个字可控性。用现成工具虽然省事但很多关键参数是封装好的比如棋盘格角点检测的亚像素精度、标定板的尺度信息怎么传入、畸变模型选了哪几种系数这些在图形界面里点几下就过去了你很难真正感知到标定过程的每一步发生了什么。而OpenCV把整个流程的函数都暴露出来了cv2.findChessboardCorners、cv2.cornerSubPix、cv2.calibrateCamera每一个环节都能看到输入输出也能单独调试这对我理解标定的本质帮助非常大。另外Python做这件事的生态确实太舒服了。OpenCV负责图像处理NumPy负责矩阵运算Matplotlib负责可视化标定板角点检测结果。这三个库装好之后整个标定流程从采集、处理到输出报告不到200行代码就能搞定。对于做视觉相关开发的人来说这套组合是最低门槛、最高性价比的起点。1.2 整体流程设计我把我自己的标定流程拆成了五个阶段这也是我认为比较科学的执行顺序准备标定板打印一张棋盘格测量格子边长贴到硬板上。采集标定图像从不同角度、不同距离拍摄标定板保证视野覆盖和姿态多样性。角点检测与可视化用OpenCV检测棋盘格角点把检测结果叠加在图像上剔除不合格的帧。执行标定计算调用cv2.calibrateCamera解算内参、畸变系数和外参。结果评估与验证计算重投影误差做一次去畸变验证确认结果可用。这五个阶段看起来简单但每个阶段都有不少容易踩的坑。后面我会逐个展开讲尤其是采集阶段很多人标定结果不好根本原因不是代码有问题而是照片拍得不合格。1.3 相机标定到底在解决什么问题在讲具体操作之前我觉得有必要把“标定到底在干什么”这件事说明白不然代码写完了你也不知道自己在解什么方程。相机成像的过程本质是把三维世界中的点映射到二维图像平面上。这个映射关系可以拆成两步第一步世界坐标系下的点通过刚体变换旋转矩阵R和平移向量T变到相机坐标系下第二步相机坐标系下的三维点通过针孔成像模型投影到像素平面。在这两步里面相机本身的固有属性也就是焦距、主点位置、像素尺寸这些是不随拍摄姿势变化的统称为内参而相机在世界坐标系中的位置姿态也就是R和T每次拍摄都不同称为外参。现实中的镜头不是完美针孔光线经过透镜会发生折射导致实际成像位置和理想投影位置之间存在偏差这就是畸变。畸变主要分径向畸变和切向畸变两类OpenCV里用k1、k2、k3三个径向系数和p1、p2两个切向系数来描述。所以相机标定要做的事情就是通过拍摄一组已知尺寸的标定板图像建立“已知三维点—图像点”的对应关系然后解算出内参矩阵、畸变系数以及每张图对应的外参。这就是整个标定的数学本质。2. 核心原理与关键概念详解2.1 四个坐标系与内参矩阵做标定之前强烈建议先把四个坐标系的关系捋清楚世界坐标系、相机坐标系、图像物理坐标系、像素坐标系。世界坐标系是你自己定义的参考系标定板上的角点坐标就是在世界坐标系下定义的。比如把棋盘格平面当成Z0的面每个角点的三维坐标就是(m×格子边长, n×格子边长, 0)。相机坐标系是以相机光心为原点、光轴为Z轴的坐标系。图像物理坐标系以光轴与成像平面的交点为原点单位是毫米。像素坐标系以图像左上角为原点单位是像素。世界坐标系的点经过外参变换到相机坐标系再经过透视投影到图像物理坐标系最后经过像素缩放和平移得到像素坐标。把这一串过程写成矩阵形式就得到了经典的相机模型s * [u, v, 1]^T K * [R | T] * [X, Y, Z, 1]^T其中K就是内参矩阵K [[fx, 0, cx], [ 0, fy, cy], [ 0, 0, 1]]fx和fy分别是x和y方向上的归一化焦距单位是像素。如果像素是正方形fx和fy理论上相等如果不等说明像素不是正方形这在一些工业相机或者手机摄像头上是可能的。cx和cy是主点坐标理论上应该在图像中心附近但实际中会有一定偏移。这里有一个容易混淆的地方fx f / dx其中f是物理焦距毫米dx是每个像素的物理尺寸毫米/像素。所以你看到fx的单位是像素它实际上是物理焦距除以像素尺寸得到的归一化结果。这就是为什么同样的相机分辨率不同标定出来的fx也不同因为dx变了。2.2 畸变模型与OpenCV的实现细节畸变的本质是镜头的光学缺陷完全消除畸变是不可能的只能尽量建模补偿。OpenCV采用的是Plumb Bob模型也就是Brown-Conrady模型的简化版用五个参数来描述畸变k1、k2、k3三个径向畸变系数p1、p2两个切向畸变系数。径向畸变是光线在透镜边缘弯曲得更厉害导致的表现为画面边缘的直线变得弯曲。k1和k2是主要项k3一般用于鱼眼镜头等大畸变场景。切向畸变则是因为透镜和成像平面不平行导致的表现为某些区域成像被轻微拉伸。畸变矫正的公式是这样的假设(x, y)是归一化平面上的理想坐标r^2 x^2 y^2那么畸变后的坐标x_dist和y_dist为x_dist x * (1 k1*r^2 k2*r^4 k3*r^6) 2*p1*x*y p2*(r^2 2*x^2) y_dist y * (1 k1*r^2 k2*r^4 k3*r^6) p1*(r^2 2*y^2) 2*p2*x*y在OpenCV的calibrateCamera里面你可以通过flags参数控制畸变模型的复杂度。比如只求解k1和k2可以用cv2.CALIB_FIX_K3如果相机本身的畸变很小可以cv2.CALIB_ZERO_TANGENT_DIST把切向畸变置零。我的经验是对于常见的工业镜头和普通USB摄像头用默认的五个畸变系数即k1、k2、p1、p2、k3全解就好。如果解出来的k3非常小或者重投影误差没有明显变化再考虑固定k3为0这样可以减少参数空间提高数值稳定性。2.3 张正友标定法的核心思想现在做相机标定几乎没有人用传统的大靶标三维标定法了大家都在用张正友标定法。这个方法1998年提出到现在二十多年依然是行业标准OpenCV、MATLAB、ROS里实现的都是它。张正友标定法的聪明之处在于它只需要相机拍摄多张不同姿态的平面棋盘格图像不需要知道相机和标定板的相对运动。它把标定板所在的平面当成Z0的世界坐标平面这样每个角点的三维坐标就简化成(px格子边长, py格子边长, 0)然后利用单应性矩阵H建立图像点和棋盘格平面点之间的映射。每张棋盘格图像都能求出一个单应性矩阵这个矩阵是内参和该视角外参的乘积。多个视角的单应性矩阵放在一起就能构造出关于内参的线性约束方程组先求出内参的初始值再用极大似然估计对所有参数包括畸变系数进行非线性优化迭代得到最终结果。理解这一点有什么用它直接告诉我们标定效果的关键取决于棋盘格图像的数量和姿态多样性。如果你只是把标定板放在正前方平移着拍所有图像的单应性矩阵都很接近约束方程组的病态程度就会很高解出来的内参就会不稳定。最理想的做法是让标定板在画面中倾斜、旋转、靠近、远离覆盖画面的各个区域。3. 实操过程与核心环节实现3.1 环境准备与依赖安装我的环境是Windows 11 Python 3.10但实际上这套代码在Ubuntu 18.04/20.04、macOS上都没问题只要Python版本在3.8以上就行。需要安装的依赖就三个pip install opencv-python numpy matplotlib有个细节值得说一下opencv-python这个包在PyPI上的维护非常活跃直接用pip install opencv-python装的就是最新的稳定版。如果你还需要处理视频文件建议再装一个opencv-contrib-python它包含了更多扩展模块。不过标定这件事标准的opencv-python足够了。如果你用的是Anaconda也可以用conda install -c conda-forge opencv numpy matplotlib装完之后验证一下python -c import cv2; print(cv2.__version__)能打印出版本号就说明环境OK。我建议不要在这个环节省时间版本冲突的问题看似小但排查起来很闹心。3.2 标定板准备与打印标定板是整个标定的基准它的质量直接影响精度。我自己用的是一条8×6的棋盘格internal corners是7×6格子边长24mm在A4纸上打印然后贴在硬纸板上。关于标定板有几个建议格子数量不要用太大的棋盘格8×6、9×7这种比较常见。格子太多远处的图像角点太密集检测精度反而下降。格子太少一张图提供的约束不够。格子尺寸视你的应用场景而定。我的相机主要是做近距离的机器人抓取工作距离大概0.3米到1米24mm的格子比较合适。如果你做的是远距离测量比如AR测距可能需要更大的格子。平整度打印出来的纸一定要贴在平整的硬板上不能弯折、不能起皱。标定板不平就相当于标定板的三维点坐标定义错了标定结果一定不准。我见过有人直接用软纸拿手上拍的效果非常差。打印精度最好用打印机100%比例输出不要用“适应页面”这种缩放选项否则格子尺寸就不是你量出来的那个值了。打印完之后用尺子量几个格子的总宽度除以格子数确认实际尺寸和标称尺寸一致。3.3 采集标定图像的实操规范这一步是整个标定流程中最容易翻车的地方。很多人随便拍个十几张就跑了结果标出来的内参一塌糊涂。我自己总结了一套采集规范严格遵守的话标定效果基本不会差。第一采集数量要够。最少15张建议20到30张。数量多一些标定结果的稳定性会好很多。但也不是越多越好30张以上再增加图像对结果的影响就不明显了。第二姿态要多样。让标定板在画面中左右移动、上下移动覆盖画面的中心和边缘区域。让标定板绕X轴、Y轴倾斜倾斜角度在20度到45度之间变化。如果完全正对相机角度变化太小求解的退化风险很大。改变拍摄距离让标定板在画面中占的面积有大有小。这样做的目的是让标定板在图像中有不同的尺度约束更充分。不能只在一个方向上变化比如只绕X轴转那样的话某些内参分量可能约束不足。第三画面要清晰。拍照时不要手抖保证棋盘格角点锐利。用工业相机的话曝光时间要适当尽量不要出现过曝或者欠曝否则角点检测的亚像素精度会下降。第四标定板尽量充满画面。让棋盘格在对角线方向上占画面面积的一半以上这样每张图能够提供足够的角点分布。第五如果是用USB摄像头或手机拍摄要注意固定相机的内参状态。不要开自动变焦不要开美颜和滤镜不要中途改动分辨率。这些都会导致内参在采集过程中发生变化标定结果自然不准。采集到的图像建议统一放到一个文件夹里命名用序号就行我一般叫calib_001.jpg、calib_002.jpg这样方便后续脚本批量读取。3.4 完整的Python标定代码实现下面就是我自己用的标定脚本注释写得比较详细。整体逻辑是遍历文件夹里的所有图像→检测棋盘格角点→提取亚像素角点→保存有效的图像点和对应的三维点→调用calibrateCamera计算内参和畸变系数→评估重投影误差。import cv2 import numpy as np import glob import os # 棋盘格参数内部角点数 CHESSBOARD_SIZE (7, 6) # 注意不是格子的数量是内部角点的行数和列数 SQUARE_SIZE 24.0 # 格子边长单位mm # 1. 准备三维点棋盘格平面设为z0 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 obj_points [] # 每张图的三维点 img_points [] # 每张图的二维像素点 valid_images [] images sorted(glob.glob(calib_images/*.jpg)) for fname in images: img cv2.imread(fname) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 2. 检测棋盘格角点 ret, corners cv2.findChessboardCorners(gray, CHESSBOARD_SIZE, None) if ret: # 3. 亚像素细化 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_sub cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) obj_points.append(objp) img_points.append(corners_sub) valid_images.append(fname) # 4. 可视化方便确认角点检测是否正确 img_show cv2.drawChessboardCorners(img.copy(), CHESSBOARD_SIZE, corners_sub, ret) cv2.imshow(Chessboard, img_show) cv2.waitKey(100) else: print(f角点检测失败: {fname}) cv2.destroyAllWindows() print(f有效图像数: {len(valid_images)} / {len(images)}) # 5. 标定核心 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None) print(内参矩阵:) print(mtx) print(畸变系数:) print(dist) # 6. 重投影误差评估 total_error 0 for i in range(len(obj_points)): imgpoints2, _ cv2.projectPoints(obj_points[i], rvecs[i], tvecs[i], mtx, dist) error cv2.norm(img_points[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) total_error error mean_error total_error / len(obj_points) print(f平均重投影误差: {mean_error:.4f} px) # 7. 保存内参 np.savez(camera_params.npz, mtxmtx, distdist)这段代码跑完之后终端会输出内参矩阵、畸变系数和平均重投影误差。我一般会把camera_params.npz存起来后面做去畸变、做双目标定、做PnP求解都用得上。3.5 如何验证标定结果是否靠谱标定跑完不等于结束结果合不合理需要验证。我从三个方面来检查第一看内参矩阵的数值是否合理。fx和fy应该比较接近都是几百到几千像素量级具体数值取决于相机分辨率和镜头焦距。如果fx和fy差太多比如超过10%要么是镜头本身是变形的要么是像素尺寸在x和y方向差异很大要么是标定过程出了问题。cx和cy应该在图像中心附近。比如1280×960的图像cx应该在640附近cy在480附近。如果偏离太多比如超过50像素要么是传感器安装偏差大要么是标定图像质量不行。第二看畸变系数是否在合理范围。k1一般在-0.1到0.1之间k2的量级通常在1e-1到1e-3左右。如果解出来k1是-5或者k2是15这种离谱数值说明标定过程有问题可能是角点检测不准也可能是图像数量太少或者姿态不够多样。第三看平均重投影误差。这个指标多少算好跟应用场景有关。一般来说小于0.5像素很好可以用于高精度三维重建。0.5到1像素可以接受大多数视觉应用都够用。大于1像素需要检查采集质量和角点检测重新标定。重投影误差的意义在于它是把标定出的内外参拿回去把三维点重新投影到图像上看和原来的二维点差多少像素。这个误差综合反映了标定的整体精度是评价结果最直观的指标。4. 常见问题与排查技巧实录4.1 角点检测失败或检测出乱序角点这是我被问得最多的问题。cv2.findChessboardCorners返回False的常见原因有几种棋盘格没有完整出现在画面中。解决方法是调整角度或距离保证所有内部角点都在画面里。边缘刚好被截断的图直接删掉就好不要勉强。反光和阴影导致了对比度变化。棋盘格在光线不均匀的时候有些角点附近会出现光斑检测算法容易找错。解决方法是选择光线柔和均匀的环境或者在标定时稍微转动标定板角度避开反光。图像过曝或欠曝。角点的梯度信息丢失了亚像素位置就没有办法精确定位。还有一类问题是检测结果看起来“歪了”角点顺序错乱。这通常是因为棋盘格太靠近画面边缘或者画面的透视畸变过大。处理办法是尽量让棋盘格在画面中心区域拍摄倾斜角度不要超过45度。4.2 重投影误差很大怎么排查如果平均重投影误差超过了1像素我一般按下面的顺序排查检查标定板的实际尺寸和代码里的SQUARE_SIZE是否一致。这是最隐蔽的问题格子尺寸差几毫米标出来的fx就偏几毫米的像素当量。检查有没有把宽度单位和长度单位混用了。我在mm、cm之间切换过几次搞得fx差10倍这类低级错误很容易犯。检查有没有混入模糊图像。有些图像检测成功但实际是糊的角点位置根本不准。这种图会明显拉高整体误差需要删掉重跑。检查棋盘格定义和实际打印是否一致。比如我代码里写(7,6)实际打印是8×6的格子内部角点就是7×6这时候如果填了(6,5)检测虽然可能成功因为OpenCV检测的是黑白交界点但对应的坐标关系全乱了标定结果不可能对。如果以上都没问题那就增加图像数量、丰富姿态重新采一轮。很多时候标定效果不好不是方法不对是数据质量不够。4.3 去畸变效果验证标定完内参之后我强烈建议做一次去畸变验证。把之前拍的标定图像或者其他纹理比较丰富的图像做一次畸变矫正看看效果。import cv2 import numpy as np # 加载标定结果 data np.load(camera_params.npz) mtx data[mtx] dist data[dist] img cv2.imread(calib_images/calib_001.jpg) h, w img.shape[:2] # 直接重映射 dst cv2.undistort(img, mtx, dist, None, mtx) # 或者用initUndistortRectifyMap remap适合视频流的逐帧处理 mapx, mapy cv2.initUndistortRectifyMap(mtx, dist, None, mtx, (w, h), cv2.CV_32FC1) dst2 cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR) cv2.imshow(Original, img) cv2.imshow(Undistorted, dst) cv2.waitKey(0) cv2.destroyAllWindows()看什么看画面的直线是不是变直了。你把一面砖墙、一扇窗户、或者打印出来的直线图案放在画面边缘如果原本是弯的去畸变之后变直了说明畸变模型是有效的。这里有一个很多人不知道的细节cv2.undistort默认的输出内参和输入内参一样所以去畸变后的图像尺寸不变。如果你用的是较大畸变的镜头矫正后的图像边缘可能出现明显的像素拉伸或空洞这是正常现象。为了减少边缘信息的丢失也可以自己计算一个新的内参矩阵来做优化OpenCV提供了cv2.getOptimalNewCameraMatrix可以调整alpha参数在保留全部像素和裁剪边缘之间做权衡。newcameramtx, roi cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h)) dst cv2.undistort(img, mtx, dist, None, newcameramtx)alpha1表示保留所有原始像素alpha0表示只保留有效像素区域边缘无效区域会被裁剪掉。4.4 单目标定和双目标定的衔接如果你接下来要做双目视觉单目标定只是第一步。双目标定需要在此基础上再标定两个相机之间的相对位姿旋转矩阵R和平移向量TOpenCV提供了cv2.stereoCalibrate接口输入就是两个相机各自的标定图像和已经得到的单目内参。我的建议是单目标定这一步一定要打好基础内参质量不过关双目标定算出来的外参也不会准。另外在采集双目图像的时候左右相机必须同步采集不能左边拍一张右边再拍一张那样的话标定板轻微移动都会导致两张图对应不上外参就废了。4.5 关于不同相机类型的经验补充这几年我标定过罗技C920这种普通USB摄像头、海康工业面阵相机、Intel RealSense D435i这种深度相机还有一些手机摄像头。每种设备都有一些要注意的地方。普通USB摄像头用Python标定是最快的。这类相机像素尺寸大畸变也相对明显标定完成后去畸变的改善非常显著。注意一定要关闭自动对焦手动把焦距固定住否则内参一直在变。工业相机通常可以手动控制曝光、增益、分辨率标定板图像质量好控制标定精度也最高。这类相机注意镜头和相机之间的接口深度C接口镜头和CS接口镜头的法兰距不同装不严可能导致像面位置变化内参就不稳定。RealSense这类深度相机除了RGB内参还有红外相机内参和双目外参需要标定。但拿到RGB内参这一步是完全一样的只是它的RGB镜头通常畸变比较小标定完之后去畸变效果可能看起来不明显但重投影误差依然能反映精度。5. 一些实用的流程优化建议5.1 采集工具的快速做法如果你手头没有工业相机用笔记本自带摄像头或者手机USB摄像头都可以做练习。不过用Python写一个采集脚本比手动按键拍照要高效得多可以用OpenCV打开摄像头按空格键保存当前帧按q退出。import cv2 cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) cap.set(cv2.CAP_PROP_FPS, 30) count 0 while True: ret, frame cap.read() if not ret: break cv2.imshow(Capture, frame) key cv2.waitKey(1) 0xFF if key ord( ): fname fcalib_images/calib_{count:03d}.jpg cv2.imwrite(fname, frame) print(fSaved: {fname}) count 1 elif key ord(q): break cap.release() cv2.destroyAllWindows()这个脚本我用了很久好处是边移动标定板边按空格能很快攒够二三十张图。如果你需要手动调曝光可以再加上曝光参数控制的代码但大部分情况自动曝光就行。5.2 自动化剔除不合格图像在实际采集中总会有几张图像角点检测成功但质量一般手动一张一张看太麻烦。我后来写了一个小逻辑在保存角点检测结果的时候同时检查棋盘格角点是否覆盖了图像的足够区域以及角点分布是否均匀。一个简单的做法是计算检测到的角点的凸包面积占整张图像面积的比例。如果占比太低说明标定板在画面中太小这种图对约束的贡献有限如果占比太高比如超过95%可能标定板已经超出了画面范围检测结果可能有误。设定一个阈值区间自动过滤这些图像。还有一个更聪明的办法标定完成后把每张图的单图重投影误差打印出来超过平均误差一倍以上的图直接删掉再重新标定。重复两轮之后重投影误差通常会下降不少。这个“迭代剔除”法在学术上就是鲁棒估计的思想实操中效果立竿见影。5.3 标定结果如何落地使用标定出的内参最终要用于哪里决定了你对精度的要求。如果只是做可视化投影、AR叠加重投影误差1个像素完全够用。如果做三维重建、双目测距建议误差控制在0.5像素以内。如果做高精度工业测量那不仅要控制采集质量最好用更复杂的标定板比如圆形阵列标定板OpenCV里也有cv2.findCirclesGrid它比棋盘格有更好的亚像素定位精度。内参的使用场景最常见的有调用cv2.undistort或cv2.remap做图像矫正通过PnP求解相机位姿比如ArUco码定位双目视觉中做立体校正和深度计算。这些场景都依赖准确的内参内参错了后面全是白算。我自己最常用的是PnP位姿估计主要做机械臂抓取的视觉引导。这种场景下内参稍微偏一点远距离的位姿误差就会被放大。所以我对标定结果的验证一直很谨慎重投影误差只是一方面我还习惯用已知尺寸的物体在实际距离上做一次测量验证。6. 从单目到更复杂的标定如果你已经顺利完成了单目标定那么接下来可以顺着这条线继续深入。双目立体标定的目标有两个一个是标定左右相机各自的畸变和内参另一个是标定两个相机之间的旋转矩阵R和平移向量T。OpenCV的cv2.stereoCalibrate可以同时完成这两件事输入就是左右相机同步拍的棋盘格图像以及你分别标出来的单目内参。双目标定完成之后用cv2.stereoRectify做立体校正可以让左右图像的极线对齐后续做立体匹配的效率会高很多。D435i这类设备出厂自带内参但如果你要把它用在特殊环境里重新标定一遍也不亏毕竟出厂参数是在理想环境下测的而实际使用的温度、镜头装配状态都会有差异。另外还有相机和IMU的联合标定这个复杂程度又上了一个台阶牵扯到时间戳对齐、旋转外参初始化、尺度估计等一系列问题。它的基础依然是相机内参标定内参如果不准联合标定出来的外参也很难可靠。所以把单目内参这场仗打好是整个视觉系统中最基本也最重要的一步。我在实际项目中的体会是标定这件事看起来只要会调用一个函数就行但真正决定效果的是采集质量、参数理解、结果验证这些“慢功夫”。Python帮我们简化了代码层面的负担但原理上的东西一点都不能省。如果你照着这篇文章做了一遍手里的内参能在真实项目中稳定使用那我这篇文章就没白写。