https://blog.csdn.net/sinat_29886521/article/details/77506426
https://www.cnblogs.com/bokeyuan-dlam/articles/15078895.html
8.1 RANSAC (Random sample consensus)
内点、外点:
内点就是应该算作正确的点,外点是不该算作正确的点(图像匹配任务就是不该匹配上的点)
算法思想【以直线拟合任务为例】:
由于增大数据采样量也会增加外点数量,因此不能作为解决办法。因此少量多次
少量指的是每次拟合只使用少量的点,多次指的是多次拟合。
这样总能找到一种解使得包含最多的内点
使用好RANSAC算法的方式(减少采样轮数):
1.减少外点的比例 2.减少每次采样所利用的样本个数(下图两个坐标)
例子1 工程调用
作用
1 3D-3D位姿求解
2 ranck 随机取样找内点数据优化结果,排除离群点干扰
CMakeLists.txt
cmake_minimum_required(VERSION 3.5) project(ICP_SVD_example) # Set C++ standard to C++11 set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Find Eigen library find_package(Eigen3 REQUIRED) # Include directories for Eigen include_directories(${EIGEN3_INCLUDE_DIR}) # Add executable target add_executable(icp_svd_example main.cpp) # Link Eigen library target_link_libraries(icp_svd_example Eigen3::Eigen)
API_3D_3D.hpp
main.cpp
#include <iostream> #include <Eigen/Dense> #include <vector> #include <cmath> #include <random> #include "API_3D_3D.hpp" using namespace Eigen; using namespace std; int main() { //Test_3(); //1-1 创建测试点 调用点代替 vector<Vector3d> source_points; vector<Vector3d> target_points; // 1-2 设置变换矩阵 Matrix3d currentR; Vector3d currentT; double currentScale=20; //currentR<<1,0,0,0,1,0,0,0,1;// 单位阵 currentR<<0,-1,0,1,0,0,0,0,1; // 旋转90度 currentT<<1000,1000,1000; // 1-3 获取随机测试点 Get_TestValue(source_points,target_points,currentScale,currentR,currentT); //2 设置优化参数 int time_=1000;// 迭代次数 int random_poinst_num=4;// 每次抽取的点数 起码3个点 double inliner_error_thresh=3;// 如果目标点是 GNSS的enu坐标(民用10米 rtk 1-10cm) //3-0 记录最优结果 Variables to store results double scale; Matrix3d R; Vector3d t; //3-1 开始计算 API_ransac_ICP_3D_3D_sRt_inliner_sR(source_points, target_points, time_, inliner_error_thresh,random_poinst_num, scale, R, t); cout << "Scale factor s: " << scale << endl; cout << "Rotation matrix R:\n" << R << endl; cout << "Translation vector t:\n" << t << endl; //4 统计结果 computeError(source_points, target_points, scale, R, t); return 0; }
API_3D_3D.hpp
#include <iostream> #include <Eigen/Dense> #include <vector> #include <cmath> #include <random> using namespace Eigen; using namespace std; // 点云类型定义 typedef vector<Vector3d> PointCloud; // 变换点和目标点计算误差 double computeError(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const double scale, const Matrix3d& R, const Vector3d& t) { // Compute transformation error using point-to-point distances double total_error = 0.0; double Mean_error = 0.0; double max_error = 0.0; double min_error = 9999; int num_points = source_points.size(); vector<double> error_; for (int i = 0; i < num_points; ++i) { Vector3d transformed_point = scale * R * source_points[i] + t; double cur_error =(transformed_point - target_points[i]).norm(); error_.push_back(cur_error); } for(auto cur_error : error_){ if(cur_error>max_error){max_error=cur_error;} if(cur_error<min_error){min_error=cur_error;} total_error = total_error+cur_error; //向量的二范数 } Mean_error=total_error / num_points; cout<<"Mean_error " <<Mean_error << " max_error "<< max_error<< " min_error "<< min_error<< " 总点数 " << num_points <<endl; return Mean_error; // Mean error } // 计算点云的每个点的范数平方 -- 用于计算S尺度 vector<double> computeNormSquared(const vector<Vector3d>& cloud) { vector<double> norms; for (const auto& point : cloud) { double normSquared = 0.0; for (int i = 0; i < point.size(); ++i) { normSquared += point[i] * point[i]; } norms.push_back(normSquared); } return norms; } // 核心公式 3d-3d sbd分解计算 /* 输入点云 一般是SLAM vo的enu坐标点 vector<Vector3d>& source_points, 目标点云 GNSS的enu坐标点 const vector<Vector3d>& target_points, 输出 尺度 double& scale, 旋转 Matrix3d& R, 位移 Vector3d& t 结果使用 target_points=scale*R*source_points+t */ void API_ICP_3D_3D_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, double& scale, Matrix3d& R, Vector3d& t) { int N = source_points.size(); // 计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 计算尺度因子 s 方法2 // 计算点云的每个点的范数平方 double sum_source = 0.0; double sum_target = 0.0; for (size_t i = 0; i < N; ++i) { sum_source += centered_source_points[i].norm(); // norm---sqrt(x^2+y^2+z^2) sum_target += centered_target_points[i].norm(); } double scale_ = sum_target /sum_source; scale = scale_; // vector<double> source_norm = computeNormSquared(centered_source_points); // 每一行的 source_norm[i]= x^2+y^2+z^2 // vector<double> target_norm = computeNormSquared(centered_target_points); // double sum_source = 0.0; // double sum_target = 0.0; // for (size_t i = 0; i < N; ++i) { // sum_source += sqrt(source_norm[i]) ; // sum_target += sqrt(target_norm[i]) ; // } // double scale_ = sum_target /sum_source; // scale = scale_; // 计算尺度因子 s 方法1 // double trace = 0.0; // for (int i = 0; i < N; ++i) { // trace += centered_target_points[i].transpose() * R * centered_source_points[i]; // } // double src_norm = 0.0; // for (int i = 0; i < N; ++i) { // src_norm += centered_source_points[i].squaredNorm(); // } // scale = trace / src_norm; // 计算协方差矩阵 H Matrix3d H = Matrix3d::Zero(); for (int i = 0; i < N; ++i) { // 是否乘上scale没有区别 centered_target_points H += centered_target_points[i] * centered_source_points[i].transpose(); //H += scale*centered_source_points[i] * centered_target_points[i].transpose(); } // 使用奇异值分解H SVD JacobiSVD<Matrix3d> svd(H, ComputeFullU | ComputeFullV); Matrix3d U = svd.matrixU(); Matrix3d V = svd.matrixV(); // 计算旋转矩阵R R = U* V.transpose() ; // src到dis点变换关系 对应后面计算t if (U.determinant() * V.determinant() < 0) // R的行列式<0 取反 { for (int x = 0; x < 3; ++x) { U(x, 2) *= -1; } R = U* V.transpose() ; } //R = V * U.transpose(); // 计算平移向量t 两个质心算位移 t = centroid_target - scale * R * centroid_source; } void API_ransac_ICP_3D_3D_sRt_inliner_sR(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 const int random_poinst_num, double& best_scale, Matrix3d& best_R, Vector3d& best_t) { // 1 数据准备 int best_inlier_count = 0; // 最大内点数 int N = source_points.size(); //cout<< "总点数 " << N << endl; // 全体点计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 全体点中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 3使用ransc 找出内点剔除外点,计算更加准确的sRt // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, N - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen); source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次随机计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; vector<Vector3d> source_points_inliner; vector<Vector3d> target_points_inliner; for (int i = 0; i < N; ++i) { Vector3d centered_source2target_points_i = scale*R*centered_source_points[i];// 使用本次随机抽取样本计算的sRt结果 ,对所有原始点的去中心点变换到目标点的中心点 尺度和旋转位置 double source_norm_i=centered_source2target_points_i.norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) double target_norm_i=centered_target_points[i].norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) double error = abs(source_norm_i - target_norm_i);//计算误差 如果目标点是GNSS的enu坐标 单位是米 if (error < error_threshold) {// 取3米 inlier_count++; // 记录本次内点数目 // 找出内点集合 source_points_inliner.push_back(source_points[i]); target_points_inliner.push_back(target_points[i]); } } // 如果本次内点多余历史最好内点数目 本次结果作为最好结果 if (inlier_count > best_inlier_count) { API_ICP_3D_3D_sRt(source_points_inliner,target_points_inliner, scale, R, t);// 使用内点再次计算最优的srt best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; if(iteration==num_iterations-1) cout<<"最大内点数:" <<inlier_count <<endl; } } } void Get_TestValue(vector<Vector3d>& source_points ,vector<Vector3d>& target_points,double currentScale,Matrix3d currentR,Vector3d currentT){ //1-1 随机数产生原始点 std::random_device rd; // 使用 std::random_device 生成真随机数种子 std::mt19937 gen(rd());// 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 uniform_int_distribution<int> randanm_once1(0, 100);//定义一个均匀整数分布 int random_number = randanm_once1(gen);// 生成随机数 int N = 100; // 100个点 float per=0.01;// 假设10%点是对的 double noise_error=10;// 噪声范围 //1-2 随机创建原始点 for (int i = 0; i < N; ++i) { source_points.push_back(Vector3d(randanm_once1(gen), randanm_once1(gen), randanm_once1(gen))); } //2 设置目标点 = 原始点+噪声 //2-1 设置随机噪声 std::uniform_int_distribution<int> randanm_once2(-noise_error/2, noise_error/2); //int random_number = randanm_once2(gen); for(int i=0;i<N;i++){ Vector3d dst_i = currentScale * currentR * source_points[i] + currentT;// 目标点=s*R*原始点+t Vector3d noise(randanm_once2(gen) ,randanm_once2(gen) ,randanm_once2(gen));// 噪声 // 目标点组成 if (i > int(N*(1-per))){target_points.push_back(dst_i );} // 目标点真值 else{target_points.push_back(dst_i + noise);} // 目标点真值+噪声 模拟视觉slam 和GNSS(民用10米 rtk 1-10cm)本身误差 // cout<< i << " " // <<" source_points: "<< source_points[i][0]<< " "<<source_points[i][1]<<" "<<source_points[i][2] // <<" noise: "<< noise[0]<< " "<<noise[1]<<" "<<noise[2] // <<" target_points: "<< dst_i[0]<< " "<<dst_i[1]<<" "<<dst_i[2] // <<" target_points_niose: "<< target_points[i][0]<< " "<<target_points[i][1]<<" "<<target_points[i][2] // << endl; } // 目标点真值+超级噪声 (模拟视觉SLAM某次错误的位姿)加上几个特别的离群来捣乱 验证内点的筛选重要性 target_points[50]+=(Vector3d){100,100,100}; target_points[60]+=(Vector3d){-100,-100,100}; target_points[70]+=(Vector3d){-100,-100,-100}; target_points[80]+=(Vector3d){-100,100,-100}; }
测试例子2 学习代码
对比了四种不同的优化方式
原始点100个 随机噪声[-5,5] 真值10% 90%加噪声
//1-1 随机数产生原始点 std::random_device rd; // 使用 std::random_device 生成真随机数种子 std::mt19937 gen(rd());// 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 uniform_int_distribution<int> randanm_once1(0, 100);//定义一个均匀整数分布 int random_number = randanm_once1(gen);// 生成随机数 int N = 100; // 100个点 float per=0.01;// 假设10%点是对的 double noise_error=10;// 噪声范围
计入超级离群点干扰
// 目标点真值+超级噪声 (模拟视觉SLAM某次错误的位姿)加上几个特别的离群来捣乱 验证内点的筛选重要性 target_points[50]+=(Vector3d){100,100,100}; target_points[60]+=(Vector3d){-100,-100,100}; target_points[70]+=(Vector3d){-100,-100,-100}; target_points[80]+=(Vector3d){-100,100,-100};
设置的变换矩阵真值
double currentScale=20; //currentR<<1,0,0,0,1,0,0,0,1;// 单位阵 currentR<<0,-1,0,1,0,0,0,0,1; // 旋转90度 currentT<<1000,1000,1000;
优化的参数
//2 设置优化参数 int time_=1000;// 迭代次数 int random_poinst_num=4;// 每次抽取的点数 起码3个点 double inliner_error_thresh=3;// 如果目标点是 GNSS的enu坐标(民用10米 rtk 1-10cm)
最后统计误差
很明显
cout << "========1 全部点参与计算 中等 离群点干扰严重 ==========: " << endl; cout << "========2 ransac 最糟糕 单纯随机性太偶然,不找内点==========: " << endl; cout << "========3 ransac 次优 ransac挑选出内点去掉离群点, 内点整体参与计算sRT,T会引入新的误差 ==========: " << endl; 在变换后的目标点上找内点 因为 t= p_target-s*R*p_source s和 R的误差再次引入t,这导致计算的变换目标点误差更大,进一步导致error更大 cout << "========4 ransac 最优 ransac挑选出内点去掉离群点, 内点整体参与计算sR,相当于1和2的联合优化 ==========: " << endl; 在去中心化的点集合上计算得到内点,只单纯计算s和R,精度更高。
代码2 学习代码
main.cpp
#include <iostream> #include <Eigen/Dense> #include <vector> #include <cmath> #include <random> using namespace Eigen; using namespace std; // 点云类型定义 typedef vector<Vector3d> PointCloud; // 变换点和目标点计算误差 double computeError(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const double scale, const Matrix3d& R, const Vector3d& t) { // Compute transformation error using point-to-point distances double total_error = 0.0; double Mean_error = 0.0; double max_error = 0.0; double min_error = 9999; int num_points = source_points.size(); vector<double> error_; for (int i = 0; i < num_points; ++i) { Vector3d transformed_point = scale * R * source_points[i] + t; double cur_error =(transformed_point - target_points[i]).norm(); error_.push_back(cur_error); } for(auto cur_error : error_){ if(cur_error>max_error){max_error=cur_error;} if(cur_error<min_error){min_error=cur_error;} total_error = total_error+cur_error; //向量的二范数 } Mean_error=total_error / num_points; cout<<"Mean_error " <<Mean_error << " max_error "<< max_error<< " min_error "<< min_error<< " 总点数 " << num_points <<endl; return Mean_error; // Mean error } // 计算点云的每个点的范数平方 -- 用于计算S尺度 vector<double> computeNormSquared(const vector<Vector3d>& cloud) { vector<double> norms; for (const auto& point : cloud) { double normSquared = 0.0; for (int i = 0; i < point.size(); ++i) { normSquared += point[i] * point[i]; } norms.push_back(normSquared); } return norms; } // 核心公式 3d-3d sbd分解计算 /* 输入点云 一般是SLAM vo的enu坐标点 vector<Vector3d>& source_points, 目标点云 GNSS的enu坐标点 const vector<Vector3d>& target_points, 输出 尺度 double& scale, 旋转 Matrix3d& R, 位移 Vector3d& t 结果使用 target_points=scale*R*source_points+t */ void API_ICP_3D_3D_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, double& scale, Matrix3d& R, Vector3d& t) { int N = source_points.size(); // 计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 计算尺度因子 s 方法2 // 计算点云的每个点的范数平方 double sum_source = 0.0; double sum_target = 0.0; for (size_t i = 0; i < N; ++i) { sum_source += centered_source_points[i].norm(); // norm---sqrt(x^2+y^2+z^2) sum_target += centered_target_points[i].norm(); } double scale_ = sum_target /sum_source; scale = scale_; // vector<double> source_norm = computeNormSquared(centered_source_points); // 每一行的 source_norm[i]= x^2+y^2+z^2 // vector<double> target_norm = computeNormSquared(centered_target_points); // double sum_source = 0.0; // double sum_target = 0.0; // for (size_t i = 0; i < N; ++i) { // sum_source += sqrt(source_norm[i]) ; // sum_target += sqrt(target_norm[i]) ; // } // double scale_ = sum_target /sum_source; // scale = scale_; // 计算尺度因子 s 方法1 // double trace = 0.0; // for (int i = 0; i < N; ++i) { // trace += centered_target_points[i].transpose() * R * centered_source_points[i]; // } // double src_norm = 0.0; // for (int i = 0; i < N; ++i) { // src_norm += centered_source_points[i].squaredNorm(); // } // scale = trace / src_norm; // 计算协方差矩阵 H Matrix3d H = Matrix3d::Zero(); for (int i = 0; i < N; ++i) { // 是否乘上scale没有区别 centered_target_points H += centered_target_points[i] * centered_source_points[i].transpose(); //H += scale*centered_source_points[i] * centered_target_points[i].transpose(); } // 使用奇异值分解H SVD JacobiSVD<Matrix3d> svd(H, ComputeFullU | ComputeFullV); Matrix3d U = svd.matrixU(); Matrix3d V = svd.matrixV(); // 计算旋转矩阵R R = U* V.transpose() ; // src到dis点变换关系 对应后面计算t if (U.determinant() * V.determinant() < 0) // R的行列式<0 取反 { for (int x = 0; x < 3; ++x) { U(x, 2) *= -1; } R = U* V.transpose() ; } //R = V * U.transpose(); // 计算平移向量t 两个质心算位移 t = centroid_target - scale * R * centroid_source; } // 不带尺度的计算 /* typedef Vector3d Point3f; void pose_estimation_3d3d ( const vector<Point3f>& pts1, const vector<Point3f>& pts2, Mat& R, Mat& t ) { Point3f p1, p2; // center of mass int N = pts1.size(); for ( int i=0; i<N; i++ ) { p1 += pts1[i]; p2 += pts2[i]; } p1 = Point3f( Vec3f(p1) / N); p2 = Point3f( Vec3f(p2) / N); vector<Point3f> q1 ( N ), q2 ( N ); // remove the center for ( int i=0; i<N; i++ ) { q1[i] = pts1[i] - p1; q2[i] = pts2[i] - p2; } // compute q1*q2^T Eigen::Matrix3d W = Eigen::Matrix3d::Zero(); for ( int i=0; i<N; i++ ) { W += Eigen::Vector3d ( q1[i].x, q1[i].y, q1[i].z ) * Eigen::Vector3d ( q2[i].x, q2[i].y, q2[i].z ).transpose(); } cout<<"W="<<W<<endl; // SVD on W Eigen::JacobiSVD<Eigen::Matrix3d> svd ( W, Eigen::ComputeFullU|Eigen::ComputeFullV ); Eigen::Matrix3d U = svd.matrixU(); Eigen::Matrix3d V = svd.matrixV(); if (U.determinant() * V.determinant() < 0) { for (int x = 0; x < 3; ++x) { U(x, 2) *= -1; } } cout<<"U="<<U<<endl; cout<<"V="<<V<<endl; Eigen::Matrix3d R_ = U* ( V.transpose() ); Eigen::Vector3d t_ = Eigen::Vector3d ( p1.x, p1.y, p1.z ) - R_ * Eigen::Vector3d ( p2.x, p2.y, p2.z ); // convert to cv::Mat R = ( Mat_<double> ( 3,3 ) << R_ ( 0,0 ), R_ ( 0,1 ), R_ ( 0,2 ), R_ ( 1,0 ), R_ ( 1,1 ), R_ ( 1,2 ), R_ ( 2,0 ), R_ ( 2,1 ), R_ ( 2,2 ) ); t = ( Mat_<double> ( 3,1 ) << t_ ( 0,0 ), t_ ( 1,0 ), t_ ( 2,0 ) ); } */ /* 随机样本选择: 从输入数据中随机选择一个小的子集作为初始样本集合(比如选择3个点来估计初始的变换参数)。 模型拟合: 使用选定的样本集合来估计模型的参数(比如平移、旋转等变换参数)。 一致性检验: 将所有其他数据点与估计模型进行比较,计算它们与模型之间的拟合误差或距离。如果数据点与模型的拟合误差在某个阈值范围内,则认为它们是一致的(inliers)。 重复迭代: 重复上述步骤多次(通常是预先设定的次数),选择具有最大一致性(最多inliers)的模型参数作为最终的拟合结果。 */ void API_ransac_ICP_3D_3D_sRt_noinliner(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 const int random_poinst_num, double& best_scale, Matrix3d& best_R, Vector3d& best_t) { int num_points = source_points.size(); int best_inlier_count = 0; // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, num_points - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen); source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; // 使用本次随机抽取样本计算的sRt结果 ,对所有原始点变换,计算误差是否在阈值内,统计内点最多的情况,记录对应sRt for (int i = 0; i < num_points; ++i) { Vector3d transformed_point = scale * R * source_points[i] + t; double error = (transformed_point - target_points[i]).norm(); //向量的二范数 //double error = computeError({source_points[i]}, {target_points[i]}, scale, R, t); if (error < error_threshold) { inlier_count++; } } // Update best model if this model is better if (inlier_count > best_inlier_count) { if(iteration==num_iterations-1) cout<<"最大内点数:" <<inlier_count <<endl; best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; } } } void API_ransac_ICP_3D_3D_sRt_inliner_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 const int random_poinst_num, double& best_scale, Matrix3d& best_R, Vector3d& best_t) { int num_points = source_points.size(); int best_inlier_count = 0; // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, num_points - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen); source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; vector<Vector3d> source_points_inliner; vector<Vector3d> target_points_inliner; // 使用本次随机抽取样本计算的sRt结果 ,对所有原始点变换,计算误差是否在阈值内,统计内点最多的情况,记录对应sRt for (int i = 0; i < num_points; ++i) { //double error = computeError({source_points[i]}, {target_points[i]}, scale, R, t); Vector3d transformed_point = scale * R * source_points[i] + t; double error = (transformed_point - target_points[i]).norm(); //向量的二范数 if (error < error_threshold) { inlier_count++; // 找出内点 source_points_inliner.push_back(source_points[i]); target_points_inliner.push_back(target_points[i]); } } // Update best model if this model is better if (inlier_count > best_inlier_count) { API_ICP_3D_3D_sRt(source_points_inliner,target_points_inliner, scale, R, t);// 使用内点再次计算最优的srt best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; if(iteration==num_iterations-1) cout<<"最大内点数:" <<inlier_count <<endl; } } } void API_ransac_ICP_3D_3D_sRt_inliner_sR(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, const int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 const int random_poinst_num, double& best_scale, Matrix3d& best_R, Vector3d& best_t) { // 1 数据准备 int best_inlier_count = 0; // 最大内点数 int N = source_points.size(); //cout<< "总点数 " << N << endl; // 全体点计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 全体点中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 3使用ransc 找出内点剔除外点,计算更加准确的sRt // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, N - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen); source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次随机计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; vector<Vector3d> source_points_inliner; vector<Vector3d> target_points_inliner; for (int i = 0; i < N; ++i) { Vector3d centered_source2target_points_i = scale*R*centered_source_points[i];// 使用本次随机抽取样本计算的sRt结果 ,对所有原始点的去中心点变换到目标点的中心点 尺度和旋转位置 double source_norm_i=centered_source2target_points_i.norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) double target_norm_i=centered_target_points[i].norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) double error = abs(source_norm_i - target_norm_i);//计算误差 如果目标点是GNSS的enu坐标 单位是米 if (error < error_threshold) {// 取3米 inlier_count++; // 记录本次内点数目 // 找出内点集合 source_points_inliner.push_back(source_points[i]); target_points_inliner.push_back(target_points[i]); } } // 如果本次内点多余历史最好内点数目 本次结果作为最好结果 if (inlier_count > best_inlier_count) { API_ICP_3D_3D_sRt(source_points_inliner,target_points_inliner, scale, R, t);// 使用内点再次计算最优的srt best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; if(iteration==num_iterations-1) cout<<"最大内点数:" <<inlier_count <<endl; } } } void Get_TestValue(vector<Vector3d>& source_points ,vector<Vector3d>& target_points,double currentScale,Matrix3d currentR,Vector3d currentT){ //1-1 随机数产生原始点 std::random_device rd; // 使用 std::random_device 生成真随机数种子 std::mt19937 gen(rd());// 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 uniform_int_distribution<int> randanm_once1(0, 100);//定义一个均匀整数分布 int random_number = randanm_once1(gen);// 生成随机数 int N = 100; // 100个点 float per=0.01;// 假设10%点是对的 double noise_error=10;// 噪声范围 //1-2 随机创建原始点 for (int i = 0; i < N; ++i) { source_points.push_back(Vector3d(randanm_once1(gen), randanm_once1(gen), randanm_once1(gen))); } //2 设置目标点 = 原始点+噪声 //2-1 设置随机噪声 std::uniform_int_distribution<int> randanm_once2(-noise_error/2, noise_error/2); //int random_number = randanm_once2(gen); for(int i=0;i<N;i++){ Vector3d dst_i = currentScale * currentR * source_points[i] + currentT;// 目标点=s*R*原始点+t Vector3d noise(randanm_once2(gen) ,randanm_once2(gen) ,randanm_once2(gen));// 噪声 // 目标点组成 if (i > int(N*(1-per))){target_points.push_back(dst_i );} // 目标点真值 else{target_points.push_back(dst_i + noise);} // 目标点真值+噪声 模拟视觉slam 和GNSS(民用10米 rtk 1-10cm)本身误差 // cout<< i << " " // <<" source_points: "<< source_points[i][0]<< " "<<source_points[i][1]<<" "<<source_points[i][2] // <<" noise: "<< noise[0]<< " "<<noise[1]<<" "<<noise[2] // <<" target_points: "<< dst_i[0]<< " "<<dst_i[1]<<" "<<dst_i[2] // <<" target_points_niose: "<< target_points[i][0]<< " "<<target_points[i][1]<<" "<<target_points[i][2] // << endl; } // 目标点真值+超级噪声 (模拟视觉SLAM某次错误的位姿)加上几个特别的离群来捣乱 验证内点的筛选重要性 target_points[50]+=(Vector3d){100,100,100}; target_points[60]+=(Vector3d){-100,-100,100}; target_points[70]+=(Vector3d){-100,-100,-100}; target_points[80]+=(Vector3d){-100,100,-100}; } // 测试3 void Test_3(){ //1-1 创建测试点 vector<Vector3d> source_points; vector<Vector3d> target_points; // 1-2 设置变换矩阵 Matrix3d currentR; Vector3d currentT; double currentScale=20; //currentR<<1,0,0,0,1,0,0,0,1;// 单位阵 currentR<<0,-1,0,1,0,0,0,0,1; // 旋转90度 currentT<<1000,1000,1000; Get_TestValue(source_points,target_points,currentScale,currentR,currentT); //2 设置优化参数 int time_=1000;// 迭代次数 int random_poinst_num=4;// 每次抽取的点数 起码3个点 double inliner_error_thresh=3;// 如果目标点是 GNSS的enu坐标(民用10米 rtk 1-10cm) // 统计四种方法的优缺点 double t_error1,t_error2,t_error3,t_error4; double s1,s2,s3,s4; //3-0 记录最优结果 Variables to store results double scale; Matrix3d R; Vector3d t; //3-1 开始计算 Perform RANSAC-based 3D-3D ICP // 100 迭代次数 误差阈值 1 cout << "========1 全部点参与计算 中等 离群点干扰严重 ==========: " << endl; API_ICP_3D_3D_sRt(source_points, target_points, scale, R, t); // Output results cout << "Scale factor s: " << scale << endl; cout << "Rotation matrix R:\n" << R << endl; cout << "Translation vector t:\n" << t << endl; t_error1= sqrt(pow((currentT[0]-t[0]),2)+pow((currentT[1]-t[1]),2)+pow((currentT[2]-t[2]),2)); cout << "位移的error:\n" << t_error1 << endl; computeError(source_points, target_points, scale, R, t); s1=currentScale-scale; //3-2 开始计算 Perform RANSAC-based 3D-3D ICP cout << "========2 ransac 最糟糕 单纯随机性太偶然,不找内点==========: " << endl; // 100 迭代次数 误差阈值 1 API_ransac_ICP_3D_3D_sRt_noinliner(source_points, target_points, time_, inliner_error_thresh,random_poinst_num, scale, R, t); // Output results cout << "Scale factor s: " << scale << endl; cout << "Rotation matrix R:\n" << R << endl; cout << "Translation vector t:\n" << t << endl; t_error2= sqrt(pow((currentT[0]-t[0]),2)+pow((currentT[1]-t[1]),2)+pow((currentT[2]-t[2]),2)); cout << "位移的error:\n" << t_error2 << endl; computeError(source_points, target_points, scale, R, t); s2=currentScale-scale; cout << "========3 ransac 次优 ransac挑选出内点去掉离群点, 内点整体参与计算sRT,T会引入新的误差 ==========: " << endl; API_ransac_ICP_3D_3D_sRt_inliner_sRt(source_points, target_points, time_, inliner_error_thresh,random_poinst_num, scale, R, t); cout << "Scale factor s: " << scale << endl; cout << "Rotation matrix R:\n" << R << endl; cout << "Translation vector t:\n" << t << endl; t_error3= sqrt(pow((currentT[0]-t[0]),2)+pow((currentT[1]-t[1]),2)+pow((currentT[2]-t[2]),2)); cout << "位移的error:\n" << t_error3 << endl; computeError(source_points, target_points, scale, R, t); s3=currentScale-scale; //cout << "位移的error: 1 all " << t_error1 << " 2 ransc " <<t_error2 << " 3 ranc+inliner "<<t_error3 << endl; cout << "========4 ransac 最优 ransac挑选出内点去掉离群点, 内点整体参与计算sR,相当于1和2的联合优化 ==========: " << endl; scale=0; R; t={0,0,0}; API_ransac_ICP_3D_3D_sRt_inliner_sR(source_points, target_points, time_, inliner_error_thresh,random_poinst_num, scale, R, t); cout << "Scale factor s: " << scale << endl; cout << "Rotation matrix R:\n" << R << endl; cout << "Translation vector t:\n" << t << endl; t_error4= sqrt(pow((currentT[0]-t[0]),2)+pow((currentT[1]-t[1]),2)+pow((currentT[2]-t[2]),2)); cout << "位移的error:\n" << t_error4 << endl; computeError(source_points, target_points, scale, R, t); s4=currentScale-scale; cout << "位移的error: 1 all " << t_error1 << " 2 ransc " <<t_error2 << " 3 ranc+inliner-sRt "<<t_error3 << " t_error4-sR "<<t_error4 <<endl; cout << "尺度的error: 1 all " << s1 << " 2 ransc " <<s2 << " 3 ranc+inliner-sRt "<<s3 << " t_error4-sRt "<<s4 <<endl; } int main() { Test_3(); return 0; }
OPENVSLAm修改后的代码
API_3D_3D_sRt.h
#ifndef API_3D_3D_sRt_H #define API_3D_3D_sRt_H #include <iostream> #include <Eigen/Dense> #include <vector> #include <cmath> #include <random> using namespace Eigen; using namespace std; // 计算均方根误差 double cal_rmse_t_v3s_v3s( vector<Eigen::Vector3d> &groundtruth,vector<Eigen::Vector3d> &estimated); // 核心公式 3d-3d sbd分解计算 /* 单纯的根据3D点匹配对计算整体sRt关系 输入点云 一般是SLAM vo的enu坐标点 vector<Vector3d>& source_points, 目标点云 GNSS的enu坐标点 const vector<Vector3d>& target_points, 输出 尺度 double& scale, 旋转 Matrix3d& R, 位移 Vector3d& t 结果使用 target_points=scale*R*source_points+t 重点 https://www.cnblogs.com/gooutlook/p/18353030 现成的库直接调用 但是没有都没有进行随机一致采样 */ void API_ICP_3D_3D_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, double& scale, Matrix3d& R, Vector3d& t) ; // 使用中心化点云找内点 误差目前理想 API_ICP_3D_3D_sRt + 随机一致性优化 void API_ransac_ICP_3D_3D_sRt_inliner_sR(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 double& best_scale, Matrix3d& best_R, Vector3d& best_t); // 使用直接变换点找内点 误差较大 API_ICP_3D_3D_sRt + 随机一致性优化 void API_ransac_ICP_3D_3D_sRt_inliner_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 double& best_scale, Matrix3d& best_R, Vector3d& best_t) ; #endif
API_3D_3D_sRt.cc
#ifndef API_3D_3D_sRt_CPP #define API_3D_3D_sRt_CPP #include "API_3D_3D_sRt.h" /* 1. RANSAC随机采样一致算法 这种方法的目的是,从所有数据中选择正确的数据,用于估计。为了方便,先给几个定义。 点:每一个数据,SLAM里指的是匹配的点对 野值/外点:错误的点 内点:正确的点 内点集:内点的集合 外点集:外点的集合 模型:带估计的参数 :估计模型所需要的最小点数 :所有的点形成的点集 显然,内点集就是我们想要找的正确数据。RANSAC的想法是从点集中随机的选择出 个点,估计出一个模型,查看剩余点是否符合这个模型。如果大部分点都符合这个模型,就相当于我们找到了一个合适的模型,自然的符合模型的点就是内点啦。由于是随机选择,一次可能难以找到正确的模型,因此需要多次才可以。下面给出完整的RANSAC算法。 STEP 1. 随机的从 中选择 个点作为一个样本,估计出模型 STEP 2. 确定在模型距离阈值 内的数据点集 , 即可为内点集 STEP 3. 如果 的大小大于某个阈值 ,用所有内点 重新估计一个更精确的模型 STEP 4. 如果 的大小小于阈值 ,则重复1步骤 STEP 5. 经过 次试验,选择最大一致集 ,并用 的所有点重新估计模型 RANSAC里面有三个重要的参数需要确定,分别是 、 、 。 */ double cal_rmse_t_v3s_v3s( vector<Eigen::Vector3d> &groundtruth,vector<Eigen::Vector3d> &estimated){ // // compute rmse double rmse = 0; for (size_t i = 0; i < estimated.size(); i++) { Eigen::Vector3d p1 = estimated[i]; Eigen::Vector3d p2 = groundtruth[i]; Eigen::Vector3d error = (p2- p1); rmse += error.squaredNorm();//用 squaredNorm() 计算误差平方和 norm平方根 } rmse = rmse / double(estimated.size()); rmse = sqrt(rmse); //cout << "RMSE = " << rmse << endl; return rmse; } // 核心公式 3d-3d sbd分解计算 /* 输入点云 一般是SLAM vo的enu坐标点 vector<Vector3d>& source_points, 目标点云 GNSS的enu坐标点 const vector<Vector3d>& target_points, 输出 尺度 double& scale, 旋转 Matrix3d& R, 位移 Vector3d& t 结果使用 target_points=scale*R*source_points+t */ void API_ICP_3D_3D_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, double& scale, Matrix3d& R, Vector3d& t) { int N = source_points.size(); // 计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 计算尺度因子 s 方法2 // 计算点云的每个点的范数平方 double sum_source = 0.0; double sum_target = 0.0; for (size_t i = 0; i < N; ++i) { sum_source += centered_source_points[i].norm(); // norm---sqrt(x^2+y^2+z^2) sum_target += centered_target_points[i].norm(); } double scale_ = sum_target /sum_source; scale = scale_; // vector<double> source_norm = computeNormSquared(centered_source_points); // 每一行的 source_norm[i]= x^2+y^2+z^2 // vector<double> target_norm = computeNormSquared(centered_target_points); // double sum_source = 0.0; // double sum_target = 0.0; // for (size_t i = 0; i < N; ++i) { // sum_source += sqrt(source_norm[i]) ; // sum_target += sqrt(target_norm[i]) ; // } // double scale_ = sum_target /sum_source; // scale = scale_; // 计算尺度因子 s 方法1 // double trace = 0.0; // for (int i = 0; i < N; ++i) { // trace += centered_target_points[i].transpose() * R * centered_source_points[i]; // } // double src_norm = 0.0; // for (int i = 0; i < N; ++i) { // src_norm += centered_source_points[i].squaredNorm(); // } // scale = trace / src_norm; // 计算协方差矩阵 H Matrix3d H = Matrix3d::Zero(); for (int i = 0; i < N; ++i) { // 是否乘上scale没有区别 centered_target_points H += centered_target_points[i] * centered_source_points[i].transpose(); //H += scale*centered_source_points[i] * centered_target_points[i].transpose(); } // 使用奇异值分解H SVD JacobiSVD<Matrix3d> svd(H, ComputeFullU | ComputeFullV); Matrix3d U = svd.matrixU(); Matrix3d V = svd.matrixV(); // 计算旋转矩阵R R = U* V.transpose() ; // src到dis点变换关系 对应后面计算t if (U.determinant() * V.determinant() < 0) // R的行列式<0 取反 { for (int x = 0; x < 3; ++x) { U(x, 2) *= -1; } R = U* V.transpose() ; } //R = V * U.transpose(); // 计算平移向量t 两个质心算位移 t = centroid_target - scale * R * centroid_source; } //https://zhuanlan.zhihu.com/p/62175983 // 使用 sr变换原始点的去中心点后,和目标点的去中心点计算的误差。 // 1随机抽取计算SRt 2 使用去中心点集合找内点,3使用最大内点数计算最有SRT void API_ransac_ICP_3D_3D_sRt_inliner_sR(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 double& best_scale, Matrix3d& best_R, Vector3d& best_t) { // 1 数据准备 int best_inlier_count = 0; // 最大内点数 int N = source_points.size(); //cout<< "总点数 " << N << endl; int num_rancek_temp = N * (N - 1) / 2; // 随机挑选2个点验证 一共多少次 if(num_rancek_temp<num_iterations) // 限制下最大次数 节省时间 1000次足以找出 { num_iterations=num_rancek_temp;//ransac随机抽取验证次数 } // 全体点计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 全体点中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 3使用ransc 找出内点剔除外点,计算更加准确的sRt // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, N - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen);// 随机数 source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次随机计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; vector<Vector3d> source_points_inliner; vector<Vector3d> target_points_inliner; for (int i = 0; i < N; ++i) { Vector3d centered_source2target_points_i = scale*R*centered_source_points[i];// 使用本次随机抽取样本计算的sRt结果 ,对所有原始点的去中心点变换到目标点的中心点 尺度和旋转位置 double source_norm_i=centered_source2target_points_i.norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) double target_norm_i=centered_target_points[i].norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) //Vector3d source_norm_i=centered_source2target_points_i; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) //Vector3d target_norm_i=centered_target_points[i]; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // 卡方分布中 3纬度变量 (x1-x1')^2+(y1-y1')^2+(z1-z1')^2 < 7.815 95% // 卡方分布中 2纬度变量 (x1-x1')^2+(y1-y1')^2 < 5.99 95% double error = abs(source_norm_i - target_norm_i);//计算误差 如果目标点是GNSS的enu坐标 单位是米 if (error < error_threshold) {// 取3米 inlier_count++; // 记录本次内点数目 // 找出内点集合 source_points_inliner.push_back(source_points[i]); target_points_inliner.push_back(target_points[i]); } } // for (int i = 0; i < N; ++i) { // Vector3d centered_source2target_points_i = scale*R*centered_source_points[i];// 使用本次随机抽取样本计算的sRt结果 ,对所有原始点的去中心点变换到目标点的中心点 尺度和旋转位置 // //double source_norm_i=centered_source2target_points_i.norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // //double target_norm_i=centered_target_points[i].norm(); //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // Vector3d source_norm_i=centered_source2target_points_i; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // Vector3d target_norm_i=centered_target_points[i]; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // // 卡方分布中 3纬度变量 (x1-x1')^2+(y1-y1')^2+(z1-z1')^2 < 7.815 95% // // 卡方分布中 2纬度变量 (x1-x1')^2+(y1-y1')^2 < 5.99 95% // Vector3d error_v3d = (source_norm_i - target_norm_i);//计算误差 如果目标点是GNSS的enu坐标 单位是米 // double error=error_v3d.squaredNorm(); // cout<< num_iterations <<" "<<i <<" "<<error <<endl; // if (error < error_threshold) {// 取3米 // inlier_count++; // 记录本次内点数目 // // 找出内点集合 // source_points_inliner.push_back(source_points[i]); // target_points_inliner.push_back(target_points[i]); // } // } // 如果本次内点多余历史最好内点数目 本次结果作为最好结果 if (inlier_count > best_inlier_count) { API_ICP_3D_3D_sRt(source_points_inliner,target_points_inliner, scale, R, t);// 使用内点再次计算最优的srt best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; } if(iteration==num_iterations-1) { cout<< "API_3D_3D_sRt sRt计算 总数点数 "<< N << " 最大内点数: " << best_inlier_count <<endl; } } } // 使用 srt变换原始点后,计算的误差。 // 不建议使用 ,t是由sR计算出来的,sr本身就是估计的,在引入t误差更大,所以还是用sr变换原始点的去中心点集合,和目标点的去中心点集合整体算误差。 void API_ransac_ICP_3D_3D_sRt_inliner_sRt(const vector<Vector3d>& source_points, const vector<Vector3d>& target_points, int num_iterations, //ransac随机抽取验证次数 const double error_threshold, // 误差阈值 3 double& best_scale, Matrix3d& best_R, Vector3d& best_t) { // 1 数据准备 int best_inlier_count = 0; // 最大内点数 int N = source_points.size(); //cout<< "总点数 " << N << endl; int num_rancek_temp = N * (N - 1) / 2; // 随机挑选2个点验证 一共多少次 if(num_rancek_temp<num_iterations) // 限制下最大次数 节省时间 1000次足以找出 { num_iterations=num_rancek_temp;//ransac随机抽取验证次数 } // 全体点计算质心 Vector3d centroid_source = Vector3d::Zero(); Vector3d centroid_target = Vector3d::Zero(); for(int i=0;i<N;i++){ centroid_source += source_points[i]; centroid_target += target_points[i]; } centroid_source /= N; centroid_target /= N; /// 全体点中心化点云 vector<Vector3d> centered_source_points (N); vector<Vector3d> centered_target_points (N); for(int i=0;i<N;i++){ centered_source_points[i]= source_points[i] - centroid_source; centered_target_points[i]= target_points[i] - centroid_target; } // 3使用ransc 找出内点剔除外点,计算更加准确的sRt // 使用 std::random_device 生成真随机数种子 std::random_device rd; // 使用梅森旋转算法引擎 mt19937,以 rd() 作为种子 std::mt19937 gen(rd()); // 定义一个均匀整数分布,范围是[1, 100] //std::uniform_int_distribution<int> dist(1, 100); uniform_int_distribution<int> dist(0, N - 1); // 生成随机数 //int random_number = dist(gen); // RANSAC loop num_iterations 迭代次数 for (int iteration = 0; iteration < num_iterations; ++iteration) { // Randomly select 3 points to form a minimal sample vector<Vector3d> source_points_temp; vector<Vector3d> target_points_temp; for (int num=0;num<4;num++){ // 最少3个点 取4个点 int idx = dist(gen);// 随机数 source_points_temp.push_back(source_points[idx]); target_points_temp.push_back(target_points[idx]); } // 本次随机计算结果 Perform ICP with these initial parameters double scale; Matrix3d R; Vector3d t; API_ICP_3D_3D_sRt(source_points_temp,target_points_temp, scale, R, t); // Compute error with all points and count inliers int inlier_count = 0; vector<Vector3d> source_points_inliner; vector<Vector3d> target_points_inliner; for (int i = 0; i < N; ++i) { Vector3d source_target_point_i = scale*R*source_points[i]+t;// 使用本次随机抽取样本计算的sRt结果 ,对所有原始点的去中心点变换到目标点的中心点 尺度和旋转位置 Vector3d target_point_i=target_points[i]; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) //Vector3d source_norm_i=centered_source2target_points_i; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) //Vector3d target_norm_i=centered_target_points[i]; //去中心化的点长2范数度 norm---sqrt(x^2+y^2+z^2) // 卡方分布中 3纬度变量 (x1-x1')^2+(y1-y1')^2+(z1-z1')^2 < 7.815 95% // 卡方分布中 2纬度变量 (x1-x1')^2+(y1-y1')^2 < 5.99 95% double error = (source_target_point_i - target_point_i).norm();//计算误差 如果目标点是GNSS的enu坐标 单位是米 error=error*error; if (error < error_threshold) {// 取3米 inlier_count++; // 记录本次内点数目 // 找出内点集合 source_points_inliner.push_back(source_points[i]); target_points_inliner.push_back(target_points[i]); } } // 如果本次内点多余历史最好内点数目 本次结果作为最好结果 if (inlier_count > best_inlier_count) { API_ICP_3D_3D_sRt(source_points_inliner,target_points_inliner, scale, R, t);// 使用内点再次计算最优的srt best_inlier_count = inlier_count; best_scale = scale; best_R = R; best_t = t; } if(iteration==num_iterations-1) { cout<< "API_3D_3D_sRt == srt计算 总数点数 "<< N << " 最大内点数: " << inlier_count <<endl; } } } #endif
API_G2O_BA.h
#ifndef G2OBA_H #define G2OBA_H #include "openvslam/type.h" #include <g2o/core/base_vertex.h> #include <g2o/core/base_binary_edge.h> #include <g2o/core/base_unary_edge.h> #include <g2o/types/slam3d/se3quat.h> #include "openvslam/optimize/g2o/se3/shot_vertex.h" #include <g2o/core/robust_kernel_impl.h> #include <iostream> using namespace std; namespace openvslam { namespace optimize { namespace g2o { namespace se3 { // class shot_vertex final : public ::g2o::BaseVertex<6, ::g2o::SE3Quat> { // public: // EIGEN_MAKE_ALIGNED_OPERATOR_NEW // shot_vertex() // : BaseVertex<6, ::g2o::SE3Quat>() {} // bool read(std::istream& is) { // Vec7_t estimate; // for (unsigned int i = 0; i < 7; ++i) { // is >> estimate(i); // } // ::g2o::SE3Quat g2o_cam_pose_wc; // g2o_cam_pose_wc.fromVector(estimate); // setEstimate(g2o_cam_pose_wc.inverse()); // return true; // } // bool write(std::ostream& os) const { // ::g2o::SE3Quat g2o_cam_pose_wc(estimate().inverse()); // for (unsigned int i = 0; i < 7; ++i) { // os << g2o_cam_pose_wc[i] << " "; // } // return os.good(); // } // void setToOriginImpl() override { // _estimate = ::g2o::SE3Quat(); // } // void oplusImpl(const number_t* update_) override { // Eigen::Map<const Vec6_t> update(update_); // setEstimate(::g2o::SE3Quat::exp(update) * estimate()); // } // }; /* Matrix3D Rbc=toEigenMatrix3d(se3bc.R()); Vector3D tbc=toG2oVector3D(se3bc.tvec); g2o::SE3Quat Tbc=g2o::SE3Quat(Rbc, tbc); 使用SE3Quat成员函数to_homogeneous_matrix()将SE3Quat->Eigen::Matrix<double,4,4> Eigen::MatrixXd toEigenMatrixXd(const cv::Mat &cvMat) { Eigen::MatrixXd eigenMat; eigenMat.resize(cvMat.rows, cvMat.cols); for (int i=0; i<cvMat.rows; i++) for (int j=0; j<cvMat.cols; j++) eigenMat(i,j) = cvMat.at<float>(i,j); return eigenMat; } */ /// g2o edge 观测值维度3 类型 Vector3d 优化节点第一个 VertexPose class GNSSConstraint_sRt_Edge : public ::g2o::BaseUnaryEdge<3, Eigen::Vector3d, openvslam::optimize::g2o::se3::shot_vertex> { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; GNSSConstraint_sRt_Edge(Eigen::Matrix4d T_VO_to_GNSS_ENU ) : _T_VO_to_GNSS_ENU(T_VO_to_GNSS_ENU) {} virtual void computeError() override { const openvslam::optimize::g2o::se3::shot_vertex* v = static_cast<const shot_vertex*>(_vertices[0]); ::g2o::SE3Quat cam_pose = v->estimate(); // 位姿节点优化后的估计值 = g2o_cam_pose_cw 世界到相机 // openvslam G2O shot_vertex.cc 这里estimate = g2o_cam_pose_wc.inverse() 也就是世界到相机的变换矩阵 // 正常情况下,单纯考虑GNSS计算误差方便性,位姿节点的estimate应该直接就是全局位置 ,也就是g2o_cam_pose_wc 相机到世界变换矩阵。这样可以直接和观测值相减 // 但是openvslam这么设计位姿节点的estimate , 是为了便于重投影误差计算 ,直接使用estimate= g2o_cam_pose_cw (世界到相机)将世界坐标系下的地图点变换到相机坐标系下的3D点 // g2o_cam_pose_wc 相机到世界 = g2o_cam_pose_cw.inverse() // g2o_cam_pose_cw 世界到相机 = g2o_cam_pose_wc.inverse() const Eigen::Matrix3d R_cw = cam_pose.rotation().toRotationMatrix(); const Eigen::Vector3d t_cw = cam_pose.translation(); // T=[R t] T逆矩阵=[R逆 -R逆*t] // [0 1] [0 1 ] const Eigen::Matrix3d R_wc = R_cw.transpose() ; const Eigen::Vector3d t_wc = -R_cw.transpose() * t_cw; // 相机位姿(R t)在世界坐标系位置x y z Eigen::Matrix3d R_cw_to_gw =_T_VO_to_GNSS_ENU.block<3, 3>(0, 0); // 包含了尺度 Eigen::Vector3d t_cw_to_gw = _T_VO_to_GNSS_ENU.block<3, 1>(0, 3);// Eigen::Vector3d t_gw = R_cw_to_gw*t_wc+t_cw_to_gw;// t_wc相机在VO-enu坐标系下的位置变换到GNSS-enu坐标系下 _error = t_gw - _measurement; //_measurement GNSS 真值在设定的参考点下的ENU世界坐标X Y Z 调用setmeasurement() 设置 Vector3d } // virtual void linearizeOplus() override { // ::g2o::BaseUnaryEdge<3, Eigen::Vector3d, openvslam::optimize::g2o::se3::shot_vertex>::linearizeOplus();// 继承自动求导 // } bool read(istream &in) {} bool write(ostream &out) const {} protected: Eigen::Matrix4d _T_VO_to_GNSS_ENU; // 真值 }; } // namespace se3 } // namespace g2o } // namespace optimize } // namespace openvslam #endif
API_G2O_BA.cpp
#ifndef G2OBA_CPP #define G2OBA_CPP #include "API_G2O_BA.h" #endif
oepncv调用代码
local_bundle_adjuster.h
#ifndef OPENVSLAM_OPTIMIZE_LOCAL_BUNDLE_ADJUSTER_H #define OPENVSLAM_OPTIMIZE_LOCAL_BUNDLE_ADJUSTER_H namespace openvslam { namespace data { class keyframe; class map_database; } // namespace data namespace optimize { class local_bundle_adjuster { public: /** * Constructor * @param map_db * @param num_first_iter * @param num_second_iter */ explicit local_bundle_adjuster(const unsigned int num_first_iter = 5, const unsigned int num_second_iter = 10); /** * Destructor */ virtual ~local_bundle_adjuster() = default; /** * Perform optimization * @param curr_keyfrm * @param force_stop_flag */ //void optimize(data::keyframe* curr_keyfrm, bool* const force_stop_flag) const; void optimize(data::keyframe* curr_keyfrm, bool* const force_stop_flag,data::map_database* map_db_) const; void optimize_with_gnss(data::keyframe* curr_keyfrm, bool* const force_stop_flag, data::map_database* map_db_, Eigen::Matrix4d *T_VO_to_GNSS_ENU_) const; void optimize_all_gnss(bool* const force_stop_flag,data::map_database* map_db_ ) const ; private: //! number of iterations of first optimization const unsigned int num_first_iter_; //! number of iterations of second optimization const unsigned int num_second_iter_; }; } // namespace optimize } // namespace openvslam #endif // OPENVSLAM_OPTIMIZE_LOCAL_BUNDLE_ADJUSTER_H
local_bundle_adjuster.cc
#include "openvslam/data/keyframe.h" #include "openvslam/data/landmark.h" #include "openvslam/data/map_database.h" #include "openvslam/optimize/local_bundle_adjuster.h" #include "openvslam/optimize/g2o/landmark_vertex_container.h" #include "openvslam/optimize/g2o/se3/shot_vertex_container.h" #include "openvslam/optimize/g2o/se3/reproj_edge_wrapper.h" #include "openvslam/util/converter.h" #include <unordered_map> #include <Eigen/StdVector> #include <g2o/core/solver.h> #include <g2o/core/block_solver.h> #include <g2o/core/sparse_optimizer.h> #include <g2o/core/robust_kernel_impl.h> #include <g2o/types/sba/types_six_dof_expmap.h> #include <g2o/solvers/eigen/linear_solver_eigen.h> #include <g2o/solvers/dense/linear_solver_dense.h> #include <g2o/solvers/csparse/linear_solver_csparse.h> #include <g2o/core/optimization_algorithm_levenberg.h> // 自己添加的优化 #include "openvslam/gnss_src/API_3D_3D_sRt.h" #include "openvslam/gnss_src/API_G2O_BA.h" #include "openvslam/gnss_src/API_GNSS_ENU_TXT_YAML.h" namespace openvslam { namespace optimize { local_bundle_adjuster::local_bundle_adjuster(const unsigned int num_first_iter, const unsigned int num_second_iter) : num_first_iter_(num_first_iter), num_second_iter_(num_second_iter) {} /* 从关键帧集合提取县级位置和GNSS-enu坐标 all_dbkeyfrms 关集帧集合 */ void API_keyframe_to_vector3dList(std::vector< data::keyframe*> &all_dbkeyfrms , vector<Eigen::Vector3d> &source_points_vo,vector<Eigen::Vector3d> &target_points_gnss) { //std::vector< data::keyframe*> all_dbkeyfrms = map_db_->get_all_keyframes(); auto cam_info = static_cast<camera::perspective*>(all_dbkeyfrms[0]->camera_); std::sort(all_dbkeyfrms.begin(), all_dbkeyfrms.end(), [&](data::keyframe *keyfrm_1, data::keyframe *keyfrm_2) { return *keyfrm_1 < *keyfrm_2; }); for (int i=0;i< all_dbkeyfrms.size();i++) { //auto local_keyfrm = id_local_keyfrm_pair.second; data::keyframe* local_keyfrm=all_dbkeyfrms[i]; if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } Eigen::Matrix4d cam_pose_cw_i = local_keyfrm->get_cam_pose(); const Eigen::Matrix4d cam_pose_wc_i = cam_pose_cw_i.inverse(); Eigen::Vector3d cam_pose_i_t = cam_pose_wc_i.block<3, 1>(0, 3); gnss_data gnss_data_i = local_keyfrm->Gnss_data; bool can_use=local_keyfrm->Gnss_data.can_use; if(!can_use){// gnss是否可用可用 continue; } Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); //gnss_data_i_xyz[2]=0;// 固定高度 source_points_vo.push_back(cam_pose_i_t); target_points_gnss.push_back(gnss_data_i_xyz); } //cout<< "从关键帧加载数据 总关键帧 "<< all_dbkeyfrms.size()<< " 有效且有GNSS的关键帧 " << source_points_vo.size() <<endl; } // 获取一级共视觉关键帧集合 void Get_first_covisibilities_Keyframes(openvslam::data::keyframe* curr_keyfrm,std::unordered_map<unsigned int, data::keyframe*> &local_keyfrms){ //std::unordered_map<unsigned int, data::keyframe*> local_keyfrms; const auto curr_covisibilities = curr_keyfrm->graph_node_->get_covisibilities(); for (auto local_keyfrm : curr_covisibilities) { if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } local_keyfrms[local_keyfrm->id_] = local_keyfrm; } } // 获取二级共视觉关键帧集合 共视关键帧的共视关键帧 扩大优化范围 。 // 注意区分:不同于地图点的共视帧, 该集合的二级共视帧和当前帧未必有共视地图点 void Get_second_covisibilities_Keyframes(openvslam::data::keyframe* curr_keyfrm,std::unordered_map<unsigned int, data::keyframe*> &local_keyfrms){ //std::unordered_map<unsigned int, data::keyframe*> local_keyfrms; const auto curr_covisibilities = curr_keyfrm->graph_node_->get_covisibilities(); for (auto local_keyfrm : curr_covisibilities) { if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } local_keyfrms[local_keyfrm->id_] = local_keyfrm; const auto second_covisibilities = local_keyfrm->graph_node_->get_covisibilities(); for (auto second_local_keyfrm : second_covisibilities) { if (!second_local_keyfrm) { continue; } if (second_local_keyfrm->will_be_erased()) { continue; } if (local_keyfrms.count(second_local_keyfrm->id_))// 如果已经存在 { continue; } local_keyfrms[second_local_keyfrm->id_] = second_local_keyfrm; } } } void local_bundle_adjuster::optimize(openvslam::data::keyframe* curr_keyfrm, bool* const force_stop_flag,data::map_database* map_db_) const { // 1. local/fixed keyframes, local landmarksを集計する // correct local keyframes of the current keyframe std::unordered_map<unsigned int, data::keyframe*> local_keyfrms; local_keyfrms[curr_keyfrm->id_] = curr_keyfrm; const auto curr_covisibilities = curr_keyfrm->graph_node_->get_covisibilities(); for (auto local_keyfrm : curr_covisibilities) { if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } local_keyfrms[local_keyfrm->id_] = local_keyfrm; } // correct local landmarks seen in local keyframes std::unordered_map<unsigned int, data::landmark*> local_lms; for (auto local_keyfrm : local_keyfrms) { const auto landmarks = local_keyfrm.second->get_landmarks(); for (auto local_lm : landmarks) { if (!local_lm) { continue; } if (local_lm->will_be_erased()) { continue; } // 重複を避ける if (local_lms.count(local_lm->id_)) { continue; } local_lms[local_lm->id_] = local_lm; } } // fixed keyframes: keyframes which observe local landmarks but which are NOT in local keyframes std::unordered_map<unsigned int, data::keyframe*> fixed_keyfrms; for (auto local_lm : local_lms) { const auto observations = local_lm.second->get_observations(); for (auto& obs : observations) { auto fixed_keyfrm = obs.first; if (!fixed_keyfrm) { continue; } if (fixed_keyfrm->will_be_erased()) { continue; } // local keyframesに属しているときは追加しない if (local_keyfrms.count(fixed_keyfrm->id_)) { continue; } // 重複を避ける if (fixed_keyfrms.count(fixed_keyfrm->id_)) { continue; } fixed_keyfrms[fixed_keyfrm->id_] = fixed_keyfrm; } } // 2. optimizerを構築 auto linear_solver = ::g2o::make_unique<::g2o::LinearSolverCSparse<::g2o::BlockSolver_6_3::PoseMatrixType>>(); auto block_solver = ::g2o::make_unique<::g2o::BlockSolver_6_3>(std::move(linear_solver)); auto algorithm = new ::g2o::OptimizationAlgorithmLevenberg(std::move(block_solver)); ::g2o::SparseOptimizer optimizer; optimizer.setAlgorithm(algorithm); if (force_stop_flag) { optimizer.setForceStopFlag(force_stop_flag); } // 3. keyframeをg2oのvertexに変換してoptimizerにセットする // shot vertexのcontainer g2o::se3::shot_vertex_container keyfrm_vtx_container(0, local_keyfrms.size() + fixed_keyfrms.size()); // vertexに変換されたkeyframesを保存しておく std::unordered_map<unsigned int, data::keyframe*> all_keyfrms; // local keyframes 等待优化的帧 for (auto& id_local_keyfrm_pair : local_keyfrms) { auto local_keyfrm = id_local_keyfrm_pair.second; all_keyfrms.emplace(id_local_keyfrm_pair); auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(local_keyfrm, local_keyfrm->id_ == 0); optimizer.addVertex(keyfrm_vtx); } // fixed keyframes 二级共同视关键帧固定 for (auto& id_fixed_keyfrm_pair : fixed_keyfrms) { auto fixed_keyfrm = id_fixed_keyfrm_pair.second; all_keyfrms.emplace(id_fixed_keyfrm_pair); auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(fixed_keyfrm, true); optimizer.addVertex(keyfrm_vtx); } // 4. keyframeとlandmarkのvertexをreprojection edgeで接続する // landmark vertexのcontainer g2o::landmark_vertex_container lm_vtx_container(keyfrm_vtx_container.get_max_vertex_id() + 1, local_lms.size()); // reprojection edgeのcontainer using reproj_edge_wrapper = g2o::se3::reproj_edge_wrapper<data::keyframe>; std::vector<reproj_edge_wrapper> reproj_edge_wraps; reproj_edge_wraps.reserve(all_keyfrms.size() * local_lms.size()); // 有意水準5%のカイ2乗値 // 自由度n=2 constexpr float chi_sq_2D = 5.99146; const float sqrt_chi_sq_2D = std::sqrt(chi_sq_2D); // 自由度n=3 constexpr float chi_sq_3D = 7.81473; const float sqrt_chi_sq_3D = std::sqrt(chi_sq_3D); for (auto& id_local_lm_pair : local_lms) { auto local_lm = id_local_lm_pair.second; // landmarkをg2oのvertexに変換してoptimizerにセットする auto lm_vtx = lm_vtx_container.create_vertex(local_lm, false); optimizer.addVertex(lm_vtx); const auto observations = local_lm->get_observations(); for (const auto& obs : observations) { auto keyfrm = obs.first; auto idx = obs.second; if (!keyfrm) { continue; } if (keyfrm->will_be_erased()) { continue; } const auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(keyfrm); const auto& undist_keypt = keyfrm->undist_keypts_.at(idx); const float x_right = keyfrm->stereo_x_right_.at(idx); const float inv_sigma_sq = keyfrm->inv_level_sigma_sq_.at(undist_keypt.octave); const auto sqrt_chi_sq = (keyfrm->camera_->setup_type_ == camera::setup_type_t::Monocular) ? sqrt_chi_sq_2D : sqrt_chi_sq_3D; auto reproj_edge_wrap = reproj_edge_wrapper(keyfrm, keyfrm_vtx, local_lm, lm_vtx, idx, undist_keypt.pt.x, undist_keypt.pt.y, x_right, inv_sigma_sq, sqrt_chi_sq); reproj_edge_wraps.push_back(reproj_edge_wrap); optimizer.addEdge(reproj_edge_wrap.edge_); } } // 5. 1回目の最適化を実行 if (force_stop_flag) { if (*force_stop_flag) { return; } } optimizer.initializeOptimization(); optimizer.optimize(num_first_iter_); // 6. アウトライア除去をして2回目の最適化を実行 bool run_robust_BA = true; if (force_stop_flag) { if (*force_stop_flag) { run_robust_BA = false; } } if (run_robust_BA) { for (auto& reproj_edge_wrap : reproj_edge_wraps) { auto edge = reproj_edge_wrap.edge_; auto local_lm = reproj_edge_wrap.lm_; if (local_lm->will_be_erased()) { continue; } if (reproj_edge_wrap.is_monocular_) { if (chi_sq_2D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { reproj_edge_wrap.set_as_outlier(); } } else { if (chi_sq_3D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { reproj_edge_wrap.set_as_outlier(); } } edge->setRobustKernel(nullptr); } optimizer.initializeOptimization(); optimizer.optimize(num_second_iter_); } // 7. アウトライアを集計する std::vector<std::pair<data::keyframe*, data::landmark*>> outlier_observations; outlier_observations.reserve(reproj_edge_wraps.size()); for (auto& reproj_edge_wrap : reproj_edge_wraps) { auto edge = reproj_edge_wrap.edge_; auto local_lm = reproj_edge_wrap.lm_; if (local_lm->will_be_erased()) { continue; } if (reproj_edge_wrap.is_monocular_) { if (chi_sq_2D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { outlier_observations.emplace_back(std::make_pair(reproj_edge_wrap.shot_, reproj_edge_wrap.lm_)); } } else { if (chi_sq_3D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { outlier_observations.emplace_back(std::make_pair(reproj_edge_wrap.shot_, reproj_edge_wrap.lm_)); } } } // 8. 情報を更新 { std::lock_guard<std::mutex> lock(data::map_database::mtx_database_); if (!outlier_observations.empty()) { for (auto& outlier_obs : outlier_observations) { auto keyfrm = outlier_obs.first; auto lm = outlier_obs.second; keyfrm->erase_landmark(lm); lm->erase_observation(keyfrm); } } for (auto id_local_keyfrm_pair : local_keyfrms) { auto local_keyfrm = id_local_keyfrm_pair.second; auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(local_keyfrm); local_keyfrm->set_cam_pose(keyfrm_vtx->estimate()); } for (auto id_local_lm_pair : local_lms) { auto local_lm = id_local_lm_pair.second; auto lm_vtx = lm_vtx_container.get_vertex(local_lm); local_lm->set_pos_in_world(lm_vtx->estimate()); local_lm->update_normal_and_depth(); } } // 全体关键帧位姿参与sRt计算 ============================================================ std::vector< data::keyframe*> all_dbkeyfrms = map_db_->get_all_keyframes(); // std::sort(all_keyfrms.begin(), all_keyfrms.end(), [&](data::keyframe *keyfrm_1, data::keyframe *keyfrm_2) // { return *keyfrm_1 < *keyfrm_2; }); vector<Eigen::Vector3d> source_points_vo; vector<Eigen::Vector3d> target_points_gnss; Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); bool use_gnss_optimize=0; // 计算sRT 变换关系 if(all_dbkeyfrms.size()>4){ for (int i=0;i< all_dbkeyfrms.size();i++) { //auto local_keyfrm = id_local_keyfrm_pair.second; data::keyframe* local_keyfrm=all_dbkeyfrms[i]; if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } Eigen::Matrix4d cam_pose_cw_i = local_keyfrm->get_cam_pose(); const Eigen::Matrix4d cam_pose_wc_i = cam_pose_cw_i.inverse(); Eigen::Vector3d cam_pose_i_t = cam_pose_wc_i.block<3, 1>(0, 3); gnss_data gnss_data_i = local_keyfrm->Gnss_data; bool can_use=local_keyfrm->Gnss_data.can_use; if(!can_use){// 如果gnss可用 continue; } Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); //gnss_data_i_xyz[2]=0;// 固定高度 source_points_vo.push_back(cam_pose_i_t); target_points_gnss.push_back(gnss_data_i_xyz); //std::cout<< "cam_pose_i_t : " <<cam_pose_i_t[0] << " " << cam_pose_i_t[1] << " "<<cam_pose_i_t[2] << " "<<std::endl; //std::cout<< "gnss_data_i_xyz : " <<gnss_data_i_xyz[0]<< " " <<gnss_data_i_xyz[1]<< " " <<gnss_data_i_xyz[2]<< " " << std::endl; } if(source_points_vo.size()>4 && source_points_vo.size()== target_points_gnss.size() ){ //std::cout<< "SRT计算 总GNSS点数" << source_points_vo.size()<< " 总关键帧: "<<all_dbkeyfrms.size() <<std::endl; //Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); double s; Eigen::Matrix3d R; Eigen::Vector3d t; double error_th=7.841; API_ransac_ICP_3D_3D_sRt_inliner_sR(source_points_vo,target_points_gnss, 1000, //ransac随机抽取验证次数 7.841, // 误差阈值 3 s, R, t) ; T_VO_to_GNSS_ENU.block<3, 3>(0, 0) = s * R; T_VO_to_GNSS_ENU.block<3, 1>(0, 3) = t; //std::cout<< T_VO_to_GNSS_ENU << std::endl; vector<Eigen::Vector3d> source_points_vo_out; for( int i=0;i<source_points_vo.size();i++){ source_points_vo[i]= s*R*source_points_vo[i]+t; } double rmse=cal_rmse_t_v3s_v3s(target_points_gnss,source_points_vo); cout <<"当前帧 "<< curr_keyfrm->id_ <<" 纯局部VO优化, 整体关键帧srt计算 RMSE = " << rmse << " 参与计算总点数 " << source_points_vo.size() << " error_th " << error_th<< endl; } } } void local_bundle_adjuster::optimize_with_gnss(openvslam::data::keyframe* curr_keyfrm, bool* const force_stop_flag,data::map_database* map_db_, Eigen::Matrix4d *T_VO_to_GNSS_ENU_ ) const { // 1. local/fixed keyframes, local landmarksを集計する // 全体关键帧位姿参与sRt计算 开始 ============================================================ // 1-1获取全体数据点对 std::vector< data::keyframe*> all_dbkeyfrms = map_db_->get_all_keyframes(); auto cam_info = static_cast<camera::perspective*>(all_dbkeyfrms[0]->camera_); // cout <<local_keyfrm->id_ << " 相机内参 " // << "fx: "<< cam_info->fx_ // << "fy: "<< cam_info->fy_ // << "cx: "<< cam_info->cx_ // << "cy: "<< cam_info->cy_ // << endl; vector<Eigen::Vector3d> source_all_points_vo; vector<Eigen::Vector3d> target_all_points_gnss; //从关键帧抽取3D点 API_keyframe_to_vector3dList(all_dbkeyfrms ,source_all_points_vo ,target_all_points_gnss); //cout<< "从关键帧加载数据 总关键帧 "<< all_dbkeyfrms.size()<< " 有效且有GNSS的关键帧 " << source_all_points_vo.size() <<endl; // 1-2根据全体数据点对计算srt Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); double scale=1; Eigen::Matrix3d R = Eigen::Matrix3d::Identity(); Eigen::Vector3d t = Eigen::Vector3d::Identity();; double all_rmse = -1; bool use_gnss_optimize=0; // GNSS满足参与优化条件标志位 起码有4个点以上 if(source_all_points_vo.size()>4 && source_all_points_vo.size()== target_all_points_gnss.size() ){ API_ransac_ICP_3D_3D_sRt_inliner_sR(source_all_points_vo,target_all_points_gnss, 1000, //ransac随机抽取验证次数 7.815, // 误差阈值 3 7.815 scale, R, t) ; T_VO_to_GNSS_ENU.block<3, 3>(0, 0) = scale * R; T_VO_to_GNSS_ENU.block<3, 1>(0, 3) = t; *T_VO_to_GNSS_ENU_=T_VO_to_GNSS_ENU; //std::cout<< T_VO_to_GNSS_ENU << std::endl; use_gnss_optimize = 1; } else{ use_gnss_optimize = 0; } if(use_gnss_optimize==1){ // 1-3全体数据点计算变换后的误差 for( int i=0;i<source_all_points_vo.size();i++){ source_all_points_vo[i]= scale*R*source_all_points_vo[i]+t; } all_rmse=cal_rmse_t_v3s_v3s(target_all_points_gnss,source_all_points_vo); cout << " optimize_with_gnss 全局sRt更新前误差 RMSE = " << all_rmse << " 参与计算总点数 " << source_all_points_vo.size() << " 尺度 " << scale <<" 内参fx "<< cam_info->fx_<<endl; } // 全体关键帧位姿参与sRt计算 结束 ============================================================ // 1-1 要纠正的局部关键帧 //std::vector< data::keyframe*> all_dbkeyfrms = map_db_->get_all_keyframes(); std::unordered_map<unsigned int, data::keyframe*> local_keyfrms; local_keyfrms[curr_keyfrm->id_] = curr_keyfrm; // 获取一级共视关键帧 const auto curr_covisibilities = curr_keyfrm->graph_node_->get_covisibilities(); for (auto local_keyfrm : curr_covisibilities) { if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } local_keyfrms[local_keyfrm->id_] = local_keyfrm; } // if(level==1){ // Get_first_covisibilities_Keyframes(curr_keyfrm,local_keyfrms);// 获取一级共视关键帧 // } // else if(level==2){ // Get_second_covisibilities_Keyframes(curr_keyfrm,local_keyfrms);// 获取一级+二级共视关键帧 误差过大 扩大优化范围 // } // 1-2 要纠正的局部地图点 correct local landmarks seen in local keyframes std::unordered_map<unsigned int, data::landmark*> local_lms; for (auto local_keyfrm : local_keyfrms) { const auto landmarks = local_keyfrm.second->get_landmarks(); for (auto local_lm : landmarks) { if (!local_lm) { continue; } if (local_lm->will_be_erased()) { continue; } // 重複を避ける if (local_lms.count(local_lm->id_)) { continue; } local_lms[local_lm->id_] = local_lm; } } //1-3 固定帧 二级共视关键帧 必然看到了局部地图点,但不属于局部关键帧的,离当前帧比较远 fixed keyframes: keyframes which observe local landmarks but which are NOT in local keyframes std::unordered_map<unsigned int, data::keyframe*> fixed_keyfrms; for (auto local_lm : local_lms) { const auto observations = local_lm.second->get_observations(); for (auto& obs : observations) { auto fixed_keyfrm = obs.first; if (!fixed_keyfrm) { continue; } if (fixed_keyfrm->will_be_erased()) { continue; } // local keyframes 避免重复 if (local_keyfrms.count(fixed_keyfrm->id_)) { continue; } // 避免重复 if (fixed_keyfrms.count(fixed_keyfrm->id_)) { continue; } fixed_keyfrms[fixed_keyfrm->id_] = fixed_keyfrm; } } // 2. optimizer 构造优化器 auto linear_solver = ::g2o::make_unique<::g2o::LinearSolverCSparse<::g2o::BlockSolver_6_3::PoseMatrixType>>(); auto block_solver = ::g2o::make_unique<::g2o::BlockSolver_6_3>(std::move(linear_solver)); auto algorithm = new ::g2o::OptimizationAlgorithmLevenberg(std::move(block_solver)); ::g2o::SparseOptimizer optimizer; optimizer.setAlgorithm(algorithm); if (force_stop_flag) { optimizer.setForceStopFlag(force_stop_flag); } // 3. keyframeをg2oのvertexに変換してoptimizerにセットする // shot vertexのcontainer g2o::se3::shot_vertex_container keyfrm_vtx_container(0, local_keyfrms.size() + fixed_keyfrms.size()); // vertexに変換されたkeyframesを保存しておく std::unordered_map<unsigned int, data::keyframe*> all_keyfrms; // local keyframes 等待优化的帧 for (auto& id_local_keyfrm_pair : local_keyfrms) { auto local_keyfrm = id_local_keyfrm_pair.second; //==================== 添加VO位姿优化点================== all_keyfrms.emplace(id_local_keyfrm_pair); auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(local_keyfrm, local_keyfrm->id_ == 0); optimizer.addVertex(keyfrm_vtx); } // fixed keyframes 二级共同视关键帧固定 for (auto& id_fixed_keyfrm_pair : fixed_keyfrms) { auto fixed_keyfrm = id_fixed_keyfrm_pair.second; all_keyfrms.emplace(id_fixed_keyfrm_pair); //gnss_data gnss_data_i = fixed_keyfrm->Gnss_data; //==================== 添加VO位姿优化点================== /** * 当前帧的二级共视帧 当前帧的共视帧的共视帧 未必和当前帧有共同的地图点 * 由地图点获取的二级共视帧 共视地图点>40 一级共视 共视地图点<40 二级共视 */ //if(level==1){ auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(fixed_keyfrm, 1);// 不固定 参与全局优化 optimizer.addVertex(keyfrm_vtx); //} // else if(level==2){// 1级优化后误差还是很大,增加二级帧优化扩范围,且一级和二级全部优化,地图的二级共视帧不在固定也得参与优化 // auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(fixed_keyfrm, 0);// 不固定 参与全局优化 // optimizer.addVertex(keyfrm_vtx); // } } if(use_gnss_optimize){ // 加入GNSS 优化边 for (auto& id_local_keyfrm_pair : local_keyfrms) { auto local_keyfrm = id_local_keyfrm_pair.second; const auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(local_keyfrm); //==================== 添加GNSS优化点================== //use_gnss_optimize=1; // 起码GNSS点对有4个以上 gnss_data gnss_data_i = local_keyfrm->Gnss_data; if(!gnss_data_i.can_use) { continue; } Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); auto vi = keyfrm_vtx;// _id 位姿节点 // Edge openvslam::optimize::g2o::se3::GNSSConstraint_sRt_Edge *edge = new openvslam::optimize::g2o::se3::GNSSConstraint_sRt_Edge(T_VO_to_GNSS_ENU); //GNSS ENU真值 3D-2D是图像1的3d点 //edge->setId(i); edge->setVertex(0, vi); // 获取优化第一个节点位姿 没有第二个节点位姿 edge->setMeasurement(gnss_data_i_xyz); //GNSS ENU真值 3D-2D是图像2的像素点 初始化已经送入 // 信息矩阵设定 误差权重 // GNSS观测边误差/米 + 重投影误差边/像素个数 需要变换到同一个量纲 // GNSS米误差 e1=GNSS_ENU(X,Y,Z)- sRt * VO_ENU(X,Y,Z) // 其中 sRt是VO_ENU到GNSS_ENU的变换矩阵,由全体3D点匹配对ICP+ranck随机优化计算得到 // 粗略比例计算 GNSS在像素尺度像的误差e2= 尺度s * 焦距fx *GNSS误差e1 // 推导原理 https://www.cnblogs.com/gooutlook/p/18349180 // r总误差 = e2*信息矩阵(默认单位阵)*e2 // = e1*信息矩阵(默认单位阵)*e1 double S_gnss3D_to_vo2D = pow(scale*cam_info->fx_,2); edge->setInformation(Eigen::Matrix3d::Identity()*S_gnss3D_to_vo2D); // 核函数设定 防止利群点误差过大干扰整体结果 // 卡方分布中 3纬度变量 (x1-x1')^2+(y1-y1')^2+(z1-z1')^2 < 7.815 95% 内点 // 卡方分布中 2纬度变量 (x1-x1')^2+(y1-y1')^2 < 5.99 95% 内点 ::g2o::RobustKernelHuber* huber_kernel = new ::g2o::RobustKernelHuber; huber_kernel->setDelta( std::sqrt(7.815)); edge->setRobustKernel(huber_kernel); optimizer.addEdge(edge); //cout<< "测试 ========== GNSS优化加入 ========== 总边数 " << N <<endl; } }//if(use_gnss_optimize) // 4. keyframeとlandmarkのvertexをreprojection edgeで接続する // landmark vertexのcontainer g2o::landmark_vertex_container lm_vtx_container(keyfrm_vtx_container.get_max_vertex_id() + 1, local_lms.size()); // reprojection edgeのcontainer using reproj_edge_wrapper = g2o::se3::reproj_edge_wrapper<data::keyframe>; std::vector<reproj_edge_wrapper> reproj_edge_wraps; reproj_edge_wraps.reserve(all_keyfrms.size() * local_lms.size()); // 有意水準5%のカイ2乗値 // 自由度n=2 constexpr float chi_sq_2D = 5.99146; const float sqrt_chi_sq_2D = std::sqrt(chi_sq_2D); // 自由度n=3 constexpr float chi_sq_3D = 7.81473; const float sqrt_chi_sq_3D = std::sqrt(chi_sq_3D); for (auto& id_local_lm_pair : local_lms) { auto local_lm = id_local_lm_pair.second; // landmarkをg2oのvertexに変換してoptimizerにセットする auto lm_vtx = lm_vtx_container.create_vertex(local_lm, false); optimizer.addVertex(lm_vtx); const auto observations = local_lm->get_observations();// std::map<keyframe*, unsigned int> for (const auto& obs : observations) { auto keyfrm = obs.first; auto idx = obs.second; if (!keyfrm) { continue; } if (keyfrm->will_be_erased()) { continue; } const auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(keyfrm); const auto& undist_keypt = keyfrm->undist_keypts_.at(idx); const float x_right = keyfrm->stereo_x_right_.at(idx); const float inv_sigma_sq = keyfrm->inv_level_sigma_sq_.at(undist_keypt.octave); const auto sqrt_chi_sq = (keyfrm->camera_->setup_type_ == camera::setup_type_t::Monocular) ? sqrt_chi_sq_2D : sqrt_chi_sq_3D; auto reproj_edge_wrap = reproj_edge_wrapper(keyfrm, keyfrm_vtx, local_lm, lm_vtx, idx, undist_keypt.pt.x, undist_keypt.pt.y, x_right, inv_sigma_sq, sqrt_chi_sq); reproj_edge_wraps.push_back(reproj_edge_wrap); optimizer.addEdge(reproj_edge_wrap.edge_); } } // 5. 1回目の最適化を実行 if (force_stop_flag) { if (*force_stop_flag) { return; } } optimizer.initializeOptimization(); optimizer.optimize(num_first_iter_); // 6. アウトライア除去をして2回目の最適化を実行 bool run_robust_BA = true; if (force_stop_flag) { if (*force_stop_flag) { run_robust_BA = false; } } if (run_robust_BA) { for (auto& reproj_edge_wrap : reproj_edge_wraps) { auto edge = reproj_edge_wrap.edge_; auto local_lm = reproj_edge_wrap.lm_; if (local_lm->will_be_erased()) { continue; } if (reproj_edge_wrap.is_monocular_) { if (chi_sq_2D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { reproj_edge_wrap.set_as_outlier(); } } else { if (chi_sq_3D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { reproj_edge_wrap.set_as_outlier(); } } edge->setRobustKernel(nullptr); } optimizer.initializeOptimization(); optimizer.optimize(num_second_iter_); } // 7. アウトライアを集計する std::vector<std::pair<data::keyframe*, data::landmark*>> outlier_observations; outlier_observations.reserve(reproj_edge_wraps.size()); for (auto& reproj_edge_wrap : reproj_edge_wraps) { auto edge = reproj_edge_wrap.edge_; auto local_lm = reproj_edge_wrap.lm_; if (local_lm->will_be_erased()) { continue; } if (reproj_edge_wrap.is_monocular_) { if (chi_sq_2D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { outlier_observations.emplace_back(std::make_pair(reproj_edge_wrap.shot_, reproj_edge_wrap.lm_)); } } else { if (chi_sq_3D < edge->chi2() || !reproj_edge_wrap.depth_is_positive()) { outlier_observations.emplace_back(std::make_pair(reproj_edge_wrap.shot_, reproj_edge_wrap.lm_)); } } } // 8. 情報を更新 { std::lock_guard<std::mutex> lock(data::map_database::mtx_database_); if (!outlier_observations.empty()) { for (auto& outlier_obs : outlier_observations) { auto keyfrm = outlier_obs.first; auto lm = outlier_obs.second; keyfrm->erase_landmark(lm); lm->erase_observation(keyfrm); } } for (auto id_local_keyfrm_pair : local_keyfrms) { auto local_keyfrm = id_local_keyfrm_pair.second; auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(local_keyfrm); local_keyfrm->set_cam_pose(keyfrm_vtx->estimate()); } for (auto id_local_lm_pair : local_lms) { auto local_lm = id_local_lm_pair.second; auto lm_vtx = lm_vtx_container.get_vertex(local_lm); local_lm->set_pos_in_world(lm_vtx->estimate()); local_lm->update_normal_and_depth(); } } // 全体关键帧位姿参与sRt计算 开始 ============================================================ //从关键帧抽取优化后的3D点 source_all_points_vo.clear(); target_all_points_gnss.clear(); //从关键帧抽取3D点 API_keyframe_to_vector3dList(all_dbkeyfrms ,source_all_points_vo ,target_all_points_gnss); if(source_all_points_vo.size()>4 && source_all_points_vo.size()== target_all_points_gnss.size() ){ API_ransac_ICP_3D_3D_sRt_inliner_sR(source_all_points_vo,target_all_points_gnss, 1000, //ransac随机抽取验证次数 7.815, // 误差阈值 3 7.815 scale, R, t) ; T_VO_to_GNSS_ENU.block<3, 3>(0, 0) = scale * R; T_VO_to_GNSS_ENU.block<3, 1>(0, 3) = t; *T_VO_to_GNSS_ENU_=T_VO_to_GNSS_ENU; //std::cout<<"local_bundle_adjuster.cc === *T_VO_to_GNSS_ENU_ 优化后" <<*T_VO_to_GNSS_ENU_ << std::endl; use_gnss_optimize = 1; } else{ use_gnss_optimize = 0; } if(use_gnss_optimize==1){ // 1-3全体数据点计算变换后的误差 for( int i=0;i<source_all_points_vo.size();i++){ source_all_points_vo[i]= scale*R*source_all_points_vo[i]+t; } all_rmse=cal_rmse_t_v3s_v3s(target_all_points_gnss,source_all_points_vo); cout << "optimize_with_gnss 全局sRt更新后误差 RMSE = " << all_rmse << " 参与计算总点数 " << source_all_points_vo.size() << " 尺度 " << scale <<" 内参fx "<< cam_info->fx_<<endl; } // 全体关键帧位姿参与sRt计算 结束 ============================================================ double error_all_th=2; if(all_rmse>error_all_th){ optimize_all_gnss(force_stop_flag,map_db_); cout << all_rmse << " 误差过大 开启全局优化 ************************* " << endl; //optimize_with_gnss(curr_keyfrm, force_stop_flag, map_db_,2); // 丢帧且不插入关集帧 //cout << all_rmse << " 误差过大 开启一级+二级联合优化 ************************* " << endl; } } void local_bundle_adjuster::optimize_all_gnss(bool* const force_stop_flag,data::map_database* map_db_ ) const { // 1. local/fixed keyframes, local landmarksを集計する // 1. データを集める const auto keyfrms = map_db_->get_all_keyframes(); const auto lms = map_db_->get_all_landmarks(); std::vector<bool> is_optimized_lm(lms.size(), true); // 全体关键帧位姿参与sRt计算 开始 ============================================================ std::vector< data::keyframe*> all_dbkeyfrms = map_db_->get_all_keyframes(); auto cam_info = static_cast<camera::perspective*>(all_dbkeyfrms[0]->camera_); // cout <<local_keyfrm->id_ << " 相机内参 " // << "fx: "<< cam_info->fx_ // << "fy: "<< cam_info->fy_ // << "cx: "<< cam_info->cx_ // << "cy: "<< cam_info->cy_ // << endl; std::sort(all_dbkeyfrms.begin(), all_dbkeyfrms.end(), [&](data::keyframe *keyfrm_1, data::keyframe *keyfrm_2) { return *keyfrm_1 < *keyfrm_2; }); vector<Eigen::Vector3d> source_points_vo; vector<Eigen::Vector3d> target_points_gnss; Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); double s; Eigen::Matrix3d R; Eigen::Vector3d t; bool use_gnss_optimize=0; // GNSS满足参与优化条件标志位 起码有4个点以上 // 计算sRT 变换关系 if(all_dbkeyfrms.size()>4){ for (int i=0;i< all_dbkeyfrms.size();i++) { //auto local_keyfrm = id_local_keyfrm_pair.second; data::keyframe* local_keyfrm=all_dbkeyfrms[i]; if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } Eigen::Matrix4d cam_pose_cw_i = local_keyfrm->get_cam_pose(); const Eigen::Matrix4d cam_pose_wc_i = cam_pose_cw_i.inverse(); Eigen::Vector3d cam_pose_i_t = cam_pose_wc_i.block<3, 1>(0, 3); gnss_data gnss_data_i = local_keyfrm->Gnss_data; bool can_use=local_keyfrm->Gnss_data.can_use; if(!can_use){// gnss是否可用可用 continue; } Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); //gnss_data_i_xyz[2]=0;// 固定高度 source_points_vo.push_back(cam_pose_i_t); target_points_gnss.push_back(gnss_data_i_xyz); //std::cout<< "cam_pose_i_t : " <<cam_pose_i_t[0] << " " << cam_pose_i_t[1] << " "<<cam_pose_i_t[2] << " "<<std::endl; //std::cout<< "gnss_data_i_xyz : " <<gnss_data_i_xyz[0]<< " " <<gnss_data_i_xyz[1]<< " " <<gnss_data_i_xyz[2]<< " " << std::endl; } if(source_points_vo.size()>4 && source_points_vo.size()== target_points_gnss.size() ){ //std::cout<< "SRT计算 总GNSS点数" << source_points_vo.size()<< " 总关键帧: "<<all_dbkeyfrms.size() <<std::endl; //Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); // double s; // Eigen::Matrix3d R; // Eigen::Vector3d t; API_ransac_ICP_3D_3D_sRt_inliner_sR(source_points_vo,target_points_gnss, 1000, //ransac随机抽取验证次数 7.815 , // 误差阈值 3 7.815 s, R, t) ; T_VO_to_GNSS_ENU.block<3, 3>(0, 0) = s * R; T_VO_to_GNSS_ENU.block<3, 1>(0, 3) = t; //std::cout<< T_VO_to_GNSS_ENU << std::endl; for( int i=0;i<source_points_vo.size();i++){ source_points_vo[i]= s*R*source_points_vo[i]+t; } double rmse=cal_rmse_t_v3s_v3s(target_points_gnss,source_points_vo); cout << " 全局整体srt优化前 RMSE = " << rmse << " 参与计算总点数 " << source_points_vo.size() << " 尺度 " << s << endl; use_gnss_optimize=1; } } // 全体关键帧位姿参与sRt计算 结束 ============================================================ // 2. optimizerを構築 auto linear_solver = ::g2o::make_unique<::g2o::LinearSolverCSparse<::g2o::BlockSolver_6_3::PoseMatrixType>>(); auto block_solver = ::g2o::make_unique<::g2o::BlockSolver_6_3>(std::move(linear_solver)); auto algorithm = new ::g2o::OptimizationAlgorithmLevenberg(std::move(block_solver)); ::g2o::SparseOptimizer optimizer; optimizer.setAlgorithm(algorithm); if (force_stop_flag) { optimizer.setForceStopFlag(force_stop_flag); } // 3. keyframeをg2oのvertexに変換してoptimizerにセットする // shot vertexのcontainer g2o::se3::shot_vertex_container keyfrm_vtx_container(0, keyfrms.size()); // keyframesをoptimizerにセット for (const auto keyfrm : keyfrms) { if (!keyfrm) { continue; } if (keyfrm->will_be_erased()) { continue; } // 添加图像位姿点 auto keyfrm_vtx = keyfrm_vtx_container.create_vertex(keyfrm, keyfrm->id_ == 0); optimizer.addVertex(keyfrm_vtx); // 添加GNSS优化点 //use_gnss_optimize=1; if(use_gnss_optimize){ //keyfrm->Gnss_data.z=0;// 固定高度 gnss_data gnss_data_i = keyfrm->Gnss_data; Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); if(!gnss_data_i.can_use) { continue; } //openvslam::optimize::g2o::se3::shot_vertex* vi = static_cast<openvslam::optimize::g2o::se3::shot_vertex*>(optimizer.vertex(keyfrm->id_)); //Eigen::Vector3d Gnss_enu_i = gnss_lists[i]; //Gnss_enu_i[2]=0;// 固定高度 auto vi = keyfrm_vtx;// _id 位姿节点 // Edge openvslam::optimize::g2o::se3::GNSSConstraint_sRt_Edge *edge = new openvslam::optimize::g2o::se3::GNSSConstraint_sRt_Edge(T_VO_to_GNSS_ENU); //GNSS ENU真值 3D-2D是图像1的3d点 //edge->setId(i); edge->setVertex(0, vi); // 获取优化第一个节点位姿 没有第二个节点位姿 edge->setMeasurement(gnss_data_i_xyz); //GNSS ENU真值 3D-2D是图像2的像素点 初始化已经送入 // 信息矩阵设定 误差权重 // GNSS观测边误差/米 + 重投影误差边/像素个数 需要变换到同一个量纲 // GNSS米误差 e1=GNSS_ENU(X,Y,Z)- sRt * VO_ENU(X,Y,Z) // 其中 sRt是VO_ENU到GNSS_ENU的变换矩阵,由全体3D点匹配对ICP+ranck随机优化计算得到 // 粗略比例计算 GNSS在像素尺度像的误差e2= 尺度s * 焦距fx *GNSS误差e1 // 推导原理 https://www.cnblogs.com/gooutlook/p/18349180 // r总误差 = e2*信息矩阵(默认单位阵)*e2 // = e1*信息矩阵(默认单位阵)*e1 double S_gnss3D_to_vo2D = pow(s*cam_info->fx_,2); edge->setInformation(Eigen::Matrix3d::Identity()*S_gnss3D_to_vo2D); // 核函数设定 防止利群点误差过大干扰整体结果 // 卡方分布中 3纬度变量 (x1-x1')^2+(y1-y1')^2+(z1-z1')^2 < 7.815 95% 内点 // 卡方分布中 2纬度变量 (x1-x1')^2+(y1-y1')^2 < 5.99 95% 内点 ::g2o::RobustKernelHuber* rk = new ::g2o::RobustKernelHuber; rk->setDelta( std::sqrt(7.815)); edge->setRobustKernel(rk); optimizer.addEdge(edge); //cout<< "测试 ========== GNSS优化加入 ========== 总边数 " << N <<endl; } } // ==============================GNSSy优化 结束================================================= // 4. keyframeとlandmarkのvertexをreprojection edgeで接続する // landmark vertexのcontainer g2o::landmark_vertex_container lm_vtx_container(keyfrm_vtx_container.get_max_vertex_id() + 1, lms.size()); // reprojection edgeのcontainer using reproj_edge_wrapper = g2o::se3::reproj_edge_wrapper<data::keyframe>; std::vector<reproj_edge_wrapper> reproj_edge_wraps; reproj_edge_wraps.reserve(10 * lms.size()); // 有意水準5%のカイ2乗値 // 自由度n=2 constexpr float chi_sq_2D = 5.99146; const float sqrt_chi_sq_2D = std::sqrt(chi_sq_2D); // 自由度n=3 constexpr float chi_sq_3D = 7.81473; const float sqrt_chi_sq_3D = std::sqrt(chi_sq_3D); //std::sqrt(7.81473) bool use_huber_kernel_=1; for (unsigned int i = 0; i < lms.size(); ++i) { auto lm = lms.at(i); if (!lm) { continue; } if (lm->will_be_erased()) { continue; } // landmarkをg2oのvertexに変換してoptimizerにセットする auto lm_vtx = lm_vtx_container.create_vertex(lm, false); optimizer.addVertex(lm_vtx); unsigned int num_edges = 0; const auto observations = lm->get_observations(); for (const auto& obs : observations) { auto keyfrm = obs.first; auto idx = obs.second; if (!keyfrm) { continue; } if (keyfrm->will_be_erased()) { continue; } if (!keyfrm_vtx_container.contain(keyfrm)) { continue; } const auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(keyfrm); const auto& undist_keypt = keyfrm->undist_keypts_.at(idx); const float x_right = keyfrm->stereo_x_right_.at(idx); const float inv_sigma_sq = keyfrm->inv_level_sigma_sq_.at(undist_keypt.octave); const auto sqrt_chi_sq = (keyfrm->camera_->setup_type_ == camera::setup_type_t::Monocular) ? sqrt_chi_sq_2D : sqrt_chi_sq_3D; auto reproj_edge_wrap = reproj_edge_wrapper(keyfrm, keyfrm_vtx, lm, lm_vtx, idx, undist_keypt.pt.x, undist_keypt.pt.y, x_right, inv_sigma_sq, sqrt_chi_sq, use_huber_kernel_); reproj_edge_wraps.push_back(reproj_edge_wrap); optimizer.addEdge(reproj_edge_wrap.edge_); ++num_edges; } if (num_edges == 0) { optimizer.removeVertex(lm_vtx); is_optimized_lm.at(i) = false; } } // 5. 最適化を実行 int num_iter_=10;// 优化次数 10 optimizer.initializeOptimization(); optimizer.optimize(num_iter_); if (force_stop_flag && *force_stop_flag) { return; } // 6. 結果を取り出す for (auto keyfrm : keyfrms) { if (keyfrm->will_be_erased()) { continue; } auto keyfrm_vtx = keyfrm_vtx_container.get_vertex(keyfrm); const auto cam_pose_cw = util::converter::to_eigen_mat(keyfrm_vtx->estimate()); keyfrm->set_cam_pose(cam_pose_cw); } for (unsigned int i = 0; i < lms.size(); ++i) { if (!is_optimized_lm.at(i)) { continue; } auto lm = lms.at(i); if (!lm) { continue; } if (lm->will_be_erased()) { continue; } auto lm_vtx = lm_vtx_container.get_vertex(lm); const Vec3_t pos_w = lm_vtx->estimate(); lm->set_pos_in_world(pos_w); lm->update_normal_and_depth(); } // 6 更细srt 计算优化后的误差 if(all_dbkeyfrms.size()>4){ source_points_vo.clear(); target_points_gnss.clear(); for (int i=0;i< all_dbkeyfrms.size();i++) { //auto local_keyfrm = id_local_keyfrm_pair.second; data::keyframe* local_keyfrm=all_dbkeyfrms[i]; if (!local_keyfrm) { continue; } if (local_keyfrm->will_be_erased()) { continue; } Eigen::Matrix4d cam_pose_cw_i = local_keyfrm->get_cam_pose(); const Eigen::Matrix4d cam_pose_wc_i = cam_pose_cw_i.inverse(); Eigen::Vector3d cam_pose_i_t = cam_pose_wc_i.block<3, 1>(0, 3); gnss_data gnss_data_i = local_keyfrm->Gnss_data; bool can_use=local_keyfrm->Gnss_data.can_use; if(!can_use){// 如果gnss可用 continue; } Eigen::Vector3d gnss_data_i_xyz(gnss_data_i.x,gnss_data_i.y,gnss_data_i.z); //gnss_data_i_xyz[2]=0;// 固定高度 source_points_vo.push_back(cam_pose_i_t); target_points_gnss.push_back(gnss_data_i_xyz); //std::cout<< "cam_pose_i_t : " <<cam_pose_i_t[0] << " " << cam_pose_i_t[1] << " "<<cam_pose_i_t[2] << " "<<std::endl; //std::cout<< "gnss_data_i_xyz : " <<gnss_data_i_xyz[0]<< " " <<gnss_data_i_xyz[1]<< " " <<gnss_data_i_xyz[2]<< " " << std::endl; } if(source_points_vo.size()>4 && source_points_vo.size()== target_points_gnss.size() ){ //std::cout<< "SRT计算 总GNSS点数" << source_points_vo.size()<< " 总关键帧: "<<all_dbkeyfrms.size() <<std::endl; //Eigen::Matrix4d T_VO_to_GNSS_ENU = Eigen::Matrix4d::Identity(); double s; Eigen::Matrix3d R; Eigen::Vector3d t; API_ransac_ICP_3D_3D_sRt_inliner_sR(source_points_vo,target_points_gnss, 1000, //ransac随机抽取验证次数 7.81 , // 误差阈值 3 3 纬度 卡方 95%内点阈值 7.81 s, R, t) ; T_VO_to_GNSS_ENU.block<3, 3>(0, 0) = s * R; T_VO_to_GNSS_ENU.block<3, 1>(0, 3) = t; //std::cout<< T_VO_to_GNSS_ENU << std::endl; for( int i=0;i<source_points_vo.size();i++){ source_points_vo[i]= s*R*source_points_vo[i]+t; } double rmse=cal_rmse_t_v3s_v3s(target_points_gnss,source_points_vo); cout << " 全局整体srt优化后 RMSE = " << rmse << " 参与计算总点数 " << source_points_vo.size() << " 尺度 " << s <<" 内参fx "<< cam_info->fx_ <<endl; } }// 6 更细srt 计算优化后的误差 } } // namespace optimize } // namespace openvslam
API_GNSS_ENU_TXT_YAML.h
#ifndef API_GNSS_ENU_YAML_h #define API_GNSS_ENU_YAML_h #include <iostream> #include <fstream> #include <sstream> #include <vector> #include <string> #include<iomanip> #include <yaml-cpp/yaml.h> #include <GeographicLib/LocalCartesian.hpp> using namespace std; class gnss_data { public: bool can_use; // 原始数据 string time_s;//时间戳名字用于找图像 名字 double time; //时间戳 用于计算时间差 double lat; double lon; double high; // ENU坐标GNSS的原点 double lat0; double lon0; double high0; // 在ENU坐标下 double x; double y; double z; double ecef_x; double ecef_y; double ecef_z; public: // 初始化 赋予原始数据 gnss_data(double time_,double lat_,double lon_,double high_):time(time_), lat(lat_),lon(lon_),high(high_) {} gnss_data() { can_use=0; // 无效GNSS数据 不能用 time_s="0"; time=0; lat=0; lon=0; high=0; lat0=0; lon0=0; high0=0; x=0; y=0; z=0; ecef_x=0; ecef_y=0; ecef_z=0; } ~gnss_data() {} // 设置原始GNSS数据 void Set_Gnss(double time_,double lat_,double lon_,double high_){ time=time_; lat=lat_; lon=lon_; high=high_; }; // 设置ENU参考点GNSS void Set_orinGnss(double lat0_,double lon0_,double high0_){ lat0=lat0_; lon0=lon0_; high0=high0_; } // 设置ENU坐标数据 void Set_ENU(double x_,double y_,double z_){ x=x_; y=y_; z=z_; } // 设置ECEF坐标数据 void Set_ECEF(double x_,double y_,double z_){ ecef_x=x_; ecef_y=y_; ecef_z=z_; } }; class GNSS_TextFileReader { public: std::string filename; char delimiter; std::vector<std::vector<std::string>> data; // 二维向量,存储每一行拆分后的数据 std::vector<gnss_data> gnss_List; public: GNSS_TextFileReader(const std::string &filename, char delimiter) : filename(filename), delimiter(delimiter) {} bool readFile() { std::ifstream file(filename); if (!file.is_open()) { std::cerr << "Error opening file " << filename << std::endl; return false; } std::string line; while (std::getline(file, line)) { std::stringstream ss(line); std::string token; std::vector<std::string> row; while (std::getline(ss, token, delimiter)) { // delimiter 分割 row.push_back(token); } data.push_back(row);// 保存string数据double gnss_data gnss_data_i( stod(row[0]),stod(row[1]),stod(row[2]),stod(row[3])); //保存double数据 gnss_data_i.time_s=row[0];// 字符串型号时间戳 gnss_List.push_back(gnss_data_i); } file.close(); return true; } void printData() { // for (const auto &row : data) { // for (const auto &col : row) { // std::cout << col << " "; // } // std::cout << std::endl; // } //for (const auto &gnss_List_i : gnss_List) { for(int i=0;i<gnss_List.size();i++){ gnss_data gnss_List_i=gnss_List[i]; cout<< "编号 " << i << " 时间戳 "<< gnss_List_i.time<< " 纬度 " << gnss_List_i.lat << " 经度 " << gnss_List_i.lon<< " 高度 "<< gnss_List_i.high << fixed << setprecision(10)<< endl; } } const std::vector<std::vector<std::string>>& getData() const { return data; } const std::vector<gnss_data>& get_gnss_List() const { return gnss_List; } }; bool Get_GNSS_INTI_YAML( std::string read_path,gnss_data &gnss_data_int0); std::string Get_YAML(std::string read_path,char *name) ; // 函数用于按照指定分隔符分割字符串 std::vector<std::string> splitString(const std::string &s, char delim) ; // int main() { // //string path_="/home/r9000k/v2_project/data/NWPU/"; // string path_="../config/"; // string path_config=path_+"FHY_config.yaml";// 初始点 // string path_GNSS=path_+"FHY_gps.txt";// 原始数据 // // 1 获取参考点 // gnss_data gnss_data_int0(-1,-1,-1,-1); // Get_GNSS_INTI_YAML(path_config,gnss_data_int0); // std::cout << fixed << setprecision(10)<< endl; // std::cout << "原点 纬度: " << gnss_data_int0.lat << endl; // std::cout << "原点 经度: " << gnss_data_int0.lon << endl; // std::cout << "原点 高度: " << gnss_data_int0.high << endl; // // 2 当前点的经纬度和高度,作为局部坐标系的原点 // double origin_latitude = gnss_data_int0.lat; // 纬度 // double origin_longitude = gnss_data_int0.lon; // 经度 // double origin_height = gnss_data_int0.high; // 高度 // // 转化为enu,并设置原点 // GeographicLib::LocalCartesian geoConverter; // geoConverter.Reset(origin_latitude, origin_longitude, origin_height); // // 转化为ecef,使用WGS84椭球模型 // GeographicLib::Geocentric wgs84(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f());// 6378137 298.257223563LL // //GeographicLib::Geocentric cgcs2000(6378137.0, 1.0 / 298.257222101); // //3 获取数据点 // GNSS_TextFileReader reader(path_GNSS, ' '); // 读取路径 分隔符号 // std::vector<gnss_data> gnss_Lists ; // if (reader.readFile()) { // //reader.printData(); // gnss_Lists = reader.get_gnss_List(); // for(int i=0;i<gnss_Lists.size();i++){ // gnss_Lists[i].Set_orinGnss(gnss_data_int0.lat,gnss_data_int0.lon,gnss_data_int0.high);// 设置原始GNSS点 // double target_latitude = gnss_Lists[i].lat; // double target_longitude = gnss_Lists[i].lon; // double target_height = gnss_Lists[i].high; // // gnss转化为enu // double x, y, z; // geoConverter.Forward(target_latitude, target_longitude, target_height, x, y, z); // gnss_Lists[i].x=x; // gnss_Lists[i].y=y; // gnss_Lists[i].z=z; // // WGS84 gnss转化为ecef // wgs84.Forward(target_latitude, target_longitude, target_height, x, y, z); // gnss_Lists[i].ecef_x=x; // gnss_Lists[i].ecef_y=y; // gnss_Lists[i].ecef_z=z; // gnss_data gnss_List_i=gnss_Lists[i]; // cout << fixed << setprecision(10)<< endl; // cout<< "编号 " << i // << " 时间戳 "<< gnss_List_i.time // << " \n纬度 " << gnss_List_i.lat // << " 经度 " << gnss_List_i.lon // << " 高度 "<< gnss_List_i.high // << " \nenu-x " << gnss_List_i.x // << " enu-y " << gnss_List_i.y // << " enu-z "<< gnss_List_i.z // << " \necef_x " << gnss_List_i.ecef_x // << " ecef_y " << gnss_List_i.ecef_y // << " ecef_z "<< gnss_List_i.ecef_z // << endl; // } // } // return 0; // } #endif
API_GNSS_ENU_TXT_YAML.cc
#ifndef API_GNSS_ENU_YAML_cpp #define API_GNSS_ENU_YAML_cpp #include "API_GNSS_ENU_TXT_YAML.h" bool Get_GNSS_INTI_YAML( std::string read_path,gnss_data &gnss_data_int0) { try { // 读取YAML文件 YAML::Node config = YAML::LoadFile(read_path); //std::cout << "读取gnss 初始点位置" << std::endl; // 访问YAML中的数据 std::string lat0 = config["Initial.lat"].as<std::string>(); std::string lon0 = config["Initial.lon"].as<std::string>(); std::string alt0 = config["Initial.alt"].as<std::string>(); double time0=0.0; // // 打印读取的数据 //std::cout << fixed << setprecision(10)<< endl; // std::cout << "原点 纬度: " << lat0 << std::endl; // std::cout << "原点 经度: " << lon0 << std::endl; // std::cout << "原点 高度: " << alt0 << std::endl; gnss_data_int0.Set_Gnss(time0,stod(lat0),stod(lon0),stod(alt0)); std::cout << "GNSS原始点加载完毕" << std::endl; } catch (const YAML::Exception& e) { std::cerr << "YAML Exception: " << e.what() << std::endl; return 0; } return 1; } std::string Get_YAML(std::string read_path,char *name) { try { // 读取YAML文件 YAML::Node config = YAML::LoadFile(read_path); // 访问YAML中的数据 std::string name_value = config[name].as<std::string>(); std::cout << "yaml 读取数据: "<<name <<" " << name_value << std::endl; return name_value; } catch (const YAML::Exception& e) { std::cerr << "YAML Exception: " << e.what() << std::endl; return "error"; } } // 函数用于按照指定分隔符分割字符串 std::vector<std::string> splitString(const std::string &s, char delim) { std::vector<std::string> tokens; std::stringstream ss(s); std::string token; while (std::getline(ss, token, delim)) { tokens.push_back(token); } return tokens; } // int main() { // //string path_="/home/r9000k/v2_project/data/NWPU/"; // string path_="../config/"; // string path_config=path_+"FHY_config.yaml";// 初始点 // string path_GNSS=path_+"FHY_gps.txt";// 原始数据 // // 1 获取参考点 // gnss_data gnss_data_int0(-1,-1,-1,-1); // Get_GNSS_INTI_YAML(path_config,gnss_data_int0); // std::cout << fixed << setprecision(10)<< endl; // std::cout << "原点 纬度: " << gnss_data_int0.lat << endl; // std::cout << "原点 经度: " << gnss_data_int0.lon << endl; // std::cout << "原点 高度: " << gnss_data_int0.high << endl; // // 2 当前点的经纬度和高度,作为局部坐标系的原点 // double origin_latitude = gnss_data_int0.lat; // 纬度 // double origin_longitude = gnss_data_int0.lon; // 经度 // double origin_height = gnss_data_int0.high; // 高度 // // 转化为enu,并设置原点 // GeographicLib::LocalCartesian geoConverter; // geoConverter.Reset(origin_latitude, origin_longitude, origin_height); // // 转化为ecef,使用WGS84椭球模型 // GeographicLib::Geocentric wgs84(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f());// 6378137 298.257223563LL // //GeographicLib::Geocentric cgcs2000(6378137.0, 1.0 / 298.257222101); // //3 获取数据点 // GNSS_TextFileReader reader(path_GNSS, ' '); // 读取路径 分隔符号 // std::vector<gnss_data> gnss_Lists ; // if (reader.readFile()) { // //reader.printData(); // gnss_Lists = reader.get_gnss_List(); // for(int i=0;i<gnss_Lists.size();i++){ // gnss_Lists[i].Set_orinGnss(gnss_data_int0.lat,gnss_data_int0.lon,gnss_data_int0.high);// 设置原始GNSS点 // double target_latitude = gnss_Lists[i].lat; // double target_longitude = gnss_Lists[i].lon; // double target_height = gnss_Lists[i].high; // // gnss转化为enu // double x, y, z; // geoConverter.Forward(target_latitude, target_longitude, target_height, x, y, z); // gnss_Lists[i].x=x; // gnss_Lists[i].y=y; // gnss_Lists[i].z=z; // // WGS84 gnss转化为ecef // wgs84.Forward(target_latitude, target_longitude, target_height, x, y, z); // gnss_Lists[i].ecef_x=x; // gnss_Lists[i].ecef_y=y; // gnss_Lists[i].ecef_z=z; // gnss_data gnss_List_i=gnss_Lists[i]; // cout << fixed << setprecision(10)<< endl; // cout<< "编号 " << i // << " 时间戳 "<< gnss_List_i.time // << " \n纬度 " << gnss_List_i.lat // << " 经度 " << gnss_List_i.lon // << " 高度 "<< gnss_List_i.high // << " \nenu-x " << gnss_List_i.x // << " enu-y " << gnss_List_i.y // << " enu-z "<< gnss_List_i.z // << " \necef_x " << gnss_List_i.ecef_x // << " ecef_y " << gnss_List_i.ecef_y // << " ecef_z "<< gnss_List_i.ecef_z // << endl; // } // } // return 0; // } #endif