[关闭]
@gnat-xj 2018-04-08T17:44:31.000000Z 字数 37493 阅读 3155

SLAMBOOK


https://gitlab.com/juantu/slambook

Ch2

hello slam

Ch3

useEigen

  1. #include <Eigen/Core>
  2. // 稠密矩阵的代数运算(逆,特征值等)
  3. #include <Eigen/Dense>
  4. // 声明一个2*3的float矩阵
  5. Eigen::Matrix<double, 3, 5> matrix_35;
  6. matrix_35 << 1, 2, 3, 4, 5,
  7. 6, 7, 8, 9, 10,
  8. 86, 7, 8, 9, 10;
  9. cout << matrix_35 << endl;
  10. Eigen::Vector3d v_3d;
  11. Eigen::Matrix<float,3,1> vd_3d; // 这是一样的
  12. Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > matrix_dynamic;
  13. Eigen::MatrixXd matrix_x; // 更简单的
  14. // 访问
  15. matrix_23(i,j);
  16. // cast 类型
  17. Eigen::Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d;
  18. matrix_33 = Eigen::Matrix3d::Random(); // 随机数矩阵
  19. cout << matrix_33.transpose() << endl; // 转置
  20. cout << matrix_33.sum() << endl; // 各元素和
  21. cout << matrix_33.trace() << endl; // 迹
  22. cout << 10*matrix_33 << endl; // 数乘
  23. cout << matrix_33.inverse() << endl; // 逆
  24. cout << matrix_33.determinant() << endl; // 行列式
  25. Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver ( matrix_33.transpose()*matrix_33 );
  26. cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl;
  27. cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl;
  28. Eigen::Matrix<double,MATRIX_SIZE,1> x2 = matrix_NN.colPivHouseholderQr().solve(v_Nd);
  29. cout <<"sum of x-x2: " << (x - x2).sum() << endl;

useGeometry

  1. #include <Eigen/Core>
  2. #include <Eigen/Geometry>
  3. Eigen::Matrix3d rotation_matrix = Eigen::Matrix3d::Identity();
  4. Eigen::AngleAxisd rotation_vector ( M_PI/4, Eigen::Vector3d ( 0,0,1 ) ); //沿 Z 轴旋转 45 度
  5. rotation_matrix = rotation_vector.toRotationMatrix();
  6. Eigen::Vector3d euler_angles = rotation_matrix.eulerAngles ( 2,1,0 ); // ZYX顺序,即roll pitch yaw顺序
  7. Eigen::Isometry3d T=Eigen::Isometry3d::Identity(); // 虽然称为3d,实质上是4*4的矩阵
  8. T.rotate ( rotation_vector ); // 按照rotation_vector进行旋转
  9. T.pretranslate ( Eigen::Vector3d ( 1,3,4 ) ); // 把平移向量设成(1,3,4)
  10. // 对于仿射和射影变换,使用 Eigen::Affine3d 和 Eigen::Projective3d 即可,略
  11. // 四元数
  12. // 可以直接把AngleAxis赋值给四元数,反之亦然
  13. Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_vector );
  14. cout<<"quaternion = \n"<<q.coeffs() <<endl; // 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
  15. // 也可以把旋转矩阵赋给它
  16. q = Eigen::Quaterniond ( rotation_matrix );
  17. cout<<"quaternion = \n"<<q.coeffs() <<endl;
  18. // 使用四元数旋转一个向量,使用重载的乘法即可
  19. v_rotated = q*v; // 注意数学上是qvq^{-1}
  20. cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl;

visualizeGeometry

null

Ch4

useSophus

  1. #include <Eigen/Core>
  2. #include <Eigen/Geometry>
  3. #include "sophus/so3.h"
  4. #include "sophus/se3.h"
  5. // 沿Z轴转90度的旋转矩阵
  6. Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
  7. Sophus::SO3 SO3_R(R); // Sophus::SO(3)可以直接从旋转矩阵构造
  8. Sophus::SO3 SO3_v( 0, 0, M_PI/2 ); // 亦可从旋转向量构造
  9. Eigen::Quaterniond q(R); // 或者四元数
  10. Sophus::SO3 SO3_q( q );
  11. // 使用对数映射获得它的李代数
  12. Eigen::Vector3d so3 = SO3_R.log();
  13. // hat 为向量到反对称矩阵
  14. cout<<"so3 hat=\n"<<Sophus::SO3::hat(so3)<<endl;
  15. // 相对的,vee为反对称到向量
  16. cout<<"so3 hat vee= "<<Sophus::SO3::vee( Sophus::SO3::hat(so3) ).transpose()<<endl; // transpose纯粹是为了输出美观一些
  17. // 增量扰动模型的更新
  18. Eigen::Vector3d update_so3(1e-4, 0, 0); //假设更新量为这么多
  19. Sophus::SO3 SO3_updated = Sophus::SO3::exp(update_so3)*SO3_R;
  20. // 注意这里不用先 hat (应该也可以, 重载过)
  21. // 李代数se(3) 是一个六维向量,方便起见先typedef一下
  22. typedef Eigen::Matrix<double,6,1> Vector6d;
  23. Vector6d se3 = SE3_Rt.log();
  24. cout<<"se3 = "<<se3.transpose()<<endl;
  25. // 观察输出,会发现在Sophus中,se(3)的平移在前,旋转在后.
  26. // 同样的,有hat和vee两个算符
  27. cout<<"se3 hat = "<<endl<<Sophus::SE3::hat(se3)<<endl;
  28. cout<<"se3 hat vee = "<<Sophus::SE3::vee( Sophus::SE3::hat(se3) ).transpose()<<endl;

Ch5

imageBasics

  1. #include <opencv2/core/core.hpp>
  2. #include <opencv2/highgui/highgui.hpp>
  3. cv::Mat image;
  4. image = cv::imread ( argv[1] ); //cv::imread函数读取指定路径下的图像
  5. // if ( image.data == nullptr ) //数据不存在,可能是文件不存在
  6. image.cols
  7. image.channels()
  8. // image.type() != CV_8UC1 && image.type() != CV_8UC3 )
  9. unsigned char* row_ptr = image.ptr<unsigned char> ( y ); // row_ptr是第y行的头指针
  10. unsigned char* data_ptr = &row_ptr[ x*image.channels() ]; // data_ptr 指向待访问的像素数据
  11. // unsigned char data = data_ptr[c]; // data为I(x,y)第c个通道的值
  12. cv::Mat image_clone = image.clone();
  13. image_clone ( cv::Rect ( 0,0,100,100 ) ).setTo ( 255 );

joinMap

  1. #include <Eigen/Geometry>
  2. #include <pcl/point_types.h>
  3. #include <pcl/io/pcd_io.h>
  4. #include <pcl/visualization/pcl_visualizer.h>
  5. vector<Eigen::Isometry3d, Eigen::aligned_allocator<Eigen::Isometry3d>> poses; // 相机位姿
  6. double data[7] = {0};
  7. for ( auto& d:data ) { fin>>d; }
  8. Eigen::Quaterniond q( data[6], data[3], data[4], data[5] );
  9. Eigen::Isometry3d T(q);
  10. T.pretranslate( Eigen::Vector3d( data[0], data[1], data[2] ));
  11. poses.push_back( T );
  12. typedef pcl::PointXYZRGB PointT;
  13. typedef pcl::PointCloud<PointT> PointCloud; // 需要指定 point cloud 里面点的类型
  14. PointCloud::Ptr pointCloud( new PointCloud ); // 用 ptr 来包装点云, 自动析构
  15. unsigned int d = depth.ptr<unsigned short> ( v )[u]; // 深度值
  16. if ( d==0 ) continue; // 为0表示没有测量到
  17. Eigen::Vector3d point;
  18. point[2] = double(d)/depthScale;
  19. point[0] = (u-cx)/fx*point[2];
  20. point[1] = (v-cy)/fy*point[2];
  21. Eigen::Vector3d pointWorld = T*point;
  22. PointT p ;
  23. p.x = pointWorld[0];
  24. p.y = pointWorld[1];
  25. p.z = pointWorld[2];
  26. p.b = color.data[ v*color.step+u*color.channels() ];
  27. p.g = color.data[ v*color.step+u*color.channels()+1 ];
  28. p.r = color.data[ v*color.step+u*color.channels()+2 ];
  29. pointCloud->points.push_back( p );
  30. pointCloud->is_dense = false;
  31. cout<<"点云共有"<<pointCloud->size()<<"个点."<<endl;
  32. pcl::io::savePCDFileBinary("map.pcd", *pointCloud );

