视觉SLAM十四讲作业练习(5)3D-3D ICP

3D-3D:ICP

(1)SVD方法

首先定义符号: p i p_i pi :第二个图像中的数据; p i ′ p_i' pi :第一个图像中的数据。

具体步骤:

(1)定义两组点的质心 p p p p ′ p' p
p = 1 n ∑ i = 1 n p i p=\frac{1}{n}\sum_{i=1}^{n}p_i p=n1i=1npi

p ′ = 1 n ∑ i = 1 n p i ′ p'=\frac{1}{n}\sum_{i=1}^{n}p_i' p=n1i=1npi

(2)计算去质心坐标
q i = p i − p ,      q i ′ = p i ′ − p ′ q_i = p_i-p,\;\;q_i'= p_i'-p' qi=pip,qi=pip
(3)定义矩阵W
W = ∑ i = 1 n q i q i ′ T W = \sum_{i=1}^{n}q_iq_i'^T W=i=1nqiqiT
W是一个3*3矩阵,对W进行奇异值分解:

W = U Σ V T W= U\Sigma V^T W=UΣVT
(4)解得R,t
R = U V T R = UV^T R=UVT

t = p − R p ′ t=p-Rp' t=pRp

ICP代码

void trans_ICP(const PointType &pts1, const PointType &pts2, Eigen::Matrix3d &R12, Eigen::Vector3d &t12){
    
    Eigen::Vector3d p1, p2;
    int N = pts1.size();
    cout << "N = \n" << N << endl;
    for (int i = 0; i < N; i++)
    {
        p1 += pts1[i];
        p2 += pts2[i];
    }
    // 计算质心
    p1 /= N;
    p2 /= N;

    PointType q1(N), q2(N); 

    for (int i = 0; i < N; i++)
    {
        q1[i] = pts1[i] - p1;
        q2[i] = pts2[i] - p2;
    }

    Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    for (int i = 0; i < N; i++)
    {
        W += q1[i] * q2[i].transpose();
    }
    cout << "W = \n" << W << endl;
    cout << "p1 = \n" << p1 << endl;
    Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
    Eigen::Matrix3d U = svd.matrixU();
    Eigen::Matrix3d V = svd.matrixV();

    cout << "U = \n" << U << endl;
    cout << "V = \n" << V << endl;

    R12 = U * (V.transpose());
    if (R12.determinant() < 0)
    {
        R12 = -R12;
    }
    t12 = p1 - R12 * p2;
    cout << "R = \n" << R12 << endl;
    cout << "t = \n" << t12 << endl;


}

(2)作业5 用ICP实现轨迹对齐

#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <pangolin/pangolin.h>
#include <sophus/se3.hpp>
#include <vector>
#include <string>
#include <unistd.h>
#include <fstream>
#include <iostream>
using namespace std;
typedef vector<Sophus::SE3d, Eigen::aligned_allocator<Sophus::SE3d>> TrajectoryType;
typedef vector<Eigen::Vector3d,Eigen::aligned_allocator<Eigen::Vector3d>> PointType;
// path to trajectory file
string trajectory_file = "../compare.txt";

// function for plotting trajectory, don't edit this code
// start point is red and end point is blue
void DrawTrajectory(TrajectoryType pose_e,TrajectoryType pose_g);
bool ReadTrajectory(const string &trajectory_file_path, TrajectoryType &poses_e, TrajectoryType &poses_g, 
                    PointType &pts1, PointType &pts2);
void trans_ICP(const PointType &pts1, const PointType &pts2, Eigen::Matrix3d &R12, Eigen::Vector3d &t12);

int main(int argc, char **argv)
{
    TrajectoryType poses_e; 
    TrajectoryType poses_g; 
    PointType pts1;
    PointType pts2;
    // 读取文件
    if (!ReadTrajectory(trajectory_file, poses_e, poses_g, pts1, pts2)) {
        cout << "读取失败...\n";
    } 
    else {
        cout << "读取成功!\n";
    }
    // Use ICP to estimate pose
    Eigen::Matrix3d R_eg;
    Eigen::Vector3d t_eg;
    trans_ICP(pts1, pts2, R_eg, t_eg);

    // Transform trajectory_g to the frame of trajectory_e 
    Sophus::SE3d T_eg(R_eg, t_eg);

    for (Sophus::SE3d &pose: poses_g) {
        pose = T_eg * pose;
    }
    // draw trajectory in pangolin
    DrawTrajectory(poses_e, poses_g);
    return 0;
}

