1、程序开始首先读取参数:
int main(int argc, char* *argv)
{
// ./run_eruroc /home/zlc/VIOFiles/MH_05_difficult/mav0/ ./config/
sData_path = argv[1];
sConfig_path = argv[2];
pSystem.reset(new System(sConfig_path)); // sConfig_path = ./config/
2、下面是开启4大线程:
std::thread the_BackEnd(&System::ProcessBackEnd, pSystem); // 后端优化线程, ProcessBackend函数能够启动是因为在创建 System类对象的时候,bStart_backend=true
std::thread thd_PubImuData(PubImuData); //
std::thread thd_PubImageData(PubImageData);
#ifdef __linux__
std::thread thd_Draw(&System::Draw, pSystem);
#elif __APPLE__
DrawIMGandGLinMainThrd();
#endif
3、程序结束:
thd_PubImuData.join();
thd_PubImageData.join();
// thd_BackEnd.join();
// thd_Draw.join();
cout << "main end ... see you ... " << endl;
return 0;
}
一、PSystem对象的创建, System类对象创建
pSystem.reset(new System(sConfig_path)); // sConfig_path = ./config/
System类构造函数如下:
System::System(string sConfig_file_) : bStart_backend(true) // 开启后端
{
string sConfig_file = sConfig_file_ + "euroc_config.yaml";
cout << "1 System() sConfig_file: " << sConfig_file << endl; // 输出中的第一句打印: 打印配置文件 sConfig_path = ./config/euroc_config.yaml
readParameters(sConfig_file); // parameters.cpp 文件中的函数
trackerData[0].readIntrinsicParameter(sConfig_file);
//readParameters(sConfig_file);
estimator.setParameter();
}
1、详细看**readParameters(sConfig_file)**函数,在parameter.cpp文件中,主要是读取euroc_config.yaml配置文件中设置的参数。需要格外注意的是,没有使用初始化函数来估计外部参数。
void readParameters(string config_file)
{
// 1. 读取文件
cv::FileStorage fsSettings(config_file, cv::FileStorage::READ);
// 2.根据读取文件内容设置参数
FOCAL_LENGTH = 460; // 设置焦距
SOLVER_TIME = fsSettings["max_solver_time"];
NUM_ITERATIONS = fsSettings["max_num_iterations"]; // 限制最大迭代次数,保证实时性
MIN_PARALLAX = fsSettings["keyframe_parallax"]; // 最小共视关系(最小视差), 10个像素
MIN_PARALLAX = MIN_PARALLAX / FOCAL_LENGTH; // 真实世界最小视差=最小视差/焦距=10.0/460.0
string OUTPUT_PATH;
fsSettings["output_path"] >> OUTPUT_PATH;
VINS_RESULT_PATH = OUTPUT_PATH + "/vins_result_no_loop.txt";
// IMU 和 相机坐标系下的外部参数变换
ESTIMATE_EXTRINSIC = fsSettings["estimate_extrinsic"]; // ESTIMATE_EXTRINSIC = 0, 不需要进行参数估计
if ( ESTIMATE_EXTRINSIC == 2 )
{ ....... }
else
{
if ( ESTIMATE_EXTRINSIC == 0 ) // 不需要估计camera和IMU之间的外部旋转和平移参数
{
cout << "fix extrinsic param " << endl;
}
// Extrinsic parameter between IMU and camera 相机和IMU之间的变换, 旋转矩阵和平移矩阵
cv::Mat cv_R, cv_T;
fsSettings["extrinsicRotation"] >> cv_R;
fsSettings["extrinsicTranslation"] >> cv_T;
Eigen::Matrix3d eigen_R; Eigen::Vector3d eigen_T;
cv::cv2eigen(cv_R, eigen_R); cv::cv2eigen(cv_T, eigen_T);
Eigen::Quaterniond Q(eigen_R); // 转换为四元数
eigen_R = Q.normalized();
RIC.push_back(eigen_R); // 确定IMU与camera的旋转矩阵
TIC.push_back(eigen_T); // 确定IMU与camera的平移矩阵
}
INIT_DEPTH = 5.0;
BIAS_ACC_THRESHOLD = 0.1;
BIAS_GYR_THRESHOLD = 0.1;
// 设置td 和 tr
TD = fsSettings["td"]; // initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock) 读图像时间 + td = 真实图像时间
ESTIMATE_TD = fsSettings["estimate_td"]; // 相机和IMU同步校准得到的时间差
// 是否为卷帘门相机
ROLLING_SHUTTER = fsSettings["rolling_shutter"]; // 0: global shutter camera, 1: rolling shutter camera
else
{ TR = 0; // 全局相机, 这里tr = 0 }
fsSettings["image_topic"] >> IMAGE_TOPIC;
fsSettings["imu_topic"] >> IMU_TOPIC; // 字符流, 所以用 ">>"
MAX_CNT = fsSettings["max_cnt"]; // 特征跟踪中最大特征点数
MIN_DIST = fsSettings["min_dist"]; // 两个特征之间的最小距离
FREQ = fsSettings["freq"]; // 控制发布次数的频率
F_THRESHOLD = fsSettings["F_threshold"];
SHOW_TRACK = fsSettings["show_track"]; // =1, publish tracking image as topic
EQUALIZE = fsSettings["equalize"]; // =1, if image is too dark or light, trun on equalize to find enough features
FISHEYE = fsSettings["fisheye"]; // =0, 不是鱼眼镜头
CAM_NAMES.push_back(config_file); // // 相机参数配置文件名
STEREO_TRACK = false;
PUB_THIS_FRAME = false;
if ( FREQ == 0 )
{
FREQ = 10;
}
fsSettings.release();
// 3.打印刚设置的参数
cout << "1 readParameters: "
... ... << endl;
}
2、System类构造函数中的trackerData[0].readIntrinsicParameter(sConfig_file);
/* 读取相机标定的内参 */
void FeatureTracker::readIntrinsicParameter(const string& calib_file)
{
cout << "read parameter of camera " << calib_file << endl; // calib_file = sConfig_path = ./config/euroc_config.yaml
m_camera = CameraFactory::instance()->generateCameraFromYamlFile(calib_file); // 见2、1节和2、2节解析
}
2、1 CameraFactory::instance()创建不同的相机类型和校准模型实例,注意,这里是为了读取相机参数,肯定与校准相关,所以这里调用这个CameraFactory类。
CameraFactory::instance(void) // 不同的相机类型和校准模型
{ // 重新创建相机实例
if (m_instance.get() == 0)
m_instance.reset(new CameraFactory);
return m_instance;
}
2、2 真正通过generateCameraFromYamlFile()函数开始读取内参:
CameraPtr CameraFactory::generateCameraFromYamlFile(const std::string& filename)
{
cv::FileStorage fs(filename, cv::FileStorage::READ); // 文件操作
Camera::ModelType modelType = Camera::MEI; // 默认为MEI模型, 后面会赋值为针孔模型
if (!fs["model_type"].isNone())
{
std::string sModelType;
fs["model_type"] >> sModelType; // euroc_config.yaml配置文件中: model_type: PINHOLE
if (boost::iequals(sModelType, "kannala_brandt"))
else if (boost::iequals(sModelType, "scaramuzza"))
else if (boost::iequals(sModelType, "pinhole"))
modelType = Camera::PINHOLE;
}
switch (modelType)
{
case Camera::KANNALA_BRANDT:
{ }
case Camera::PINHOLE:
{
PinholeCameraPtr camera(new PinholeCamera); // 创建PinholeCamera相机对象
PinholeCamera::Parameters params = camera->getParameters(); // 获得参数读取指针, 具体函数见后面
params.readFromYamlFile(filename); // 读取相机内参K矩阵和投影参数 函数
camera->setParameters(params); // 设置相机参数
return camera;
}
case Camera::SCARAMUZZA:
{ }
case Camera::MEI:
default:
{ }
}
return CameraPtr();
}
2.2.1 获得参数读取指针:
const PinholeCamera::Parameters& PinholeCamera::getParameters(void) const // 用到这个函数返回 参数引用
{
return mParameters;
}
2.2.2 其中的getParameters函数如下,在PinholeCamera.cpp函数中:
bool PinholeCamera::Parameters::readFromYamlFile(const std::string& filename) // 从配置文件euroc_config.yaml中读取畸变参数矩阵和相机内参K矩阵
{
cv::FileStorage fs(filename, cv::FileStorage::READ);
m_modelType = PINHOLE;
fs["camera_name"] >> m_cameraName; // euroc_config.yaml 配置文件中: 相机名称: camera_name: camera
m_imageWidth = static_cast<int>(fs["image_width"]); // image_width: 752 列宽
m_imageHeight = static_cast<int>(fs["image_height"]); // image_height: 480 行高
cv::FileNode n = fs["distortion_parameters"];
m_k1 = static_cast<double>(n["k1"]);
... ...
m_p2 = static_cast<double>(n["p2"]);
n = fs["projection_parameters"];
m_fx = static_cast<double>(n["fx"]);
... ...
m_cy = static_cast<double>(n["cy"]);
std::cout << "1 PinholeCamera "
<< "\n m_cameraName: " << m_cameraName
... ...<< std::endl << std::endl;
return true;
}
2.2.3 camera->setParameters(params); 设置相机参数函数如下:
void PinholeCamera::setParameters(const PinholeCamera::Parameters& parameters)
{
mParameters = parameters;
if ((mParameters.k1() == 0.0) && (mParameters.k2() == 0.0) && (mParameters.p1() == 0.0) && (mParameters.p2() == 0.0)) // 畸变参数为0, 说明没有畸变矫正
m_noDistortion = true;
else
m_noDistortion = false;
m_inv_K11 = 1.0 / mParameters.fx(); // 设置内参矩阵的逆, 在畸变投影模型中会用到
m_inv_K13 = -mParameters.cx() / mParameters.fx();
m_inv_K22 = 1.0 / mParameters.fy();
m_inv_K23 = -mParameters.cy() / mParameters.fy();
}
3、System类构造函数中的estimator.setParameter();,开始设置相机和IMU之间的外部变换参数:
void Estimator::setParameter()
{
for (int i = 0; i < NUM_OF_CAM; i++)
{
tic[i] = TIC[i];
ric[i] = RIC[i];
}
cout << "1 Estimator::setParameter FOCAL_LENGTH: " << FOCAL_LENGTH << endl;
f_manager.setRic(ric); // 只设置相机与IMU之间的旋转参数
project_sqrt_info_ = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
td = TD; // 相机和IMU同步校准得到的时间差 △△△△△ ☆☆☆☆☆
}