Kalibr简介:
Kalibr是一个用于标定多相机系统和IMU(惯性测量单元)的开源工具包,旨在提供一套完整的、易于使用的标定工具。它是ETH Zurich自主飞行系统实验室的一个项目,该实验室致力于研究和开发各种机器人技术。
Kalibr可以用于相机的内部参数标定,外部参数标定和时间同步标定,也可以用于IMU的标定,以及相机和IMU之间的外部标定。它支持多种相机模型(包括针孔模型、鱼眼模型和全景模型)和IMU模型,可以处理多达数十个相机和多个IMU的标定。Kalibr还提供了一个图形用户界面和命令行接口,使用户可以灵活地选择和执行不同的标定任务。
除了标定功能,Kalibr还提供了一些其他的功能,如轨迹估计和轨迹优化,这些功能可以在多相机系统和IMU的SLAM(同时定位和地图构建)中发挥重要作用。
总的来说,Kalibr是一个功能强大、灵活性高的标定工具包,适用于需要进行多相机系统和IMU标定的各种研究和应用场景。
Kalibr 多目相机标定流程
1 概述
Kalibr中实现了多目标定的功能,其原理可以分为以下几个步骤:
- 选取标定板:在标定之前,需要选取一个合适的标定板。Kalibr支持使用不同类型的标定板,如棋盘格、圆形格、椭圆格等。标定板应该被放置在多个相机可以看到的位置,并在每个相机中采集足够数量的图像,以便进行标定。
- 提取特征点:使用每个相机采集的标定板图像,从中提取特征点。Kalibr中支持使用不同的特征点提取算法,如FAST、ORB、SIFT等。
- 匹配特征点:对于每个标定板图像对,需要将它们之间的特征点进行匹配。Kalibr中提供了多种特征点匹配算法,如基于描述子的匹配、基于光流的匹配等。
- 求解相机内参:根据匹配好的特征点和标定板的几何信息,可以通过最小二乘法求解每个相机的内参矩阵。Kalibr中使用了基于投影误差的优化算法来求解内参矩阵。
- 求解相机外参:在求解相机外参之前,需要选择一个基准相机,将其坐标系作为全局坐标系。对于其他相机,可以通过与基准相机之间的特征点匹配来求解它们之间的相对姿态(外参)。Kalibr中使用了基于四元数的优化算法来求解相机之间的相对姿态。
- 优化:最后,对于所有相机的内参和外参,需要进行联合优化,以进一步提高标定结果的精度。Kalibr中使用了基于最小二乘法的优化算法来实现联合优化。
2 代码解读
2.1 标定板获取
在kalibr_calibrate_cameras中,可以通过:
targetConfig =kc.CalibrationTargetParameters(parsed.targetYaml)
的方式来创建 CalibrationTargetParameters 类的实例,并读取标定板参数配置文件中的内容,以便在后续的相机标定过程中使用。
具体可以实现查看aslam_offline_calibration/kalibr/python/kalibr_camera_calibration/CameraCalibrator.py的CalibrationTargetParameters类。
这段代码定义了一个名为 CalibrationTargetParameters 的类,它继承自 ParametersBase 类。CalibrationTargetParameters 类用于读取和解析标定板参数配置文件中的内容,以便在标定相机时使用。
- 构造函数 __init__(self, yamlFile, createYaml=False) 接收两个参数:yamlFile 是标定板参数配置文件的路径,createYaml 是一个布尔值,表示是否创建一个新的配置文件。
类中定义了一系列函数,用于检查和获取标定板的参数,例如:
- checkTargetType(self, target_type):检查标定板类型是否合法。
- getTargetType(self):获取标定板的类型。
getTargetParams(self):获取标定板的具体参数,例如标定板格子的行列数,行列之间的距离等。根据不同的标定板类型,需要读取不同的参数。
该类还有一些辅助函数用于检查标定板参数配置文件的内容是否合法,如果不合法则抛出异常。
2.2 角点提取
角点提取的核心是使用cv库中的棋盘格角点自动提取方法。
2.2.1 extractCornersFromDataset
角点提取和extractCornersFromDataset方法有关,具体实现查看:aslam_offline_calibration/kalibr/python/kalibr_common/TargetExtractor.py
该方法实现了从一个数据集中提取校准板角点的功能。函数的输入是数据集、检测器和一些参数,输出是一系列角点的位置和其他信息。该函数支持单进程和多进程两种方式。如果启用了多进程,函数会将数据集中的每张图片交给不同的进程处理,最终将结果合并。如果没有启用多进程,则使用单进程处理每张图片。
在函数内部,首先通过 dataset.numImages() 获取数据集中的图片数量,并创建了一个 sm.Progress2() 对象,用于显示处理进度。
然后,如果启用了多进程,代码会创建一个 multiprocessing.Manager() 对象,以便在多个进程之间共享数据。接着,函数遍历数据集中的每张图片,将其数据打包放入 taskq 队列中。同时,复制一份传入的检测器 detector,在 plist 列表中存储 numProcesses 个新的进程,并将每个进程分配到一个核心上。每个进程从 taskq 队列中获取一张图片,使用检测器 detector_copy 进行角点检测,并将检测结果放入 resultq 队列中。检测完成后,等待所有进程完成并退出。
在多进程处理结束后,主进程将从 resultq 队列中读取所有检测结果,并将其按时间戳排序。最终,该函数返回一个包含所有角点的列表 targetObservations。
如果没有启用多进程,则使用单进程的方式遍历数据集,并对每张图片进行角点检测。对于每张图片,先检测是否启用了估计变换 noTransformation。如果没有启用,则使用 detector.findTarget() 方法进行角点检测和估计变换。如果启用了,则使用 detector.findTargetNoTransformation() 方法进行角点检测,但不估计变换。在检测完成后,将结果存入 targetObservations 列表中。
最后,如果未检测到任何角点,则输出错误信息。如果检测到角点,则显示处理结果。
2.2.2 角点提取核心步骤
文件:
\kalibr\master\aslam_cv\aslam_cameras\src\GridCalibrationTargetCheckerboard.cpp
使用 cv::findChessboardCorners 函数从输入图像中提取棋盘格的角点。 当 _options.doSubpixelRefinement 为真且角点检测成功时,对检测到的角点进行亚像素级别的精度优化。
角点的输出格式是一个Eigen MatrixXd对象,该对象的每一行代表一个角点在图像坐标系下的位置,每行包含两个元素,即x和y坐标值。因此,输出格式是一个矩阵,其行数等于检测到的角点数,每行包含两个元素表示角点的坐标。
2.3内参求解
以小孔成像相机为例:
首先进行相机内参的初始化,再使用BA算法进行优化,求解方法为L-M方法。
2.3.1 相机内参初始化
文件:Line 710
kalibrmaster\aslam_cv\aslam_cameras\include\aslam\cameras\implementation\PinholeProjection.hpp
对于每组标定板的观测数据,通过检测每个格点所在的圆圈来计算焦距的初值。具体来说,首先将棋盘格的每一行都拟合一个圆,然后计算任意两行之间所有圆心的连线长度,根据这些连线的长度,计算出所有可能的焦距初值(这些焦距初值都是通过两个圆心连线的长度计算得到的,具体计算方法参考了论文 "Equidistant Fish-Eye Calibration and Rectification by Vanishing Point Extraction, PAMI 2010")
2.3.2 相机内参优化
文件:
aslam_offline_calibration\kalibr\python\kalibr_camera_calibration\CameraIntializers.py
方法名:
calibrateIntrinsics()
该函数的输入参数包括一个相机模型和一组观测数据,输出标定是否成功。主要步骤如下:
1.将所有的设计变量添加到OptimizationProblem()类中。
2.添加角点的误差项到优化问题中。
3.优化相机的内参和畸变系数。
4.输出标定是否成功。
具体来说:
第1步中,函数首先将相机内参的设计变量、畸变系数的设计变量和快门时间的设计变量添加到问题中。
第2步中,函数使用已知的标定板,得到标定板上各个角点的3D坐标和2D坐标(通过函数estimateTransformation()获得齐次变换矩阵)。在每个观测数据中,依次计算角点的重投影误差(通过调用reprojectionError()计算)并将其加入优化问题。
第3步中,函数使用Levenberg-Marquardt算法优化相机内参和畸变系数,使得重投影误差最小。
第4步中,输出标定是否成功。
2.4 位姿求解
2.4.1 初始相对外参矩阵
文件:
aslam_offline_calibration/kalibr/python/kalibr_camera_calibration/MulticamGraph.py(Line118)
解读:
检查所有的相机是否都通过公共目标点观测连接(形成连通图),如果没有则报错退出程序;
- 使用Dijkstra算法查找具有最大公共观测目标点的相机对;
- 对于选定的相机对,使用基础矩阵恢复初始的相对位姿变换,得到从低编号相机到高编号相机的初始变换矩阵;
- 将得到的初始变换矩阵进行拼接,得到整个相机系统的初始位姿。
- 在具体实现中,该函数首先使用图论算法判断相机是否能够形成连通图,若不能则直接退出程序。然后,该函数使用Dijkstra算法查找具有最大公共观测目标点的相机对,选定相机对后使用基础矩阵进行相机位姿的恢复。最后,将所有的相机对恢复的位姿进行拼接,得到整个相机系统的初始位姿。
- 由于视觉SLAM中的相机位姿是不断优化更新的,因此这个初始位姿的正确性对整个SLAM系统的性能具有很大影响。
2.4.2 初始化全局外参矩阵
- 首先,从观测数据库中获取所有看到目标角点的相机ID和每个相机看到的角点数。
- 找到角点数最多的相机,使用该相机的外参进行PNP求解,得到相机到目标的位姿变换 T_{t, c_n}。
- 然后,根据baseline_guesses(相对外参)和外参变换T_{t, c_n},得到相机c_n到相机c_0的位姿变换T_{c_n, c_0}。
- 最后,将T_{t, c_n}和T_{c_n, c_0}相乘,得到目标在相机c_0坐标系下的位姿变换T_{t, c_0}。
Q&A:
1. baseline_guesses和T_{t, c_n}的区别。
baseline_guesses是两个相机的相对外参,T_{t, c_n}是在以角点最多的相机为参考坐标系下的全局位姿。我这样理解对吗
2. baseline_guesses如何转移到T_{t, c_n}。
假设已知某相机C_N到目标t的变换T_{t_,c_N},以及相机C_N到相机C_0中存在N个已知baseline,现要将相机C_N转换到相机c_0坐标下,得到T_{t_,c_0},则该过程符合连乘原则:
T_{c_n,c_0} = \prod_{i=0}^{n-1} b_i T_{t,c_0} = T_{t,c_n} T_{c_n,c_0} |
2.5 外参和畸变参数等优化:
其核心思想是利用BA算法进行参数优化,使用的求解方法为L-M方法。
其中设计变量为:
- T_tc_guess
- baselines DVs(即上面提到的baseline_guesses)
- 角点位置
- distortionDesignVariable
- projectionDesignVariable()
- shutterDesignVariable()
其中,添加待求解问题的设计变量操作在,具体查看文件。
文件:
\kalibrmaster\aslam_offline_calibration\kalibr\python\kalibr_camera_calibration\CameraCalibrator.py
类名:CalibrationTargetOptimizationProblem
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)