简介:移动机器人视觉中的相机标定与定位算法详解,是一份面向机器人视觉、无人机导航与增强现实领域研究人员和技术人员的PDF格式技术文档。内容从相机模型的数学推导入手,系统讲解Tsai标定法、张氏标定法、DLT算法以及非线性优化校准参数等关键技术;同时讨论全景相机等非常规相机的建模方式,并介绍如何使用统一球形模型同时处理透视相机与全方向相机。文档还对比了现代P3P与EPnP方法在不同场景下的性能差异,配有大量数学公式与图形解释,便于读者深入理解标定与定位算法背后的原理细节。资源包共含1个PDF文件,大小约3.44MB,已有112人学习下载。适合用于系统掌握相机标定与定位的基础理论,也可作为移动机器人、无人机及AR项目在实际工程实现中的重要参考。 做移动机器人,不管是搞AGV、扫地机还是园区配送车,只要走视觉这条路,相机标定一定是绕不开的第一道坎。我见过不少团队,SLAM代码调了好几个月,精度死活上不去,最后回头排查,问题出在最基础的相机标定上——内参偏了,畸变没修干净,后端的定位算法再好也是白搭。
这篇内容想把我这些年做移动机器人视觉定位时攒下的标定经验捋一遍,从成像模型讲起,到单目、双目、相机-IMU联合标定的实操流程,再到标定结果怎么影响最终的视觉定位精度,最后附一份踩坑记录。适合刚入门机器人视觉的学生,也适合做实际产品但标定流程一直没规范的工程师,看完应该能把标定这条线彻底理清楚。
1. 相机标定到底在标什么:先把成像模型搞明白
1.1 针孔模型和四个坐标系
相机标定表面上看起来是“算一组数”,但本质是在求解一个成像模型。绝大多数工业相机、RGB相机甚至部分红外相机,都符合针孔成像模型——光从物体表面反射,穿过镜头中心(光心),落在感光元件上,形成一幅图像。
整个成像过程涉及四个坐标系:世界坐标系、相机坐标系、图像物理坐标系、像素坐标系。世界坐标系是人为定义的一个基准,描述物体在三维空间中的位置;相机坐标系以光心为原点,Z轴指向相机前方;图像物理坐标系的原点在光轴与成像平面的交点,单位是毫米;像素坐标系则是我们看到的图像数组,原点在左上角,单位是像素。
标定要做的事,就是求出三维空间点映射到二维像素点的那套数学关系。这个关系可以拆成两部分:相机在世界坐标中的位姿,也就是外参(旋转矩阵R和平移向量t),还有相机自身的固有属性,也就是内参(焦距fx、fy,主点坐标cx、cy,以及畸变系数)。
对内参和外参的理解有个很容易混淆的点:外参描述的是“这个相机在哪儿、朝哪儿看”,内参描述的是“这个相机本身怎么看东西”。移动机器人定位时,视觉SLAM和视觉Tag定位解算的其实是外参,但前提是内参必须准确。内参一旦偏了,后续所有三维重建和位姿求解都会跟着错。
1.2 畸变模型:标定真正要解决的硬骨头
现实中镜头不是完美的针孔,光线经过透镜会发生折射,导致成像变形。这就是畸变。畸变主要分两种:径向畸变和切向畸变。
径向畸变来源于镜头的曲率,表现为直线成像后变成曲线。远离图像中心的点,畸变越明显,这也是为什么用广角镜头拍棋盘格时,边缘的格子会明显弯曲。切向畸变则是镜头与成像传感器不完全平行导致的,表现为成像平面上的梯形或旋转偏移。
在OpenCV和Kalibr这些工具里,径向畸变通常用k1、k2、k3三个系数描述,切向畸变用p1、p2两个系数描述。标定算法本质上就是通过拍摄多张已知图案的标定板图像,建立约束方程,迭代求解出这些畸变系数,再做去畸变处理。
一个需要特别提醒的点:畸变校正不是“一刀切”的事。标定得到畸变系数只是第一步,还需要在图像处理管线里做remap操作,或者直接把畸变模型交给后端优化器去处理。SLAM系统里,如果前期只在预处理阶段做了去畸变,而后端用的特征点坐标没有同步更新,就会造成前后端数据不一致,定位精度同样会崩。这是很多团队容易忽视的管线一致性坑。
1.3 为什么要盯住重投影误差
标定结果好不好,不能凭肉眼判断,要用数值说话。常用的指标是重投影误差(reprojection error)。简单说,就是把已知的三维标定点投影回图像平面,计算投影点和实际检测到的角点之间的像素距离。误差越小,标定越准。一般单目标定重投影误差控制在0.1像素以内算优秀,0.2到0.3像素是常态,超过0.5像素就建议重新标定了。
这个指标在Matlab标定工具箱、Kalibr的输出里都有。但我建议不要只看它给的平均值,最好把每张图的误差都翻一遍。很多时候平均值看着还行,仔细看却是某几张边缘图拉高了整体方差,这说明畸变模型没有把镜头边缘完全刻画好,或者是标定板拍摄角度太极端造成的。这种“隐性不准”在之后的定位环节会悄悄侵蚀精度。
2. 标定工具箱怎么选:Kalibr、Matlab、OpenCV与ZED 2i实战对比
2.1 主流标定工具横向对比
做标定,工具选型决定了效率边界。我列一个对比表,基本覆盖了移动机器人项目里最常见的几类标定工具。
| 工具 | 支持类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| Kalibr | 单目、双目、相机-IMU联合 | 标定精度高,支持多种畸变模型,命令行批处理,适合自动化流程 | 配置复杂,依赖ROS | 科研、SLAM系统、视觉-惯性融合 |
| Matlab Camera Calibrator | 单目、双目 | GUI可视化,操作直观,文档完善 | 自动化能力弱,不适合嵌入式端标定 | 学习入门、快速验证 |
| OpenCV | 单目、双目 | 跨平台,Python/C++接口,可嵌入自研流程 | 需要自己写采集和评估逻辑 | 嵌入式产品化、定制化标定 |
| ZED SDK | 双目(ZED系列) | 出厂自带标定,开箱即用 | 仅支持自家相机 | 直接用ZED相机的项目 |
2.2 标定板选型:棋盘格、Apriltag与ChArUco
标定板是标定过程中的“基准尺”,选错了直接影响角点检测稳定性。最常见的三种:棋盘格(Checkerboard)、Apriltag、ChArUco。
棋盘格用得最广,角点检测简单可靠,OpenCV里的findChessboardCorners非常成熟。但棋盘格有个致命问题:它无法辨识方向,拍摄时如果标定板旋转了180度,算法依然能检测出角点,但对应关系会错乱。解决方式是在棋盘格旁放一个明显的方向标记,或者拍摄时保证标定板始终朝向相机。
Apriltag自带ID和方向信息,Kalibr默认推荐使用Apriltag或Aprilgrid作为标定靶。每个tag都是一个独特的编码图案,检测时能直接读出ID和角点对应关系,在大角度拍摄时依然能保持稳定检测。代价是角点数量不如棋盘格多,标定单目时,一般建议拍摄15到20帧以上的图像来补偿。
ChArUco是棋盘格和ArUco的结合体,每个格子交点附近嵌入了ArUco编码。它既保留了棋盘格密集角点的优势,又具备方向矫正能力。如果自建标定流程,我比较推荐ChArUco,检测鲁棒性和标定精度都比较均衡。
2.3 ubuntu24.04 + ROS2 + ZED 2i的选型建议
热词里提到ubuntu24.04联合ZED 2i标定,这个组合现在很常见。ZED 2i出厂自带标定数据,在大多数场景下可以直接使用SDK里的内参。但如果你要做高精度的视觉定位,或者相机受过磕碰、温度变化大,我仍然建议自己重新标定一遍。
ZED 2i在ROS2环境下的标定流程一般是:先安装ZED SDK,再安装zed-ros2-wrapper,通过SDK获取左右目图像流,然后用自己采集的标定包走Kalibr或者OpenCV完成重标定。ZED 2i的左右目基线是120mm,出厂内参基本精准,所以重标定通常只需要微调畸变参数,工作量不大,但能让后续的深度估计和视觉定位更有底。
值得单独说明的是,Ubuntu 24.04比较新,ROS2版本对应的是Jazzy。安装ZED SDK前,一定要确认版本兼容性,否则容易遇到CUDA依赖冲突。我一般建议先在干净环境里装好ZED SDK,再装ROS2 wrapper,顺序反了容易产生运行时崩溃。
3. 实操流程:单目、双目与相机-IMU联合标定的完整步骤
3.1 单目相机标定:从图像采集到内参输出
单目标定是所有标定的基础。以OpenCV Python为例,给出一个可以完整跑的标定流程。
第一步,采集标定板图像。打印一张10x7的棋盘格,格子边长比如30mm,用相机从不同角度、不同距离、不同倾斜度拍摄30到40张。采集时要注意:标定板要平整,不要拿在手里晃;相机曝光要合适,避免反光和过曝;拍摄角度要覆盖画面中心和边缘,尤其是四角,否则边缘畸变参数标不准。
第二步,角点检测。用cv2.findChessboardCorners检测棋盘格角点,得到像素坐标,再用cv2.cornerSubPix做亚像素细化。
import cv2 import numpy as np import glob # 棋盘格尺寸(内角点数) checkerboard = (9, 6) # 格子边长,单位mm square_size = 30.0 objp = np.zeros((checkerboard[0] * checkerboard[1], 3), np.float32) objp[:, :2] = np.mgrid[0:checkerboard[0], 0:checkerboard[1]].T.reshape(-1, 2) objp *= square_size obj_points = [] img_points = [] 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, checkerboard, None) if ret: obj_points.append(objp) corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) img_points.append(corners2) cv2.drawChessboardCorners(img, checkerboard, corners2, ret) cv2.imshow('img', img) cv2.waitKey(100) cv2.destroyAllWindows() ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None) print("内参矩阵:\n", mtx) print("畸变系数:\n", dist)这里有一个新手容易忽略的细节:棋盘格尺寸传的是内角点数,不是格子个数。比如你打印的是10x7的格子,内角点是9x6,代码里要写(9, 6),写错的话角点数量对不上,检测会直接失败。
3.2 双目相机标定:立体校正与基线
双目标定比单目多一步:除了左右目各自的内参和畸变系数,还需要计算左右相机坐标系之间的旋转矩阵R和平移向量T,也就是“外参中的相对位姿”。这个相对位姿决定双目立体匹配时极线校正的效果,也是深度计算的基础。
OpenCV里可以用cv2.stereoCalibrate做双目标定,输入左右目的角点对,输出R、T、本征矩阵E和基础矩阵F。标定完成后,用cv2.stereoRectify计算左右目的校正映射,再配合cv2.initUndistortRectifyMap生成remap查找表。
实际操作中,双目标定的坑集中在采集环节。左右目必须同步采集,否则标定板稍微一动,左右图对应点的位置就不一致,标定出来的双目外参会明显偏大。ZED 2i这类硬件同步相机问题不大,但如果是两个独立USB相机做双目,最好用硬件触发或者软件同步采集,不能直接两路各自抓图完事。
还有一个常见问题是左右图像亮度差异。双目标定对亮度不敏感,但后续立体匹配对亮度有一致性要求。如果左右目曝光差异很大,建议在标定采集时就统一曝光时间,避免后期匹配质量受影响。
3.3 相机-IMU联合标定:Kalibr的标准流程
移动机器人做VIO或视觉-惯性SLAM时,相机和IMU之间的外参(camera-IMU extrinsic)是必须标定的。最常用的工具是苏黎世联邦理工开源的Kalibr。
使用Kalibr的大致流程分三步。
第一步,准备标定板配置文件。Kalibr使用YAML描述标定板,比如Aprilgrid:
target_type: 'aprilgrid' tag_cols: 6 tag_rows: 6 tag_size: 0.08 tag_spacing: 0.3第二步,录制bag包。用ROS2录制话题时,需要有图像话题和IMU话题。录制过程中,手持标定板在相机前做缓慢的旋转和移动,让IMU充分激发加速度计和陀螺仪。录制时间一般在40秒到2分钟,太短数据不足,太长容易产生漂移。IMU频率和图像频率都会被读进标定算法,所以要确保话题消息的时间戳准确。
第三步,分别执行相机标定和相机-IMU联合标定。
相机标定命令:
kalibr_calibrate_cameras --target april_6x6.yaml --bag cam_imu.bag \ --models pinhole-radtan pinhole-radtan \ --topics /cam0/image_raw /cam1/image_raw相机-IMU联合标定命令:
kalibr_calibrate_imu_camera --target april_6x6.yaml \ --cam camchain.yaml --imu imu.yaml --bag cam_imu.bag联合标定输出的是相机到IMU的变换矩阵T_cam_imu,以及IMU的噪声密度和随机游走参数。这些参数会直接写进VINS-Fusion、ORB-SLAM3或OpenVINS的配置文件中。标定完成后,建议在相同环境下重跑一遍,验证输出参数的稳定性。多次标定结果如果差别很大,通常说明采集数据质量有问题。
4. 标定结果怎么影响视觉定位:从内参到位姿解算
4.1 移动机器人视觉定位的三种主流思路
移动机器人的视觉定位,从技术路线上大致分三类。第一类是基于人工标记的定位,比如地面贴Apriltag或ArUco,机器人通过识别标记角点计算自身位姿,常用于AGV的工位对接和精准停靠。第二类是基于自然特征点的视觉SLAM和VIO,比如ORB-SLAM3、VINS-Mono,通过特征点匹配和三角化实时估计位姿,适合大范围未知环境。第三类是直接法视觉里程计,比如DSO,不提取特征点,而是直接对像素灰度建模,计算量小但光照鲁棒性差。
这三类定位算法无一例外都需要用到相机内参。特征点法在初始化时需要根据内参将像素坐标反投影到归一化平面,再求解本质矩阵或单应矩阵;直接法在光度误差计算时需要内参来构建像素金字塔和投影关系;标记定位里的PnP求解更是直接把内参矩阵当作已知量。内参一旦有偏差,所有下游计算的输入就都是脏数据。
4.2 PnP与位姿求解:内参是怎么参与计算的
以Apriltag定位为例,说明标定精度和定位精度的量化关系。Apriltag检测流程是先做图像二值化,找出四边形轮廓,然后根据tag家族解码,最后提取角点的亚像素坐标。拿到四个角点的像素坐标和它们在tag坐标系中的三维坐标后,用OpenCV的solvePnP求解相机位姿。
solvePnP的完整调用是这样的:
retval, rvec, tvec = cv2.solvePnP( object_points, # 三维点,世界坐标系 image_points, # 对应的二维像素点 camera_matrix, # 内参矩阵 dist_coeffs # 畸变系数 )这里的关键在于,如果camera_matrix和dist_coeffs是错的,那么rvec和tvec即使能收敛,解出来的位姿也是偏的。举个直观例子:焦距fx标定偏差2%,在3米外的tag上,横向位置误差就能达到6厘米。对于要求毫米级对接精度的AGV来说,这是完全不可接受的。
所以,当你的机器人出现“远处定位飘,近处定位准”的现象,别急着怀疑PnP算法,先检查相机标定。而且最好把去畸变后的图像直接喂给定位模块,而不是让定位模块消化原始畸变图像,否则边缘区域的定位误差会被放大。
4.3 标定误差对SLAM系统定位精度的传导路径
SLAM系统是一个闭环,标定误差会沿着状态估计链条层层传导。第一层是前端视觉里程计,特征点匹配和三角化都依赖内参,内参偏了,三角化出来的三维点就是歪的。第二层是后端优化,BA优化在优化位姿的同时,如果相机内参也参与优化(自标定),那么内参会变成一个可调整变量,可能吸收其他误差,导致位姿估计偏差被掩盖但真实精度并没有提升。第三层是回环检测,回环本质上是特征描述子的匹配和几何验证,如果内参不准,验证过程中的几何约束会变宽松,误回环的概率上升。
我在一个室内服务机器人项目里做过对照实验:使用出厂内参时,视觉里程计漂移率大约1.2%;重新标定后,同样的序列漂移率降到了0.7%。没有改任何算法参数,唯一变化的就是内参和畸变系数。这说明标定在SLAM系统里的天花板效应非常明显——算法再好,标定精度不够,系统上限就摆在那。
5. 常见问题与排错手册:标定过程中踩过的真实坑
5.1 重投影误差居高不下
如果标定后重投影误差一直大于0.3像素,优先检查三件事:标定板是否平整、图像是否存在运动模糊、标定板在画面中是否太小。标定板不平整是常见原因,打印纸轻微弯曲都会引入几毫米误差;运动模糊在高帧率相机上容易被忽视,但角点检测对锐度很敏感;标定板在画面中面积过小时,角点亚像素检测缺乏足够梯度支撑,精度自然下降。
红外相机标定还有一个特殊问题:传统棋盘格在红外波段下对比度低,角点检测经常失败。解决方式是使用加热的靶标,或者在白底板上粘贴高热辐射材料,让红外图像中能形成清晰边界。国内一些做红外测温机器人的团队,会直接用3D打印的镂空棋盘格配合热源作为标定靶,效果比打印纸好很多。
5.2 角点检测数量不稳定
OpenCV的findChessboardCorners偶尔会在某些帧上检测失败,尤其是大角度拍摄时。这并不一定是采集问题,而是算法本身对遮挡和极端透视变换敏感。解决办法不是硬调参数,而是多拍几十张,然后过滤掉检测失败的帧。我一般会检查每张图的角点数量和亚像素细化后的坐标方差,把方差跳变的帧剔除,再送入标定流程。
如果是Apriltag检测失败,大概率是曝光或二值化阈值问题。Apriltag对反光比较敏感,标定板表面有覆膜的话反光会很严重,建议用哑光打印纸,避免塑封。
5.3 双目校正后仍有明显视差
双目立体校正的目标是让左右目图像的行对齐,如果校正后同一特征点在左右图的行坐标还有明显偏差,说明双目外参标定不准确。常见原因包括左右目采集不同步、标定板运动过快、左右目标定板亮度差异导致检测精度不一致。
定位到这类问题时,先用静态场景复测:固定双目相机,拍摄静止的标定板,检查左右目角点行坐标差。如果静态下误差依然明显,重新做双目标定;如果静态正常但动态下有残差,问题大概率出在采集同步上。
5.4 时间戳不同步导致相机-IMU标定失败
用Kalibr做相机-IMU联合标定时,最常见的问题是时间戳不同步。图像话题和IMU话题的时间基准必须一致,否则标定算法无法正确匹配两个传感器的时间流。如果无法硬件同步,可以先用time sync工具做软件同步,并确保IMU的延迟补偿参数在标定后同样写入定位算法配置。
另外一个容易被忽略的点是IMU的频率。Kalibr标定对IMU的频率没有硬性要求,但低频IMU(比如50Hz以下)会降低标定的可观测性,IMU内参的估计误差会明显变大。建议至少使用100Hz以上的IMU数据进行联合标定。
5.5 快速定位标定问题的排查顺序
当你面对“定位不准,怀疑标定”的情况,我建议按这个顺序排查:先看内参是否准确,重投影误差是多少;再看畸变校正是否正确,画面边缘的直线是否被拉直;接着看双目外参,立体校正后的水平视差是否在合理范围;最后看相机-IMU外参,静止状态下VIO是否发散。
按照这个顺序走一遍,80%的标定相关定位问题都能定位到根因。
最后分享一个我自己的习惯
我每次标定完,都会把标定板放在一个固定位置,拍一张照片,单独保存一份标定参数和这张照片。下次标定时,再用同一位置拍一张,快速对比一下重投影误差和内参变化。如果内参漂移超过1%,说明相机可能受过磕碰或者温度变化太大,系统就要提前预警。这个习惯帮我提前发现过好几次相机松动问题,算是个低成本高收益的小技巧。做移动机器人,标定不是一次性的,它会贯穿产品整个生命周期,值得多花时间认真对待。
本文还有配套的精品资源,点击获取