资讯动态

相机模型与标定:无人机视觉定位从像素到空间坐标的完整指南

发布时间:2026/9/8 20:02:04 来源:尧图企业网站定制
一架无人机在室内走廊里低速飞行飞控上报的坐标是(1.5, 2.3, 1.0)单位是米。这个数字到底怎么来的GPS在室内早就失效了光流在纯色墙面前也会漂移此时最可靠的感知手段就是视觉。但相机输出的只是一帧 1920×1080 的像素矩阵从这些离散的 RGB 数值到“我在空间哪个位置”的连续坐标中间隔着的不是一步而是一整套数学模型——这就是标题里说的相机模型与标定。这篇文章我想把这条链路从头到尾拆一遍从针孔模型到底层畸变从标定板选型到 Kalibr 实操再补上我在无人机视觉项目里踩过的几个真实的坑。内容主要面向正在做无人机视觉感知、SLAM 入门、以及想把相机真正用起来做空间定位的开发者。1. 室内无GPS时无人机靠什么给自己定位1.1 从像素到坐标相机就是无人机的一双“眼睛”先把问题摆到台面上无人机要“理解空间中的真实位置”它理解的是什么是一组相对于某个参考系的坐标比如机体系下的(x, y, z)或者世界系下的经纬度加高度。GPS 直接给经纬度所以室外飞的时候飞控很轻松但在室内、桥洞下、树冠遮挡密集的区域GPS 信号要么没有要么跳变严重。这时候就得换一套测距手段而视觉是成本最低、信息量最大的方案。相机本质上是一个“投影仪的反向设备”三维世界里的一个点P(X, Y, Z)经过镜头的光学中心投射到图像传感器上变成一个像素点p(u, v)。这个从(X, Y, Z)到(u, v)的映射关系就是相机模型要回答的问题。知道了这个映射无人机看到图像上某个特征点就能反向推算它在空间中的方向再配合多个角度的观测或者 IMU 的加速度积分就能估算出自身位置。这里有个最容易被新手忽略的点像素坐标本身没有任何物理意义必须结合相机内参才能把像素“还原”成一条方向射线。而这条射线到底准不准完全取决于内参标定得准不准。很多人以为标定就是跑个脚本出一堆数字实际上数字只是开始后面所有定位、建图、避障算法的精度都被这几个数字锚定着。1.2 视觉定位的两条经典路线VIO与全局重定位无人机视觉定位大体上分两派一派是 VIOVisual-Inertial Odometry代表作是 VINS-Fusion、OKVIS、ROVIO另一派是全局重定位典型手段是 map-based localization比如先建好一张地图然后无人机对着当前图像找自己在地图中的位置。VIO 的核心逻辑是把相机当成“速度计”通过连续帧之间特征点的移动估算出相机的相对运动再用 IMU 的角速度与加速度数据把尺度拉回来。这里有个关键前提——特征点在不同帧之间的像素位移必须和真实运动保持一致。如果内参标定错了比如焦距偏了 2%那么算法算出来的平移量也会按比例偏掉畸变参数没标准图像边缘的特征点位置就会系统性偏移导致估计轨迹出现明显的弯曲。全局重定位则更直接当前图像里的特征描述子和预存地图里的特征描述子做匹配匹配对了就能直接给出相机的位姿。但这一步对特征点位置的准确性极其敏感一个像素级别的偏差在 5 米外的物体上就对应几厘米的空间误差如果畸变校正不到位边缘区域的误差会被放大到十几甚至几十厘米直接导致定位跳变。所以无论走哪条路线内参和外参的精度都决定了整个系统的上限。这也是为什么这篇文章愿意花大篇幅去讲模型和标定而不是直接丢一段 Kalibr 命令让你去跑——不理解原理的人出了问题根本不知道怎么排查。2. 针孔模型与畸变把三维世界“压缩”进二维图像的那道数学门2.1 内参矩阵里每个数字到底在说什么先建立最朴素的针孔相机模型。想象一个暗箱前面戳一个小孔外面的光线通过小孔后在箱内壁上成一个倒像。为了数学上方便处理我们把成像平面翻到小孔前方形成虚拟像平面这样图像就是正立的。设相机坐标系原点在光心OZ轴指向相机前方一个空间点P(X, Y, Z)在虚拟像平面上的投影点坐标是x f * X / Z y f * Y / Zf是焦距单位是毫米。但图像像素坐标的原点在左上角u轴向右v轴向下而且像素本身是长方形排列的所以还要引入像素坐标系到物理坐标系的转换u fx * X / Z cx v fy * Y / Z cy这里的fx、fy是焦距在像素单位下的表达实际等于物理焦距除以单个像素的物理尺寸cx、cy是主点坐标理论上应该在图像正中心但实际生产装配总有偏差。把这四个参数写成矩阵形式就是内参矩阵KK [fx 0 cx] [ 0 fy cy] [ 0 0 1]有人问为什么fx和fy不相等因为像素不是正方的尤其是卷帘快门的 CMOS 传感器横向和纵向的像元尺寸可能有细微差异。如果你用的是普通工业相机标定出来的fx和fy比值通常在 1 附近但如果是鱼眼镜头或者经过前置镜头组差异会明显变大。主点cx、cy是很多人标定完从不看的参数但实际上它直接决定了图像的“有效中心”。如果cx偏差很大说明镜头光轴和传感器中心没有对准这在无人机挂载相机时特别常见——安装座加工误差、镜头拧歪、甚至撞击后的微小位移都会导致主点漂移。VIO 算法里主点错误会导致轨迹在旋转时出现系统性偏移很难在后期通过调参补偿。2.2 畸变为什么躲不掉以及张氏标定法怎么解决它真实镜头不是数学上的针孔而是一组透镜。透镜的折射会引入两种主要畸变径向畸变画面边缘的直线向内凹或向外凸。原因是透镜边缘的曲率比中心大或更复杂导致入射角与投影位置不完全线性。切向畸变镜头组装配时各镜片的光轴不完全重合导致图像在某些区域产生“拉扯”。在低价镜头上尤其明显。径向畸变的数学模型通常用泰勒级数展开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听到这里很多人头大了但只要记住一件事畸变系数的作用是在对图像去畸变时把边缘像素拉回正确位置。张正友标定法之所以成为行业标准是因为它只需要相机对着一个平面棋盘格拍十几张不同角度的照片就能同时估计出内参和畸变系数过程可以完全自动化不需要昂贵的三维标定设备。其核心是利用棋盘格角点的共面性先通过单应矩阵建立每张图像的角度约束再通过多张图像联合优化所有参数。我自己在标定后会做的事情是用 OpenCV 把一张棋盘格图像做去畸变然后叠加到原图上肉眼观察边缘直线是否恢复笔直。这一步比任何数值指标都直观也是快速发现标定异常的第一道关卡。3. 标定全流程实战从标定板选型到 Kalibr 一次跑通3.1 标定板、镜头和采集姿势的选择细节标定板是第一步很多人随便在某宝买一张几十块的棋盘格就开干后面出了问题才回头找原因。棋盘格要关注几个参数格子边长精度标定算法假设每个格子的物理尺寸是已知的如果边长标称 30mm 实际是 29.5mm内参里的焦距就会跟着偏 1.7%表面平整度纸张覆膜后贴在不平的泡沫板上棋盘格整体会有局部扭曲标定出来的畸变系数会参考这种错误扭曲导致边缘去畸变后反而更糟覆膜反光哑光覆膜最好亮面在斜射光照下会产生高光角点检测会失败或者出现半像素偏移。如果你做的是无人机视觉项目建议直接用 25mm 到 40mm 格子边长的陶瓷基板或者铝基板重量虽然比纸板重一点但平整度、耐磨性和抗反光能力完全不在一个级别。室内环境用 A3 纸打印覆哑光膜也能凑合但照片数量最好多采 20 张以上。镜头选择也有讲究。标定用的镜头应该是你实际飞行时用的镜头不能在标定后更换。无人机视觉模块如果需要调焦最好选锁焦镜头标定完用胶点死如果用的是可变焦镜头每换一次焦距就要重新标定这也是 ZED 2i 这类双目相机容易被吐槽的原因——它出厂标定不错但你拆装过镜头或者大力碰撞后内参就可能漂移必须重新标定。3.2 数据采集的五个黄金准则数据采集效率直接决定标定质量。我总结出五个准则基本都是踩坑踩出来的保持标定板在画面中有足够占比至少占画面的三分之一否则角点像素太少亚像素提取精度不够标定结果误差大。离得太远的结果是内参偏小畸变系数会被平均掉。姿态多样化棋盘格相对于相机的姿态至少要有三个不同方向的倾斜包括正面平视、绕水平轴上下倾、绕竖直轴左右倾。如果所有照片都是正对棋盘格拍内参矩阵中的fx、fy会退化因为这种状态下尺度信息被约束在单一方向上。覆盖画面所有区域棋盘格必须出现在画面的九个区域——四个角、四条边中间、正中间。很多人只让棋盘格在画面中心附近晃动边缘区域的畸变参数完全没被观测标定出来的k1、k2基本不可信。运动幅度要小且稳定采集时无人机或者相机要保持低动态不要快速旋转或大幅移动否则图像运动模糊角点检测亚像素精度下降。Kalibr 的imu-cam标定对运动激励有要求但纯相机标定不需要大幅运动手稳拿着慢慢换角度就行。亮度均匀最好在散射光环境下拍摄避免阳光直射产生高反光和阴影。曝光时间可以适当缩短确保棋盘格黑白交界处有清晰的梯度不要出现过曝或欠曝。3.3 Kalibr、OpenCV、MATLAB 三套工具的选型对比标定工具我实际都用过各有适用场景。用一张表说清楚工具优点缺点推荐场景OpenCVPython/C免费开源、脚本可控、上手快只标内参和畸变不标相机-IMU外参优化模型相对简单快速验证镜头纯视觉定位项目前期MATLAB Camera Calibrator界面可视化、误差分析图表齐全、支持多种畸变模型收费导出到 ROS 节点配置稍麻烦实验室教学、单目/双目内参标定Kalibr支持多相机、相机-IMU 外参标定、APRILGRID 标定板、与 ROS/ROS2 生态无缝衔接命令行操作门槛略高依赖环境配置较繁琐对输入数据质量非常挑剔无人机 VIO/SLAM 项目、多传感器融合如果你走 ROS2 PX4 这条路我推荐直接上 Kalibr因为它的产出格式可以直接被 VINS-Fusion、ORB-SLAM3 等视觉惯性算法读取。Kalibr 内部使用的标定板是 Aprilgrid它比棋盘格好在支持部分遮挡时可继续检测且能提供完整 ID 以区分姿势数据丢失的容错性好很多。Kalibr 标定单目内参的命令大致长这样rosrun kalibr kalibr_calibrate_cameras \ --target april_6x6_50x50cm.yaml \ --bag dataset_cam.bag \ --models pinhole-radtan \ --topics /cam0/image_raw \ --bag-from-to 5 60 \ --show-extraction--models pinhole-radtan表示使用针孔模型加径向切向畸变模型这是最通用的组合。如果是鱼眼镜头可以换成pinhole-equidistant或者omni-radtan。3.4 标定结果怎么判断好坏重投影误差与直观验证拿到标定结果后第一眼看重投影误差。所谓重投影误差就是把棋盘格角点的三维坐标通过估计出的内外参重新投影到图像上与检测到的像素坐标的均方根误差。Kalibr 输出里会有一个reprojection error字段单位是像素。经验上单目针孔模型的均值在 0.1~0.3 像素之间算合格超过 0.5 像素说明标定过程有问题需要检查数据。但重投影误差只是内部一致性指标它不能直接告诉你“实际测距准不准”。很多标定结果在重投影上表现优秀但部署到 VIO 后轨迹仍然漂移原因往往是内参和外参之间的一致性不够。这里我建议做一次直观验证把标定好的内参写进去畸变函数对着一张平面上的标尺拍照测量图像中两端最远像素点对应的物理距离看是否与实际一致。焦距偏差 1%一米尺子的测量误差就是 1 厘米这个测试简单但非常有效。另外还有一个很实用的技巧标定两次间隔一天然后对比两次结果中的fx、fy、cx、cy。如果相差超过 1%说明镜头结构不稳定或者标定数据有较大的条件偏差需要重新标定。这比单次标定的误差指标更能反映系统的稳定性。4. 外参标定让相机和飞控“对准同一个世界”4.1 单目/双目内参之后的相机-IMU外参标定很多做无人机视觉的人标完内参就以为大功告成结果一跑 VINS-Fusion 轨迹全是斜的问题几乎都出在外参上。外参描述的是相机坐标系到 IMU/机体系之间的旋转和平移关系。如果外参的旋转角度差了三度那么视觉估计的位姿和 IMU 积分出来的位姿之间就会存在一个持续偏差融合出来的轨迹要么漂移要么振荡。标定外参最常用的工具还是 Kalibr它的kalibr_calibrate_imu_camera命令可以联合标定相机-IMU 外参、时延、以及相机内参。前提是有一组稳定的图像序列和 IMU 数据时间戳已经对齐IMU 和相机安装在同一个刚性结构上没有相对松动数据采集时让设备做充分的运动激励包括平移、旋转、加速覆盖六个自由度。我个人的做法是先把设备固定在一个小车上或者手持在桌面空间划“8”字同时旋转手腕让相机朝向不断变化。这样 IMU 和相机的运动都能被充分激发外参的可观性才会被满足。如果运动幅度太小外参中的平移量会非常不可靠。4.2 标定结果如何接入 ROS2 与 PX4Kalibr 标定结束后会输出一个camchain.yaml里面包含相机内参、畸变系数、以及 IMU 的外参变换。接下来要做的就是把它们写进 ROS2 的相机驱动配置文件以及 PX4 的传感器参数。在 ROS2 侧典型的配置是camera_name: cam0 image_width: 1280 image_height: 720 camera_matrix: rows: 3 cols: 3 data: [526.3, 0.0, 635.1, 0.0, 525.8, 365.2, 0.0, 0.0, 1.0] distortion_model: radtan distortion_coefficients: rows: 1 cols: 4 data: [0.108, -0.241, 0.002, -0.001]在 PX4 侧如果你用的是 PX4 自带的 VIO 模块需要把相机位姿写入到vehicle_visual_odometry发布前的一个坐标变换关系里。简单说PX4 期望收到的视觉里程计位姿已经是以机体系为参考的如果你的相机坐标系和机体系不重合就要做一次T_body_cam的变换。外参标定的数字如果错了位置估计就会一路漂移甚至出现“概念翻转”——飞控认为无人机在向右飞实际上是向左。4.3 外参误差在 SLAM 里是怎么被放大的说一个我印象深刻的例子。有一次在模拟器里用 VINS-Fusion 跑仿真数据集轨迹精度一直在 10 厘米左右浮动但真机测试时误差到了 30 厘米。排查了很久最后发现是无人机上相机的安装座有一点几毫米的胶垫形变导致外参里的平移量和标定时不一致。几毫米的误差在地面上肉眼根本看不出但 SLAM 算法在优化过程中会把这种偏差折算成尺度误差和旋转漂移距离越远放大越明显。还有一个常见的坑是相机从标定支架上拆下来再装回去外参已经发生了变化。如果做不到重新标定至少要做一次安装重复性测试——拆装三次每次跑一小段里程计对比轨迹差异是否在误差范围内。如果三次轨迹都一致说明安装一致性尚可如果差异明显就要考虑加装定位销或者刚性更好的安装座。5. 真实踩坑记录三次标定失败背后的排查链路5.1 标定板轻微反光重投影误差从0.15涨到0.8那次用 A3 纸打印棋盘格覆了一层亮膜在实验室荧光灯正下方拍。采集的时候肉眼看着棋盘格没什么问题但 Kalibr 跑完--show-extraction发现大约 15% 的角点提取位置偏移了半像素还多重投影误差居然到了 0.8 像素。查看提取图像才注意到棋盘格黑格表面有很轻微的高光反射角点边缘的灰度梯度被高光污染亚像素检测的位置发生了偏移。解决办法是换哑光覆膜或者把光源从正上方改成侧向散射。另一个方案是降低相机曝光时间避免白色区域过曝。自那以后我的规则是任何标定板照片都不能出现纯白区域饱和度达到 255 的现象。5.2 采集时手抖导致图像运动模糊Kalibr直接崩Kalibr 对图像质量非常挑剔尤其是提取 Aprilgrid 角点的环节稍微模糊一点的图像就会导致检测不出完整网格或者生成错误的坐标。有一回我采集了 90 秒的数据结果 Kalibr 运行到中途直接报KeyError查看日志发现是连续十几帧图像都无法识别出足够的完整网格单元。排查过程是这样的先看提取结果图发现模糊帧基本出现在我转身的瞬间——手持设备旋转过快曝光时间又偏长产生了运动模糊。解决方案是降低曝光时间到 2ms 左右同时尽量用机械云台固定设备让相机在运动过程中保持姿态稳定。人造光源下如果曝光时间太短会有频闪可以把采集场地安排到稳定的 LED 光源环境或者使用外部补光灯。5.3 双目基线距离写错深度图出现“纸片化”双目相机的深度估计依赖基线长度B和焦距f。如果你用的 ZED 2i 的官方标称基线是 120mm但实际安装架子的公差让基线变成了 119mm那么深度值的整体偏差大约是 0.8%。这听起来不大但在 5 米处就会产生 4 厘米的误差而且距离越远误差成比例增大。更麻烦的是如果左右目的外参旋转矩阵有 0.5 度的误差那么深度图在边缘区域会出现明显的“纸片化”——物体仿佛被压缩成一张张薄片垂直边缘出现锯齿。这个问题的排查链路是输出深度图看边缘的连续性再对比真实距离看 3 米处和 8 米处的偏差趋势。如果远距离偏差明显大于近距离优先怀疑基线或者外参旋转如果远近偏差比较均匀优先怀疑焦距和主点。我自己最后是重新做了一次全尺寸标定把左右目的相对变换也交给 Kalibr 去估计而不是直接采用出厂参数问题才彻底解决。6. 标定之后的路从内参到空间理解的完整闭环6.1 标定结果在VINS-Fusion里的真实表现当内参、畸变、外参都标定到位之后VINS-Fusion 这类 VIO 系统的表现才能正常。我在室内走廊里跑过一段真实测试无人机搭载单目加 IMU以 0.8m/s 的速度平飞距离地面约 1.2 米走廊长度约 18 米。标定良好的情况下端到端轨迹总漂移控制在 2% 以内也就是大约 35 厘米而在外参未重新标定时同样的路径漂移会超过 15%轨迹末端直接偏移出走廊边界。这里要强调一点VIO 的尺度恢复依赖 IMU 与相机的外参平移量如果T_imu_cam的平移误差过大尺度就会收敛到错误值导致速度估计偏大或偏小。很多人在真机测试时发现视觉里程计的速度和实际速度差一大截第一反应是调滤波参数实际上应该回去检查外参标定。6.2 如何快速验证标定精度是否满足飞行需求最后分享一个快速验证流程不需要复杂的实验设施把相机固定好对准一个已知大小的矩形标记物比如 A4 纸从 0.5 米到 5 米每隔 0.5 米拍一张照片根据内参和畸变参数做去畸变后用 PnP 求解标记物四个角点对应的距离和实际激光测距值对比误差在 3% 以内说明内参和畸变满足一般视觉定位需求超过 5%建议重新标定然后开启 VIO让无人机悬停观察速度和位置估计是否稳定如果悬停 30 秒位置漂移超过 0.3 米重点排查外参和 IMU 噪声参数。按照这个流程每次装完无人机都能在半小时内确定标定是否需要返工。从我个人的经验来看绝大多数视觉定位问题追根溯源都发生在标定这个环节。把相机模型和标定搞通后面做路径规划、避障、目标跟踪才会有一个真正可靠的空间参考基准。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价