bool ReadTrajectory(const string &trajectory_file_path, TrajectoryType &poses_e, TrajectoryType &poses_g,
                   PointType &pts1, PointType &pts2)
{
    // 读文件
    // 2创建流对象
    ifstream reader;

    // 3.1打开文件
    reader.open(trajectory_file_path, ios::in);
    
    // 3.2判断是否打开成功
    if (!reader.is_open()) {
        cout << "文件打开失败!!! " << trajectory_file_path << '\n';
        return false;
    }

    // 4读数据
    // 将读到的数据储存在SE(3)矩阵中
    // 估计轨迹 Te
    Eigen::Quaterniond q(1,0,0,0);  //四元数
    Eigen::Vector3d t(0,0,0);       //平移
    double time;

    while (!reader.eof())   //eof() 判断文件是否为空或者是否读到文件结尾
    {   //文件存储的格式为[time_e,t_e,q_e,time_g,t_g,q_g]

        reader >> time >> t[0] >> t[1] >> t[2] >> q.x() >> q.y() >> q.z() >> q.w();
        Sophus::SE3d SE3_e (q,t);
        pts1.push_back(t);
        poses_e.push_back(SE3_e);

        reader >> time >> t[0] >> t[1] >> t[2] >> q.x() >> q.y() >> q.z() >> q.w();
        Sophus::SE3d SE3_g (q,t);
        pts2.push_back(t);
        poses_g.push_back(SE3_g);
    }
    
    reader.close();

    return true;
}

void DrawTrajectory(TrajectoryType poses_e, TrajectoryType poses_g) {
    if (poses_e.empty() || poses_g.empty()) {
        cerr << "Trajectory is empty!\n";
        return;
    }

    // create pangolin window and plot the trajectory
    pangolin::CreateWindowAndBind("Trajectory Viewer", 1024, 768);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    pangolin::OpenGlRenderState s_cam(
            pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
            pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
    );

    pangolin::View &d_cam = pangolin::CreateDisplay()
            .SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
            .SetHandler(new pangolin::Handler3D(s_cam));


    while (pangolin::ShouldQuit() == false) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        d_cam.Activate(s_cam);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glLineWidth(2);
        for (size_t i = 0; i < poses_e.size() - 1; i++) {
            glColor3f(1 - (float) i / poses_e.size(), 0.0f, (float) i / poses_e.size());
            glBegin(GL_LINES);
            auto p1_e = poses_e[i], p2_e = poses_e[i + 1];
            glVertex3d(p1_e.translation()[0], p1_e.translation()[1], p1_e.translation()[2]);
            glVertex3d(p2_e.translation()[0], p2_e.translation()[1], p2_e.translation()[2]);
            glEnd();
        }
        for (size_t i = 0; i < poses_g.size() - 1; i++) {
            glColor3f(1 - (float) i / poses_g.size(), 0.0f, (float) i / poses_g.size());
            glBegin(GL_LINES);
            auto p1_g = poses_g[i], p2_g = poses_g[i + 1];
            glVertex3d(p1_g.translation()[0], p1_g.translation()[1], p1_g.translation()[2]);
            glVertex3d(p2_g.translation()[0], p2_g.translation()[1], p2_g.translation()[2]);
            glEnd();
        }
        pangolin::FinishFrame();
        usleep(5000);   // sleep 5 ms
    }
}

void trans_ICP(const PointType &pts1, const PointType &pts2, Eigen::Matrix3d &R12, Eigen::Vector3d &t12){
    
    Eigen::Vector3d p1, p2;
    int N = pts1.size();
    cout << "N = \n" << N << endl;
    for (int i = 0; i < N; i++)
    {
        p1 += pts1[i];
        p2 += pts2[i];
    }
    // 计算质心
    p1 /= N;
    p2 /= N;

    PointType q1(N), q2(N); 

    for (int i = 0; i < N; i++)
    {
        q1[i] = pts1[i] - p1;
        q2[i] = pts2[i] - p2;
    }

    Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
    for (int i = 0; i < N; i++)
    {
        W += q1[i] * q2[i].transpose();
    }
    cout << "W = \n" << W << endl;
    cout << "p1 = \n" << p1 << endl;
    Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
    Eigen::Matrix3d U = svd.matrixU();
    Eigen::Matrix3d V = svd.matrixV();

    cout << "U = \n" << U << endl;
    cout << "V = \n" << V << endl;

    R12 = U * (V.transpose());
    if (R12.determinant() < 0)
    {
        R12 = -R12;
    }
    t12 = p1 - R12 * p2;
    cout << "R = \n" << R12 << endl;
    cout << "t = \n" << t12 << endl;

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值