Ch6

ceres_curve_fitting

  1. #include <opencv2/core/core.hpp>
  2. #include <ceres/ceres.h>
  3. // 代价函数的计算模型
  4. struct CURVE_FITTING_COST {
  5. CURVE_FITTING_COST ( double x, double y ) : _x ( x ), _y ( y ) {}
  6. template <typename T>
  7. bool operator() (
  8. const T *const abc, // 模型参数,有3维
  9. T* residual // 残差
  10. ) const
  11. {
  12. residual[0] = T (_y) - ceres::exp (abc[0]*T(_x)*T(_x) + abc[1]*T(_x) + abc[2]); // y-exp(ax^2+bx+c)
  13. return true;
  14. }
  15. const double _x, _y; // x,y数据
  16. };
  17. double abc[3] = {0,0,0}; // abc参数的估计值
  18. ceres::Problem problem;
  19. for ( int i=0; i<N; i++ ) {
  20. problem.AddResidualBlock ( // 向问题中添加误差项
  21. // 使用自动求导,模板参数:误差类型,输出维度,输入维度,维数要与前面struct中一致
  22. new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3> (
  23. new CURVE_FITTING_COST ( x_data[i], y_data[i] )
  24. ),
  25. nullptr, // 核函数,这里不使用,为空
  26. abc // 待估计参数
  27. );
  28. }
  29. // 配置求解器
  30. ceres::Solver::Options options; // 这里有很多配置项可以填
  31. options.linear_solver_type = ceres::DENSE_QR; // 增量方程如何求解
  32. options.minimizer_progress_to_stdout = true; // 输出到cout
  33. ceres::Solver::Summary summary; // 优化信息
  34. ceres::Solve ( options, &problem, &summary ); // 开始优化
  35. // 输出结果
  36. cout<<summary.BriefReport() <<endl;

g2o_curve_fitting

  1. // 曲线模型的顶点,模板参数:优化变量维度和数据类型
  2. // dimention and estimate type
  3. class CurveFittingVertex: public g2o::BaseVertex<3, Eigen::Vector3d> {
  4. public:
  5. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  6. virtual void setToOriginImpl() // 重置
  7. { _estimate << 0,0,0; }
  8. virtual void oplusImpl( const double* update ) // 更新
  9. { _estimate += Eigen::Vector3d(update); }
  10. // 存盘和读盘:留空
  11. virtual bool read( istream& in ) {}
  12. virtual bool write( ostream& out ) const {}
  13. };
  14. // 误差模型 模板参数:观测值维度,类型,连接顶点类型
  15. class CurveFittingEdge: public g2o::BaseUnaryEdge<1,double,CurveFittingVertex>
  16. {
  17. public:
  18. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  19. CurveFittingEdge( double x ): BaseUnaryEdge(), _x(x) {}
  20. // 计算曲线模型误差
  21. void computeError()
  22. {
  23. const CurveFittingVertex* v = static_cast<const CurveFittingVertex*> (_vertices[0]);
  24. const Eigen::Vector3d abc = v->estimate();
  25. _error(0,0) = _measurement - std::exp( abc(0,0)*_x*_x + abc(1,0)*_x + abc(2,0) ) ;
  26. }
  27. virtual bool read( istream& in ) {}
  28. virtual bool write( ostream& out ) const {}
  29. public:
  30. double _x; // x 值, y 值为 _measurement
  31. };
  32. // 构建图优化,先设定g2o
  33. typedef g2o::BlockSolver< g2o::BlockSolverTraits<3,1> > Block; // 每个误差项优化变量维度为3,误差值维度为1
  34. Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>(); // 线性方程求解器
  35. Block* solver_ptr = new Block( linearSolver ); // 矩阵块求解器
  36. // 梯度下降方法,从GN, LM, DogLeg 中选
  37. g2o::OptimizationAlgorithmWithHessian *solver;
  38. if (argc > 2) {
  39. if (strcmp(argv[2],"GN")) {
  40. cout << "using GN" << endl;
  41. solver = new g2o::OptimizationAlgorithmGaussNewton(solver_ptr );
  42. } else if (strcmp(argv[2],"LM")) {
  43. cout << "using LM" << endl;
  44. solver = new g2o::OptimizationAlgorithmLevenberg( solver_ptr );
  45. } else if (strcmp(argv[2],"DogLeg")) {
  46. cout << "using DogLeg" << endl;
  47. solver = new g2o::OptimizationAlgorithmDogleg(solver_ptr );
  48. }
  49. } else {
  50. cout << "using default solver: Levenberg (options are 'GN', 'LM', 'DogLeg')" << endl;
  51. solver = new g2o::OptimizationAlgorithmLevenberg( solver_ptr );
  52. }
  53. g2o::SparseOptimizer optimizer; // 图模型
  54. optimizer.setAlgorithm( solver ); // 设置求解器
  55. optimizer.setVerbose( true ); // 打开调试输出
  56. // 往图中增加顶点
  57. CurveFittingVertex* v = new CurveFittingVertex();
  58. v->setEstimate( Eigen::Vector3d(0,0,0) );
  59. v->setId(0);
  60. optimizer.addVertex( v );
  61. // 往图中增加边
  62. for ( int i=0; i<N; i++ )
  63. {
  64. CurveFittingEdge* edge = new CurveFittingEdge( x_data[i] );
  65. edge->setId(i);
  66. edge->setVertex( 0, v ); // 设置连接的顶点
  67. edge->setMeasurement( y_data[i] ); // 观测数值
  68. edge->setInformation( Eigen::Matrix<double,1,1>::Identity()*1/(w_sigma*w_sigma) ); // 信息矩阵:协方差矩阵之逆
  69. optimizer.addEdge( edge );
  70. }
  71. // 执行优化
  72. cout<<"start optimization"<<endl;
  73. chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  74. optimizer.initializeOptimization();
  75. optimizer.optimize(100);
  76. chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  77. chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>( t2-t1 );
  78. cout<<"solve time cost = "<<time_used.count()<<" seconds. "<<endl;
  79. cout<<"actual model: " << a << " " << b << " " << c << endl;
  80. // 输出优化值
  81. Eigen::Vector3d abc_estimate = v->estimate();
  82. cout<<"estimated model: "<<abc_estimate.transpose()<<endl;

Ch7

feature_extraction.cpp

  1. std::vector<KeyPoint> keypoints_1, keypoints_2;
  2. Mat descriptors_1, descriptors_2;
  3. Ptr<FeatureDetector> detector = ORB::create();
  4. Ptr<DescriptorExtractor> descriptor = ORB::create();
  5. // Ptr<FeatureDetector> detector = FeatureDetector::create(detector_name);
  6. // Ptr<DescriptorExtractor> descriptor = DescriptorExtractor::create(descriptor_name);
  7. Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create ( "BruteForce-Hamming" );
  8. //-- 第一步:检测 Oriented FAST 角点位置
  9. detector->detect ( img_1,keypoints_1 );
  10. //-- 第二步:根据角点位置计算 BRIEF 描述子
  11. descriptor->compute ( img_1, keypoints_1, descriptors_1 );
  12. //-- 第三步:对两幅图像中的BRIEF描述子进行匹配,使用 Hamming 距离
  13. vector<DMatch> matches;
  14. //BFMatcher matcher ( NORM_HAMMING );
  15. matcher->match ( descriptors_1, descriptors_2, matches );
  16. //-- 第四步:匹配点对筛选
  17. double min_dist=10000, max_dist=0;
  18. //找出所有匹配之间的最小距离和最大距离, 即是最相似的和最不相似的两组点之间的距离
  19. min_dist = min_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;
  20. max_dist = max_element( matches.begin(), matches.end(), [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;
  21. //当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.
  22. std::vector< DMatch > good_matches;
  23. for ( int i = 0; i < descriptors_1.rows; i++ ) {
  24. if ( matches[i].distance <= max ( 2*min_dist, 30.0 ) ) {
  25. good_matches.push_back ( matches[i] );
  26. }
  27. }
  28. drawMatches ( img_1, keypoints_1, img_2, keypoints_2, good_matches, img_goodmatch );

pose_estimation_2d2d.cpp

  1. void find_feature_matches (
  2. const Mat& img_1, const Mat& img_2,
  3. std::vector<KeyPoint>& keypoints_1,
  4. std::vector<KeyPoint>& keypoints_2,
  5. std::vector< DMatch >& matches );
  6. Mat pose_estimation_2d2d (
  7. std::vector<KeyPoint> keypoints_1,
  8. std::vector<KeyPoint> keypoints_2,
  9. std::vector< DMatch > matches,
  10. Mat& R, Mat& t );
  11. // 像素坐标转相机归一化坐标
  12. Point2d pixel2cam ( const Point2d& p, const Mat& K );
  13. cout<<"一共找到了"<<matches.size() <<"组匹配点"<<endl;
  14. Mat R,t;
  15. Mat E = pose_estimation_2d2d ( keypoints_1, keypoints_2, matches, R, t );
  16. // Eigen::Matrix<double,Dynamic,Dynamic> eigenTxR; cv2eigen(txR, eigenTxR);
  17. // Eigen::Matrix<double,Dynamic,Dynamic> eigenE; cv2eigen(E, eigenE);
  18. // cout << "diff: " << eigenE.array() / eigenTxR.array();
  19. Mat ratio;
  20. divide(txR, E, ratio);
  21. cout << "diff: " << ratio;
  22. //-- 验证对极约束
  23. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  24. for ( DMatch m: matches ) {
  25. Point2d pt1 = pixel2cam ( keypoints_1[ m.queryIdx ].pt, K );
  26. Point2d pt2 = pixel2cam ( keypoints_2[ m.trainIdx ].pt, K );
  27. Mat y1 = ( Mat_<double> ( 3,1 ) << pt1.x, pt1.y, 1 );
  28. Mat y2 = ( Mat_<double> ( 3,1 ) << pt2.x, pt2.y, 1 );
  29. Mat d = y2.t() * t_x * R * y1;
  30. cout << "epipolar constraint = " << d << endl;
  31. }
  32. //////////////
  33. Point2d pixel2cam ( const Point2d& p, const Mat& K ) {
  34. return Point2d
  35. (
  36. // ( x -cx ) / fx
  37. ( p.x - K.at<double> ( 0,2 ) ) / K.at<double> ( 0,0 ),
  38. // ( y-cy ) / fy
  39. ( p.y - K.at<double> ( 1,2 ) ) / K.at<double> ( 1,1 )
  40. );
  41. }
  42. Mat pose_estimation_2d2d ( std::vector<KeyPoint> keypoints_1,
  43. std::vector<KeyPoint> keypoints_2,
  44. std::vector< DMatch > matches,
  45. Mat& R, Mat& t )
  46. {
  47. // 相机内参,TUM Freiburg2
  48. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  49. //-- 把匹配点转换为vector<Point2f>的形式
  50. vector<Point2f> points1;
  51. vector<Point2f> points2;
  52. for ( int i = 0; i < ( int ) matches.size(); i++ ) {
  53. points1.push_back ( keypoints_1[matches[i].queryIdx].pt );
  54. points2.push_back ( keypoints_2[matches[i].trainIdx].pt );
  55. }
  56. //-- 计算基础矩阵
  57. Mat fundamental_matrix;
  58. fundamental_matrix = findFundamentalMat ( points1, points2, CV_FM_8POINT );
  59. cout<<"fundamental_matrix is "<<endl<< fundamental_matrix<<endl;
  60. //-- 计算本质矩阵
  61. Point2d principal_point ( 325.1, 249.7 ); //相机光心, TUM dataset标定值, cx, cy
  62. double focal_length = 521; //相机焦距, TUM dataset标定值, fx, fy
  63. Mat essential_matrix;
  64. essential_matrix = findEssentialMat ( points1, points2, focal_length, principal_point );
  65. cout<<"essential_matrix is "<<endl<< essential_matrix<<endl;
  66. //-- 计算单应矩阵
  67. Mat homography_matrix;
  68. // ransacReprojThreshold
  69. homography_matrix = findHomography ( points1, points2, RANSAC, 3 );
  70. cout<<"homography_matrix is "<<endl<<homography_matrix<<endl;
  71. //-- 从本质矩阵中恢复旋转和平移信息.
  72. recoverPose ( essential_matrix, points1, points2, R, t, focal_length, principal_point );
  73. cout<<"R is "<<endl<<R<<endl;
  74. cout<<"t is "<<endl<<t<<endl;
  75. return essential_matrix;
  76. }

pose_estimation_3d2d.cpp

  1. void bundleAdjustment (
  2. const vector<Point3f> points_3d,
  3. const vector<Point2f> points_2d,
  4. const Mat& K,
  5. Mat& R, Mat& t
  6. );
  7. // 建立3D点
  8. Mat d1 = imread ( argv[3], CV_LOAD_IMAGE_UNCHANGED ); // 深度图为16位无符号数,单通道图像
  9. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  10. vector<Point3f> pts_3d;
  11. vector<Point2f> pts_2d;
  12. for ( DMatch m:matches ) {
  13. ushort d = d1.ptr<unsigned short> (int ( keypoints_1[m.queryIdx].pt.y )) [ int ( keypoints_1[m.queryIdx].pt.x ) ];
  14. if ( d == 0 ) // bad depth
  15. continue;
  16. float dd = d/5000.0;
  17. Point2d p1 = pixel2cam ( keypoints_1[m.queryIdx].pt, K );
  18. pts_3d.push_back ( Point3f ( p1.x*dd, p1.y*dd, dd ) );
  19. pts_2d.push_back ( keypoints_2[m.trainIdx].pt );
  20. }
  21. Mat r, t;
  22. solvePnP ( pts_3d, pts_2d, K, Mat(), r, t, false ); // 调用OpenCV 的 PnP 求解,可选择EPNP,DLS等方法
  23. Mat R;
  24. cv::Rodrigues ( r, R ); // r为旋转向量形式,用Rodrigues公式转换为矩阵
  25. bundleAdjustment ( pts_3d, pts_2d, K, R, t );
  26. void bundleAdjustment (
  27. const vector< Point3f > points_3d,
  28. const vector< Point2f > points_2d,
  29. const Mat& K,
  30. Mat& R, Mat& t )
  31. {
  32. // 初始化g2o
  33. typedef g2o::BlockSolver< g2o::BlockSolverTraits<6,3> > Block; // pose 维度为 6, landmark 维度为 3
  34. Block::LinearSolverType* linearSolver = new g2o::LinearSolverCSparse<Block::PoseMatrixType>(); // 线性方程求解器
  35. Block* solver_ptr = new Block ( linearSolver ); // 矩阵块求解器
  36. g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg ( solver_ptr );
  37. g2o::SparseOptimizer optimizer;
  38. optimizer.setAlgorithm ( solver );
  39. // vertex
  40. g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap(); // camera pose
  41. Eigen::Matrix3d R_mat;
  42. cv2eigen(R, R_mat);
  43. // R_mat <<
  44. // R.at<double> ( 0,0 ), R.at<double> ( 0,1 ), R.at<double> ( 0,2 ),
  45. // R.at<double> ( 1,0 ), R.at<double> ( 1,1 ), R.at<double> ( 1,2 ),
  46. // R.at<double> ( 2,0 ), R.at<double> ( 2,1 ), R.at<double> ( 2,2 );
  47. pose->setId ( 0 );
  48. pose->setEstimate ( g2o::SE3Quat (
  49. R_mat,
  50. Eigen::Vector3d ( t.at<double> ( 0,0 ), t.at<double> ( 1,0 ), t.at<double> ( 2,0 ) )
  51. ) );
  52. optimizer.addVertex ( pose );
  53. int index = 1;
  54. for ( const Point3f p:points_3d ) // landmarks
  55. {
  56. g2o::VertexSBAPointXYZ* point = new g2o::VertexSBAPointXYZ();
  57. point->setId ( index++ );
  58. point->setEstimate ( Eigen::Vector3d ( p.x, p.y, p.z ) );
  59. point->setMarginalized ( true ); // g2o 中必须设置 marg 参见第十讲内容
  60. optimizer.addVertex ( point );
  61. }
  62. // parameter: camera intrinsics
  63. g2o::CameraParameters* camera = new g2o::CameraParameters (
  64. K.at<double> ( 0,0 ), Eigen::Vector2d ( K.at<double> ( 0,2 ), K.at<double> ( 1,2 ) ), 0
  65. );
  66. camera->setId ( 0 );
  67. optimizer.addParameter ( camera );
  68. // edges
  69. index = 1;
  70. for ( const Point2f p:points_2d )
  71. {
  72. g2o::EdgeProjectXYZ2UV* edge = new g2o::EdgeProjectXYZ2UV();
  73. edge->setId ( index );
  74. edge->setVertex ( 0, dynamic_cast<g2o::VertexSBAPointXYZ*> ( optimizer.vertex ( index ) ) );
  75. edge->setVertex ( 1, pose );
  76. edge->setMeasurement ( Eigen::Vector2d ( p.x, p.y ) );
  77. edge->setParameterId ( 0,0 );
  78. edge->setInformation ( Eigen::Matrix2d::Identity() );
  79. optimizer.addEdge ( edge );
  80. index++;
  81. }
  82. chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  83. optimizer.setVerbose ( true );
  84. optimizer.initializeOptimization();
  85. optimizer.optimize ( 100 );
  86. chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  87. chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>> ( t2-t1 );
  88. cout<<"optimization costs time: "<<time_used.count() <<" seconds."<<endl;
  89. cout<<endl<<"after optimization:"<<endl;
  90. cout<<"T="<<endl<<Eigen::Isometry3d ( pose->estimate() ).matrix() <<endl;

pose_estimation_3d3d.cpp

  1. void pose_estimation_3d3d (
  2. const vector<Point3f>& pts1,
  3. const vector<Point3f>& pts2,
  4. Mat& R, Mat& t
  5. );
  6. void bundleAdjustment(
  7. const vector<Point3f>& points_3d,
  8. const vector<Point3f>& points_2d,
  9. Mat& R, Mat& t
  10. );
  11. // g2o edge
  12. class EdgeProjectXYZRGBDPoseOnly : public g2o::BaseUnaryEdge<3, Eigen::Vector3d, g2o::VertexSE3Expmap>
  13. {
  14. public:
  15. EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
  16. EdgeProjectXYZRGBDPoseOnly( const Eigen::Vector3d& point ) : _point(point) {}
  17. virtual void computeError()
  18. {
  19. const g2o::VertexSE3Expmap* pose = static_cast<const g2o::VertexSE3Expmap*> ( _vertices[0] );
  20. // measurement is p, point is p'
  21. _error = _measurement - pose->estimate().map( _point );
  22. }
  23. virtual void linearizeOplus()
  24. {
  25. g2o::VertexSE3Expmap* pose = static_cast<g2o::VertexSE3Expmap *>(_vertices[0]);
  26. g2o::SE3Quat T(pose->estimate());
  27. Eigen::Vector3d xyz_trans = T.map(_point);
  28. double x = xyz_trans[0];
  29. double y = xyz_trans[1];
  30. double z = xyz_trans[2];
  31. _jacobianOplusXi(0,0) = 0;
  32. _jacobianOplusXi(0,1) = -z;
  33. _jacobianOplusXi(0,2) = y;
  34. _jacobianOplusXi(0,3) = -1;
  35. _jacobianOplusXi(0,4) = 0;
  36. _jacobianOplusXi(0,5) = 0;
  37. _jacobianOplusXi(1,0) = z;
  38. _jacobianOplusXi(1,1) = 0;
  39. _jacobianOplusXi(1,2) = -x;
  40. _jacobianOplusXi(1,3) = 0;
  41. _jacobianOplusXi(1,4) = -1;
  42. _jacobianOplusXi(1,5) = 0;
  43. _jacobianOplusXi(2,0) = -y;
  44. _jacobianOplusXi(2,1) = x;
  45. _jacobianOplusXi(2,2) = 0;
  46. _jacobianOplusXi(2,3) = 0;
  47. _jacobianOplusXi(2,4) = 0;
  48. _jacobianOplusXi(2,5) = -1;
  49. }
  50. bool read ( istream& in ) {}
  51. bool write ( ostream& out ) const {}
  52. protected:
  53. Eigen::Vector3d _point;
  54. };
  55. ushort d1 = depth1.ptr<unsigned short> ( int ( keypoints_1[m.queryIdx].pt.y ) ) [ int ( keypoints_1[m.queryIdx].pt.x ) ];
  56. ushort d2 = depth2.ptr<unsigned short> ( int ( keypoints_2[m.trainIdx].pt.y ) ) [ int ( keypoints_2[m.trainIdx].pt.x ) ];
  57. if ( d1==0 || d2==0 ) // bad depth
  58. continue;
  59. Point2d p1 = pixel2cam ( keypoints_1[m.queryIdx].pt, K );
  60. Point2d p2 = pixel2cam ( keypoints_2[m.trainIdx].pt, K );
  61. float dd1 = float ( d1 ) /5000.0;
  62. float dd2 = float ( d2 ) /5000.0;
  63. pts1.push_back ( Point3f ( p1.x*dd1, p1.y*dd1, dd1 ) );
  64. pts2.push_back ( Point3f ( p2.x*dd2, p2.y*dd2, dd2 ) );
  65. Mat R, t;
  66. pose_estimation_3d3d ( pts1, pts2, R, t );
  67. cout<<"ICP via SVD results: "<<endl;
  68. cout<<"R = "<<R<<endl;
  69. cout<<"t = "<<t<<endl;
  70. cout<<"R_inv = "<<R.t() <<endl;
  71. cout<<"t_inv = "<<-R.t() *t<<endl;
  72. cout<<"calling bundle adjustment"<<endl;
  73. // bundleAdjustment( pts1, pts2, R, t );
  74. void pose_estimation_3d3d (
  75. const vector<Point3f>& pts1,
  76. const vector<Point3f>& pts2,
  77. Mat& R, Mat& t
  78. )
  79. {
  80. Point3f p1, p2; // center of mass
  81. int N = pts1.size();
  82. for ( int i=0; i<N; i++ ) { p1 += pts1[i]; p2 += pts2[i]; }
  83. p1 = Point3f( Vec3f(p1) / N);
  84. p2 = Point3f( Vec3f(p2) / N);
  85. vector<Point3f> q1 ( N ), q2 ( N ); // remove the center
  86. for ( int i=0; i<N; i++ ) { q1[i] = pts1[i] - p1; q2[i] = pts2[i] - p2; }
  87. // compute q1*q2^T
  88. Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
  89. for ( int i=0; i<N; i++ )
  90. {
  91. W += Eigen::Vector3d ( q1[i].x, q1[i].y, q1[i].z ) * Eigen::Vector3d ( q2[i].x, q2[i].y, q2[i].z ).transpose();
  92. }
  93. cout<<"W="<<W<<endl;
  94. // SVD on W
  95. Eigen::JacobiSVD<Eigen::Matrix3d> svd ( W, Eigen::ComputeFullU|Eigen::ComputeFullV );
  96. Eigen::Matrix3d U = svd.matrixU();
  97. Eigen::Matrix3d V = svd.matrixV();
  98. cout<<"U="<<U<<endl;
  99. cout<<"V="<<V<<endl;
  100. Eigen::Matrix3d R_ = U* ( V.transpose() );
  101. Eigen::Vector3d t_ = Eigen::Vector3d ( p1.x, p1.y, p1.z ) - R_ * Eigen::Vector3d ( p2.x, p2.y, p2.z );
  102. eigen2cv(R_, R);
  103. eigen2cv(t_, t);
  104. }
  105. void bundleAdjustment (
  106. const vector< Point3f >& pts1,
  107. const vector< Point3f >& pts2,
  108. Mat& R, Mat& t )
  109. {
  110. // 初始化g2o
  111. typedef g2o::BlockSolver< g2o::BlockSolverTraits<6,3> > Block; // pose维度为 6, landmark 维度为 3
  112. Block::LinearSolverType* linearSolver = new g2o::LinearSolverEigen<Block::PoseMatrixType>(); // 线性方程求解器
  113. Block* solver_ptr = new Block( linearSolver ); // 矩阵块求解器
  114. g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton( solver_ptr );
  115. g2o::SparseOptimizer optimizer;
  116. optimizer.setAlgorithm( solver );
  117. // vertex
  118. g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap(); // camera pose
  119. pose->setId(0);
  120. pose->setEstimate( g2o::SE3Quat(
  121. Eigen::Matrix3d::Identity(),
  122. Eigen::Vector3d( 0,0,0 )
  123. ) );
  124. optimizer.addVertex( pose );
  125. // edges
  126. int index = 1;
  127. vector<EdgeProjectXYZRGBDPoseOnly*> edges;
  128. for ( size_t i=0; i<pts1.size(); i++ )
  129. {
  130. EdgeProjectXYZRGBDPoseOnly* edge = new EdgeProjectXYZRGBDPoseOnly(
  131. Eigen::Vector3d(pts2[i].x, pts2[i].y, pts2[i].z) );
  132. edge->setId( index );
  133. edge->setVertex( 0, dynamic_cast<g2o::VertexSE3Expmap*> (pose) );
  134. edge->setMeasurement( Eigen::Vector3d(
  135. pts1[i].x, pts1[i].y, pts1[i].z) );
  136. edge->setInformation( Eigen::Matrix3d::Identity()*1e4 );
  137. optimizer.addEdge(edge);
  138. index++;
  139. edges.push_back(edge);
  140. }
  141. chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  142. optimizer.setVerbose( true );
  143. optimizer.initializeOptimization();
  144. optimizer.optimize(10);
  145. chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  146. chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2-t1);
  147. cout<<"optimization costs time: "<<time_used.count()<<" seconds."<<endl;
  148. cout<<endl<<"after optimization:"<<endl;
  149. cout<<"T="<<endl<<Eigen::Isometry3d( pose->estimate() ).matrix()<<endl;

triangulation.cpp

  1. void triangulation (
  2. const vector<KeyPoint>& keypoint_1,
  3. const vector<KeyPoint>& keypoint_2,
  4. const std::vector< DMatch >& matches,
  5. const Mat& R, const Mat& t,
  6. vector<Point3d>& points
  7. );
  8. Mat R,t;
  9. pose_estimation_2d2d ( keypoints_1, keypoints_2, matches, R, t );
  10. //-- 三角化
  11. vector<Point3d> points;
  12. triangulation( keypoints_1, keypoints_2, matches, R, t, points );
  13. //-- 验证三角化点与特征点的重投影关系
  14. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  15. for ( int i=0; i<matches.size(); i++ )
  16. {
  17. Point2d pt1_cam = pixel2cam( keypoints_1[ matches[i].queryIdx ].pt, K );
  18. Point2d pt1_cam_3d(
  19. points[i].x/points[i].z,
  20. points[i].y/points[i].z
  21. );
  22. cout<<"point in the first camera frame: "<<pt1_cam<<endl;
  23. cout<<"point projected from 3D "<<pt1_cam_3d<<", d="<<points[i].z<<endl;
  24. // 第二个图
  25. Point2f pt2_cam = pixel2cam( keypoints_2[ matches[i].trainIdx ].pt, K );
  26. Mat pt2_trans = R*( Mat_<double>(3,1) << points[i].x, points[i].y, points[i].z ) + t;
  27. pt2_trans /= pt2_trans.at<double>(2,0);
  28. cout<<"point in the second camera frame: "<<pt2_cam<<endl;
  29. cout<<"point reprojected from second frame: "<<pt2_trans.t()<<endl;
  30. cout<<endl;
  31. }
  32. void pose_estimation_2d2d (
  33. const std::vector<KeyPoint>& keypoints_1,
  34. const std::vector<KeyPoint>& keypoints_2,
  35. const std::vector< DMatch >& matches,
  36. Mat& R, Mat& t )
  37. {
  38. // 相机内参,TUM Freiburg2
  39. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  40. //-- 把匹配点转换为vector<Point2f>的形式
  41. vector<Point2f> points1;
  42. vector<Point2f> points2;
  43. for ( int i = 0; i < ( int ) matches.size(); i++ )
  44. {
  45. points1.push_back ( keypoints_1[matches[i].queryIdx].pt );
  46. points2.push_back ( keypoints_2[matches[i].trainIdx].pt );
  47. }
  48. //-- 计算基础矩阵
  49. Mat fundamental_matrix;
  50. fundamental_matrix = findFundamentalMat ( points1, points2, CV_FM_8POINT );
  51. cout<<"fundamental_matrix is "<<endl<< fundamental_matrix<<endl;
  52. //-- 计算本质矩阵
  53. Point2d principal_point ( 325.1, 249.7 ); //相机主点, TUM dataset标定值
  54. int focal_length = 521; //相机焦距, TUM dataset标定值
  55. Mat essential_matrix;
  56. essential_matrix = findEssentialMat ( points1, points2, focal_length, principal_point );
  57. cout<<"essential_matrix is "<<endl<< essential_matrix<<endl;
  58. //-- 计算单应矩阵
  59. Mat homography_matrix;
  60. homography_matrix = findHomography ( points1, points2, RANSAC, 3 );
  61. cout<<"homography_matrix is "<<endl<<homography_matrix<<endl;
  62. //-- 从本质矩阵中恢复旋转和平移信息.
  63. recoverPose ( essential_matrix, points1, points2, R, t, focal_length, principal_point );
  64. cout<<"R is "<<endl<<R<<endl;
  65. cout<<"t is "<<endl<<t<<endl;
  66. }
  67. void triangulation (
  68. const vector< KeyPoint >& keypoint_1,
  69. const vector< KeyPoint >& keypoint_2,
  70. const std::vector< DMatch >& matches,
  71. const Mat& R, const Mat& t,
  72. vector< Point3d >& points )
  73. {
  74. Mat T1 = (Mat_<float> (3,4) <<
  75. 1,0,0,0,
  76. 0,1,0,0,
  77. 0,0,1,0);
  78. Mat T2 = (Mat_<float> (3,4) <<
  79. R.at<double>(0,0), R.at<double>(0,1), R.at<double>(0,2), t.at<double>(0,0),
  80. R.at<double>(1,0), R.at<double>(1,1), R.at<double>(1,2), t.at<double>(1,0),
  81. R.at<double>(2,0), R.at<double>(2,1), R.at<double>(2,2), t.at<double>(2,0)
  82. );
  83. Mat K = ( Mat_<double> ( 3,3 ) << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1 );
  84. vector<Point2f> pts_1, pts_2;
  85. for ( DMatch m:matches )
  86. {
  87. // 将像素坐标转换至相机坐标
  88. pts_1.push_back ( pixel2cam( keypoint_1[m.queryIdx].pt, K) );
  89. pts_2.push_back ( pixel2cam( keypoint_2[m.trainIdx].pt, K) );
  90. }
  91. Mat pts_4d;
  92. cv::triangulatePoints( T1, T2, pts_1, pts_2, pts_4d );
  93. // 转换成非齐次坐标
  94. for ( int i=0; i<pts_4d.cols; i++ )
  95. {
  96. Mat x = pts_4d.col(i);
  97. x /= x.at<float>(3,0); // 归一化
  98. Point3d p (
  99. x.at<float>(0,0),
  100. x.at<float>(1,0),
  101. x.at<float>(2,0)
  102. );
  103. points.push_back( p );
  104. }
  105. }

Ch8

directmethod

opticalflow

sparse:

  1. // 一次测量的值,包括一个世界坐标系下三维点与一个灰度值
  2. struct Measurement {
  3. Measurement ( Eigen::Vector3d p, float g ) : pos_world ( p ), grayscale ( g ) {}
  4. Eigen::Vector3d pos_world;
  5. float grayscale;
  6. };
  7. inline Eigen::Vector3d project2Dto3D ( int x, int y, int d,
  8. float fx, float fy, float cx, float cy,
  9. float scale
  10. ) {
  11. float zz = float ( d ) /scale;
  12. float xx = zz* ( x-cx ) /fx;
  13. float yy = zz* ( y-cy ) /fy;
  14. return Eigen::Vector3d ( xx, yy, zz );
  15. }
  16. inline Eigen::Vector2d project3Dto2D ( float x, float y, float z,
  17. float fx, float fy, float cx, float cy
  18. ) {
  19. float u = fx*x/z+cx;
  20. float v = fy*y/z+cy;
  21. return Eigen::Vector2d ( u,v );
  22. }
  23. // 直接法估计位姿
  24. // 输入:测量值(空间点的灰度),新的灰度图,相机内参; 输出:相机位姿
  25. // 返回:true为成功,false失败
  26. bool poseEstimationDirect ( const vector<Measurement>& measurements,
  27. cv::Mat* gray,
  28. Eigen::Matrix3f& intrinsics,
  29. Eigen::Isometry3d& Tcw );
  30. // project a 3d point into an image plane, the error is photometric error
  31. // an unary edge with one vertex SE3Expmap (the pose of camera)
  32. class EdgeSE3ProjectDirect: public BaseUnaryEdge< 1, double, VertexSE3Expmap>
  33. {
  34. public:
  35. EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  36. EdgeSE3ProjectDirect() {}
  37. EdgeSE3ProjectDirect ( Eigen::Vector3d point, float fx, float fy, float cx, float cy, cv::Mat* image )
  38. : x_world_ ( point ), fx_ ( fx ), fy_ ( fy ), cx_ ( cx ), cy_ ( cy ), image_ ( image )
  39. {}
  40. virtual void computeError()
  41. {
  42. const VertexSE3Expmap* v =static_cast<const VertexSE3Expmap*> ( _vertices[0] );
  43. Eigen::Vector3d x_local = v->estimate().map ( x_world_ );
  44. float x = x_local[0]*fx_/x_local[2] + cx_;
  45. float y = x_local[1]*fy_/x_local[2] + cy_;
  46. // check x,y is in the image
  47. if ( x-4<0 || ( x+4 ) >image_->cols || ( y-4 ) <0 || ( y+4 ) >image_->rows )
  48. {
  49. _error ( 0,0 ) = 0.0;
  50. this->setLevel ( 1 );
  51. }
  52. else
  53. {
  54. _error ( 0,0 ) = getPixelValue ( x,y ) - _measurement;
  55. }
  56. }
  57. // plus in manifold
  58. virtual void linearizeOplus( )
  59. {
  60. if ( level() == 1 ) {
  61. _jacobianOplusXi = Eigen::Matrix<double, 1, 6>::Zero();
  62. return;
  63. }
  64. VertexSE3Expmap* vtx = static_cast<VertexSE3Expmap*> ( _vertices[0] );
  65. Eigen::Vector3d xyz_trans = vtx->estimate().map ( x_world_ ); // q in book
  66. double x = xyz_trans[0];
  67. double y = xyz_trans[1];
  68. double invz = 1.0/xyz_trans[2];
  69. double invz_2 = invz*invz;
  70. float u = x*fx_*invz + cx_;
  71. float v = y*fy_*invz + cy_;
  72. // jacobian from se3 to u,v
  73. // NOTE that in g2o the Lie algebra is (\omega, \epsilon), where \omega is so(3) and \epsilon the translation
  74. // 书上 195 页公式, omega 和 epsilon 顺序和书上不同
  75. Eigen::Matrix<double, 2, 6> jacobian_uv_ksai;
  76. jacobian_uv_ksai ( 0,0 ) = - x*y*invz_2 *fx_;
  77. jacobian_uv_ksai ( 0,1 ) = ( 1+ ( x*x*invz_2 ) ) *fx_;
  78. jacobian_uv_ksai ( 0,2 ) = - y*invz *fx_;
  79. jacobian_uv_ksai ( 0,3 ) = invz *fx_;
  80. jacobian_uv_ksai ( 0,4 ) = 0;
  81. jacobian_uv_ksai ( 0,5 ) = -x*invz_2 *fx_;
  82. jacobian_uv_ksai ( 1,0 ) = - ( 1+y*y*invz_2 ) *fy_;
  83. jacobian_uv_ksai ( 1,1 ) = x*y*invz_2 *fy_;
  84. jacobian_uv_ksai ( 1,2 ) = x*invz *fy_;
  85. jacobian_uv_ksai ( 1,3 ) = 0;
  86. jacobian_uv_ksai ( 1,4 ) = invz *fy_;
  87. jacobian_uv_ksai ( 1,5 ) = -y*invz_2 *fy_;
  88. Eigen::Matrix<double, 1, 2> jacobian_pixel_uv;
  89. jacobian_pixel_uv ( 0,0 ) = ( getPixelValue ( u+1,v )-getPixelValue ( u-1,v ) ) /2;
  90. jacobian_pixel_uv ( 0,1 ) = ( getPixelValue ( u,v+1 )-getPixelValue ( u,v-1 ) ) /2;
  91. _jacobianOplusXi = jacobian_pixel_uv*jacobian_uv_ksai;
  92. }
  93. // dummy read and write functions because we don't care...
  94. virtual bool read ( std::istream& in ) {}
  95. virtual bool write ( std::ostream& out ) const {}
  96. protected:
  97. // get a gray scale value from reference image (bilinear interpolated)
  98. inline float getPixelValue ( float x, float y )
  99. {
  100. uchar* data = & image_->data[ int ( y ) * image_->step + int ( x ) ];
  101. float xx = x - floor ( x );
  102. float yy = y - floor ( y );
  103. return float (
  104. ( 1-xx ) * ( 1-yy ) * data[0] +
  105. xx* ( 1-yy ) * data[1] +
  106. ( 1-xx ) *yy*data[ image_->step ] +
  107. xx*yy*data[image_->step+1]
  108. );
  109. }
  110. public:
  111. Eigen::Vector3d x_world_; // 3D point in world frame
  112. float cx_=0, cy_=0, fx_=0, fy_=0; // Camera intrinsics
  113. cv::Mat* image_=nullptr; // reference image
  114. };
  115. int main ( int argc, char** argv )
  116. {
  117. if ( argc != 2 ) {
  118. cout << "usage: useLK path_to_dataset" << endl;
  119. return 1;
  120. }
  121. srand ( ( unsigned int ) time ( 0 ) );
  122. string path_to_dataset = argv[1];
  123. string associate_file = path_to_dataset + "/associate.txt";
  124. ifstream fin ( associate_file );
  125. string rgb_file, depth_file, time_rgb, time_depth;
  126. cv::Mat color, depth, gray;
  127. vector<Measurement> measurements;
  128. // 相机内参
  129. float cx = 325.5;
  130. float cy = 253.5;
  131. float fx = 518.0;
  132. float fy = 519.0;
  133. float depth_scale = 1000.0;
  134. Eigen::Matrix3f K;
  135. K<<fx,0.f,cx,0.f,fy,cy,0.f,0.f,1.0f;
  136. Eigen::Isometry3d Tcw = Eigen::Isometry3d::Identity();
  137. cv::Mat prev_color;
  138. // 我们以第一个图像为参考,对后续图像和参考图像做直接法
  139. for ( int index=0; index<10; index++ ) {
  140. cout<<"*********** loop "<<index<<" ************"<<endl;
  141. // 1305031473.127744 rgb/1305031473.127744.png 1305031473.124072 depth/1305031473.124072.png
  142. fin>>time_rgb>>rgb_file>>time_depth>>depth_file;
  143. color = cv::imread ( path_to_dataset+"/"+rgb_file );
  144. depth = cv::imread ( path_to_dataset+"/"+depth_file, -1 );
  145. if ( color.data==nullptr || depth.data==nullptr )
  146. continue;
  147. cv::cvtColor ( color, gray, cv::COLOR_BGR2GRAY );
  148. if ( index ==0 ) {
  149. // 对第一帧提取FAST特征点
  150. vector<cv::KeyPoint> keypoints;
  151. cv::Ptr<cv::FastFeatureDetector> detector = cv::FastFeatureDetector::create();
  152. detector->detect ( color, keypoints );
  153. for ( auto kp:keypoints ) {
  154. // 去掉邻近边缘处的点
  155. if ( kp.pt.x < 20 || kp.pt.y < 20 || ( kp.pt.x+20 ) >color.cols || ( kp.pt.y+20 ) >color.rows )
  156. continue;
  157. ushort d = depth.ptr<ushort> ( cvRound ( kp.pt.y ) ) [ cvRound ( kp.pt.x ) ];
  158. if ( d==0 )
  159. continue;
  160. Eigen::Vector3d p3d = project2Dto3D ( kp.pt.x, kp.pt.y, d, fx, fy, cx, cy, depth_scale );
  161. float grayscale = float ( gray.ptr<uchar> ( cvRound ( kp.pt.y ) ) [ cvRound ( kp.pt.x ) ] );
  162. measurements.push_back ( Measurement ( p3d, grayscale ) );
  163. }
  164. prev_color = color.clone();
  165. continue;
  166. }
  167. // 使用直接法计算相机运动
  168. chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  169. poseEstimationDirect ( measurements, &gray, K, Tcw );
  170. chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  171. chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>> ( t2-t1 );
  172. cout<<"direct method costs time: "<<time_used.count() <<" seconds."<<endl;
  173. cout<<"Tcw="<<Tcw.matrix() <<endl;
  174. // plot the feature points
  175. cv::Mat img_show ( color.rows*2, color.cols, CV_8UC3 );
  176. prev_color.copyTo ( img_show ( cv::Rect ( 0,0,color.cols, color.rows ) ) );
  177. color.copyTo ( img_show ( cv::Rect ( 0,color.rows,color.cols, color.rows ) ) );
  178. for ( Measurement m:measurements ) {
  179. if ( rand() > RAND_MAX/5 ) continue;
  180. Eigen::Vector3d p = m.pos_world;
  181. Eigen::Vector2d pixel_prev = project3Dto2D ( p ( 0,0 ), p ( 1,0 ), p ( 2,0 ), fx, fy, cx, cy );
  182. Eigen::Vector3d p2 = Tcw*m.pos_world;
  183. Eigen::Vector2d pixel_now = project3Dto2D ( p2 ( 0,0 ), p2 ( 1,0 ), p2 ( 2,0 ), fx, fy, cx, cy );
  184. if ( pixel_now(0,0)<0 || pixel_now(0,0)>=color.cols || pixel_now(1,0)<0 || pixel_now(1,0)>=color.rows )
  185. continue;
  186. float b = 255*float ( rand() ) /RAND_MAX;
  187. float g = 255*float ( rand() ) /RAND_MAX;
  188. float r = 255*float ( rand() ) /RAND_MAX;
  189. cv::circle ( img_show, cv::Point2d ( pixel_prev ( 0,0 ), pixel_prev ( 1,0 ) ), 8, cv::Scalar ( b,g,r ), 2 );
  190. cv::circle ( img_show, cv::Point2d ( pixel_now ( 0,0 ), pixel_now ( 1,0 ) +color.rows ), 8, cv::Scalar ( b,g,r ), 2 );
  191. cv::line ( img_show,
  192. cv::Point2d ( pixel_prev ( 0,0 ), pixel_prev ( 1,0 ) ),
  193. cv::Point2d ( pixel_now ( 0,0 ), pixel_now ( 1,0 ) +color.rows ),
  194. cv::Scalar ( b,g,r ), 1 );
  195. }
  196. cv::imshow ( "result", img_show );
  197. cv::waitKey ( 0 );
  198. }
  199. return 0;
  200. }
  201. bool poseEstimationDirect ( const vector< Measurement >& measurements, cv::Mat* gray, Eigen::Matrix3f& K, Eigen::Isometry3d& Tcw )
  202. {
  203. // 初始化g2o
  204. typedef g2o::BlockSolver<g2o::BlockSolverTraits<6,1>> DirectBlock; // 求解的向量是6*1的
  205. DirectBlock::LinearSolverType* linearSolver = new g2o::LinearSolverDense< DirectBlock::PoseMatrixType > ();
  206. DirectBlock* solver_ptr = new DirectBlock ( linearSolver );
  207. // g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton( solver_ptr ); // G-N
  208. g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg ( solver_ptr ); // L-M
  209. g2o::SparseOptimizer optimizer;
  210. optimizer.setAlgorithm ( solver );
  211. optimizer.setVerbose( true );
  212. g2o::VertexSE3Expmap* pose = new g2o::VertexSE3Expmap();
  213. pose->setEstimate ( g2o::SE3Quat ( Tcw.rotation(), Tcw.translation() ) );
  214. pose->setId ( 0 );
  215. optimizer.addVertex ( pose );
  216. // 添加边
  217. int id=1;
  218. for ( Measurement m: measurements )
  219. {
  220. EdgeSE3ProjectDirect* edge = new EdgeSE3ProjectDirect (
  221. m.pos_world,
  222. K ( 0,0 ), K ( 1,1 ), K ( 0,2 ), K ( 1,2 ), gray
  223. );
  224. edge->setVertex ( 0, pose );
  225. edge->setMeasurement ( m.grayscale );
  226. edge->setInformation ( Eigen::Matrix<double,1,1>::Identity() );
  227. edge->setId ( id++ );
  228. optimizer.addEdge ( edge );
  229. }
  230. cout<<"edges in graph: "<<optimizer.edges().size() <<endl;
  231. optimizer.initializeOptimization();
  232. optimizer.optimize ( 30 );
  233. Tcw = pose->estimate();
  234. }

L2

  1. #include <iostream>
  2. #include <cmath>
  3. #include <Eigen/Core>
  4. #include <Eigen/Geometry>
  5. using namespace std;
  6. int main ( int argc, char** argv )
  7. {
  8. // w, x, y, z
  9. Eigen::Quaterniond q1 = Eigen::Quaterniond (0.35,0.2,0.3,0.1); q1.normalize();
  10. Eigen::Quaterniond q2 = Eigen::Quaterniond (-0.5,0.4,-0.1,0.2); q2.normalize();
  11. Eigen::Isometry3d T1_cw = Eigen::Isometry3d::Identity();
  12. T1_cw.rotate ( q1 );
  13. T1_cw.pretranslate ( Eigen::Vector3d ( 0.3,0.1,0.1 ) );
  14. Eigen::Isometry3d T2_cw = Eigen::Isometry3d::Identity();
  15. T2_cw.rotate ( q2 );
  16. T2_cw.pretranslate ( Eigen::Vector3d ( -0.1,0.5,0.3 ) );
  17. cout << "Answer:\n" << T2_cw * T1_cw.inverse() * Eigen::Vector3d (0.5, 0.0, 0.2) << endl;
  18. return 0;
  19. }

Eigen 文档阅读

image_1c9t3js879q14h01k431m7a1c2mm.png-126.6kB

Linear algebra and decompositions

http://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html

This page explains how to solve linear systems, compute various decompositions such as LU, QR, SVD, eigendecompositions... After reading this page, don't miss our catalogue of dense matrix decompositions.

最通用的是 colPivHouseHolderQr 分解. 一个折中.

image_1c9t2n9gs12871qhgb5v1und1ski9.png-72.4kB

如果正定的话, 就可以用 llt 和 ldlt.
For example, if your matrix is positive definite, the above table says that a very good choice is then the LLT or LDLT decomposition.

可能需要检验是否计算正确:

  1. MatrixXd A = MatrixXd::Random(100,100);
  2. MatrixXd b = MatrixXd::Random(100,50);
  3. MatrixXd x = A.fullPivLu().solve(b);
  4. double relative_error = (A*x - b).norm() / b.norm(); // norm() is L2 norm
  5. cout << "The relative error is:\n" << relative_error << endl;

SelfAdjointtEigenSolver 之类的.A SelfAdjoint 是说对称, 对弈实矩阵来说就是对称阵. 对于 complex, 是 hermitten.
Where the conjugate transpose is denoted A^(H), A^(T) is the transpose, and z^_ is the complex conjugate. If a matrix is self-adjoint, it is said to be Hermitian. 所以用的 H 表示 conjugate transpose 啊.

You need an eigendecomposition here, see available such decompositions on this page. Make sure to check if your matrix is self-adjoint, as is often the case in these problems. Here's an example using SelfAdjointEigenSolver, it could easily be adapted to general matrices using EigenSolver or ComplexEigenSolver.

对与小矩阵 (4x4 及以下), 用 inverse 和 determinant 还不错. 如果是大矩阵, 就应该避免 (很慢), 用分解的方法来做. 而且, 实践上, 用 determinant 来看是否可以 invert 是很不对的 (有误差).

Rank-revealing decompositions

Rank-revealing decompositions offer at least a rank() method. They can also offer convenience methods such as isInvertible(), and some are also providing methods to compute the kernel (null-space) and image (column-space) of the matrix, as is the case with FullPivLU.

Solving linear least squares systems

http://eigen.tuxfamily.org/dox/group__LeastSquares.html

This page describes how to solve linear least squares systems using Eigen. An overdetermined system of equations, say Ax = b, has no solutions. In this case, it makes sense to search for the vector x which is closest to being a solution, in the sense that the difference Ax - b is as small as possible. This x is called the least square solution (if the Euclidean norm is used).

Of these, the SVD decomposition is generally the most accurate but the slowest, normal equations is the fastest but least accurate, and the QR decomposition is in between.

If the matrix A is ill-conditioned (条件数不够), then this is not a good method, because the condition number of ATA is the square of the condition number of A. This means that you lose twice as many digits using normal equation than if you use the other methods.

Cholesky 分解是分解成三角, 只对 Hermitian, 正定的矩阵有用.

image_1c9t3q5b81s7q1r658qvrfl13bf13.png-0.8kB

L 是下三角, L* 是她的 conjugate transpose. 一般函数叫 llt.

LDL* 这个 L 是 lower unitriangular (lower unit triangular, 对角线上都是 1). D 是 diagnal.
函数叫 ldlt.

The LDL variant, if efficiently implemented, requires the same space and computational complexity to construct and use but avoids extracting square roots.6 Some indefinite matrices for which no Cholesky decomposition exists have an LDL decomposition with negative entries in D. For these reasons, the LDL decomposition may be preferred. For real matrices, the factorization has the form A = LDLT and is often referred to as LDLT decomposition (or LDLT decomposition, or LDL'). It is closely related to the eigendecomposition of real symmetric matrices, A = QΛQT.

In linear algebra, the Cholesky decomposition or Cholesky factorization is a decomposition of a Hermitian, positive-definite matrix into the product of a lower triangular matrix and its conjugate transpose, which is useful for efficient numerical solutions, e.g. Monte Carlo simulations. It was discovered by André-Louis Cholesky for real matrices. When it is applicable, the Cholesky decomposition is roughly twice as efficient as the LU decomposition for solving systems of linear equations.

image_1c9t437dfpp11c5910bb3kk1npi1g.png-123.1kB

image_1c9t4ccgs8336a16tlpftasb20.png-61kB

Several people in this thread asked why you would ever want to do Cholesky on a non-positive-definite matrix. I thought I'd mention a case would motivate this question.

Cholesky decomposition is used to generate Gaussian random variants given a covariance matrix using xi=∑jLijzj
where each zj Normal(0,1) and L is the Cholesky decomposition of the covariance matrix.

#

http://eigen.tuxfamily.org/dox/group__TopicLinearAlgebraDecompositions.html

L3

cpp

他们每天假装关心你,站在道德的高地假惺惺说你的国家没有人权,怜悯你的苦难。但是当你真正争取到一点点尊严和体面,在此过程中损害了他们一根毛的利益,他们就会扔掉所谓的优雅体面怜悯和同情心,用最恶毒的词语咒骂你。他们自己开大排量汽车,却见不得穷人烧火取暖说你污染环境。自己享受血汗工厂廉价的商品服务,却"忧国忧民"觉得中国劳工涨工资成本高削弱国家竞争力。这就是我从来对某些发达国家口惠实不至的伪善嗤之以鼻的原因。这也是我对国内某些自私又爱表演的慈善家嗤之以鼻的原因。每到此时,我才更觉得放弃优越的物质生活,不畏艰难困苦,摒弃小我私心的白求恩大夫,农机专家/核物理学家寒春阳早夫妇这样的人才是真正高尚和脱离低级趣味的人。

作者:吴名士
链接:https://www.zhihu.com/question/270454447/answer/354818407
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

考验人性三守则:一、不把被考验者置于极端的环境,而是确保他处于一个大体平常的生理、心理环境。你不能羞辱、打击别人,然后指望别人继续对你好。你不能在爆发大饥荒的时候要求女人饿死也不出卖肉体,男人饿死也不可偷盗。二、不对人性做出过高期待,差不多就行了。你必须了解社会,知道中不溜的人是个啥德行。三、出于公平原则,若被考验者反过来考验你,你要接受。

作者:城市猎人
链接:https://www.zhihu.com/question/35930303/answer/353387591
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

其中有一条说:出轨是因为,我死水一般的婚姻生活让我备受孤独的煎熬。

因为大多数时候,人是选择做一个好人,而不是本性如此。

LiDAR 即 Light Detection And Ranging,
是一个集激光、全球定位系统
(GPS)和惯性导航系统(INS)三种现代尖端技术于一身的空间测量系统,
能够快速、精确地获取地面三维信息,被广泛用于地面数据探测和模型的恢
复、重建等应用中,在这些应用中显示了巨大的前景,并逐渐成为三维数据
模型获取的一种重要方法。

在使用机载 LiDAR 时需要除了激光数据意外的其他数据来保证激光数
据的精确性。在传感器移动时,传感器的高度、位置、姿态都需要记录下来,
用于确定每一束激光发出与接收时刻的位置与姿态。这些额外的信息对于
激光数据的完整性是至关重要的。

激光是以波长划分的,600-1000nm 的激光主要用于非科学用途,不过
由于这个波段的激光能够被人眼接收,所以此波段的激光的最大功率被限制
在低水平,一防止对人眼的伤害。一般使用 1550nm 波长的激光,因为这个
波段的激光不能够被人眼接收,所以能够以更高的功率使用。 长波激光主要
用于长距离、低精度的测量。1550nm 波长的另一个好处是它不会在夜视仪
下显示,因此非常适合于军事用途。

精度高是使用 LiDAR 数据的主要原因之一, LiDAR 技术对于测量大面
积的地形高程数据是一个省时、省力的一个选择。因此确定数据采集精度和
存储精度的等级对于 LiDAR 数据采集与之后的使用是很重要的。数据提供
商可以通过控制一些数据采集过程中的参数,来控制数据采集的精度与数据
采集成本。当这些数据被采集之后,就会被分析它的精度是否是能够满足后
面的应用需求。数据存储精度的确定是以最大化数据应用为原则的。LiDAR
数据的数据精度一般会随着元数据和质量监测报告一起提供给用户。

点数据一般使用 LAS 格式存储,LAS 格式是一种以二进制形式存储的
文件格式。LiDAR 点数据不仅仅只包含 X、Y、Z 三个坐标值,还可以存储
很多其它的数据,如反射强度、反射数量、点分类,具体参照图 5。当然这
些数据也可以使用文本文件的形式存储,但是这样使得数据文件大小变得很
大,不利于数据处理。
DEM 数据是规则的数据,一般存储在栅格数据文件中,如 GeoTiff(.tif)、
Esri Grid(.adf)、ERDAS 的 img。这些 DEM 数据可以使用许多内差技术生
成。这些内差技术简单的有最临近像元法,复杂的有克里金法,如图 6。最
常见的 DEM 生成使用距离的导数作为权值内差得到栅格点的高程。

单束激光多次反射

以穿透水面获得底部的数据点。水深 LiDAR 系统使用的激光在干净的水
体中最多可以穿透 70 米,但是在浑浊或者有波浪的水体中穿透能力仅仅只
有几米。

Towards Autonomous Driving

holistic - Bing dictionary
US[hoʊ'lɪstɪk]UK[həʊ'lɪstɪk]

adj.整体的;全面的;功能整体性的
网络全人;全盘的;整体论

holistic lane centering

Road
Experience
Management
(REM)

image_1c9t9i5ne1hlt17me3cv10021lo82d.png-1052.5kB

image_1c9t9jsqp7vo1ah2rr6dof9s62q.png-1174.2kB

Sensing alone (righthand image) cannot robustly detect the drivable path to enable safe hands-
free control. The Roadbook data can bridge the gap as localization is based on a high degree of
redundancy of landmarks and is therefore robust.

where pedestrians are more vulnerable to accidents and
together with REM can provide data about infrastructure
(lanes, traffic signs) for decision makers

Absolute Safety is impossible – typical highway
situation

“Self-driving cars should be statistically
better than a human driver”

Goal: Self-driving cars should never be esponsible for accidents, meaning:

RSS is:
A mathematical, interpretable, model, formalizing the “common
sense" or “human judgement" of “who is responsible for an
accident"

image_1c9ta1k531jjo1kukv2jlvg1ke137.png-93.2kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注