• 高博SLAM基础课第三次作业——轨迹绘图计算误差


    作业描述

    我为你准备了一个轨迹文件(code/trajectory.txt)。该文件的每一行由若干个数据组成,格式为
    [t, t x , t y , t z , q x , q y , q z , q w ],
    其中 t 为时间,t x , t y , t z 为 T W C 的平移部分,q x , q y , q z , q w 是四元数表示的 T W C 的旋转部分,q w
    为四元数实部。同时,我为你提供了画图程序 draw_trajectory.cpp 文件。该文件提供了画图部分
    的代码,请你完成数据读取部分的代码,然后书写 CMakeLists.txt 以让此程序运行起来。注意我
    们需要用到 Pangolin 库来画图,所以你需要事先安装 Pangolin(如果你做了第一次作业,那么现
    在已经安装了)。CMakeLists.txt 可以参照 ORB-SLAM2 部分。

    遇到的坑:

      代码文件放进clion编译不出来,include后也发现各种未定义引用,后来发现要改Cmakelist.txt才行

      照着ORB-SLAM2的Cmake写法,把eigen3和Pangolin库都target_link进来,报的未定义引用只剩了Sophus库,再用find_package时候发现不行,网上查到是因为Sophus的lib文件在find_package时候不能引入路径。于是手动

      set(Sophus_LIBRARIES "/usr/local/lib/libSophus.so")即可

      在绘图时候发现轨迹的一端经常拉出一条长直线,推测是vector最后一项的值有问题,把绘图循环里的末尾值减少一个,轨迹正常。

    Cmakelist.txt如下:

     1 cmake_minimum_required(VERSION 2.8)
     2 project(useEigen)
     3 find_package(Pangolin REQUIRED)
     4 find_package(Eigen3 3.1.0 REQUIRED)
     5 
     6 set(Sophus_LIBRARIES "/usr/local/lib/libSophus.so")
     7 set(CMAKE_BUILD_TYPE "Release")
     8 SET( CMAKE_CXX_FLAGS "-std=c++11 -O3")
     9 
    10 include_directories(
    11         ${Pangolin_INCLUDE_DIRS}
    12         ${EIGEN3_INCLUDE_DIR}
    13         )
    14 
    15 add_executable(eigenMatrix eigenMatrix.cpp)
    16 
    17 add_executable(tra trajectory.cpp)
    18 
    19 target_link_libraries(tra
    20         ${EIGEN3_LIBS}
    21         ${Pangolin_LIBRARIES}
    22         ${Sophus_LIBRARIES}
    23         )

    源码如下:

    #include <sophus/se3.h>
    #include <string>
    #include <iostream>
    #include <fstream>
    #include <unistd.h>
    // need pangolin for plotting trajectory
    #include <pangolin/pangolin.h>
    #include <Eigen/Core>
    // 稠密矩阵的代数运算(逆,特征值等)
    #include <Eigen/Dense>
    using namespace std;
    using namespace Eigen;
    // path to trajectory file
    string trajectory_file = "/home/steve/L3/code/groundtruth.txt";
    string estimated_file = "/home/steve/L3/code/estimated.txt";
    // function for plotting trajectory, don't edit this code
    // start point is red and end point is blue
    void DrawTrajectory(vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > ,vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > );
    
    int main(int argc, char **argv) {
    
        vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > poses1;
        vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > poses2;
        /// implement pose reading code
        // start your code here (5~10 lines)
        ifstream f(trajectory_file);
        string s0;
        while(!f.eof()) {
            Vector3d t_tmp;
            double db1, db2, db3,db4;
            f>> db1 >> db1 >> db2 >> db3;
            t_tmp << db1, db2, db3;
            f >> db1 >> db2 >> db3>>db4;
            Quaterniond q(db4,db1,db2,db3);
            //cout<<q<<endl;
            poses1.emplace_back(Sophus::SE3(q,t_tmp));
        }
    
        ifstream g(estimated_file);
    
        while(!g.eof()) {
            Vector3d t_tmp;
            double db1, db2, db3,db4;
            g>> db1 >> db1 >> db2 >> db3;
            t_tmp << db1, db2, db3;
            g >> db1 >> db2 >> db3>>db4;
            Quaterniond q(db4,db1,db2,db3);
            //cout<<q<<endl;
            poses2.emplace_back(Sophus::SE3(q,t_tmp));
        }
        // end your code here
    
       //  draw trajectory in pangolin
        DrawTrajectory(poses1,poses2);
    
        return 0;
    
    
    
    }
    
    /*******************************************************************************************/
    void DrawTrajectory(vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > poses1,vector<Sophus::SE3, Eigen::aligned_allocator<Sophus::SE3> > poses2){
        if (poses1.empty()&&poses1.empty() ){
            cerr << "Trajectory is empty!" << endl;
            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 < poses1.size() - 2; i++) {
                glColor3f(1 - (float) i / poses1.size(), 0.0f, (float) i / poses1.size());
                glBegin(GL_LINES);
                auto p1 = poses1[i], p2 = poses1[i + 1];
                glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]);
                glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
                glEnd();
            }
            for (size_t i = 0; i < poses2.size() - 2; i++) {
                glColor3f(1 - (float) i / poses2.size(), 0.0f, (float) i / poses2.size());
                glBegin(GL_LINES);
                auto p3 = poses2[i], p4 = poses2[i + 1];
                glVertex3d(p3.translation()[0], p3.translation()[1], p3.translation()[2]);
                glVertex3d(p4.translation()[0], p4.translation()[1], p4.translation()[2]);
                glEnd();
            }
    
    
    
            pangolin::FinishFrame();
            usleep(5000);   // sleep 5 ms
        }
    
    }

    学习另一博客把绘图部分输入改为两个使得可以绘出两条轨迹比较。对estimated和groundtruth做了对比,绘图如下:

    一开始以为是3条线后来发现是根据长度变颜色。。。

    光滑的是基准,粗糙的是估计值(应该还没有做后端优化)

    下一题:计算误差值

    阅读se3各接口后调用代码如下

     double error_s=0;
        for(int i=0;i<poses1.size()-1;i++){//也是要-1不然结果不对
            error_s+= Sophus::SE3::log(poses1[i].inverse()*poses2[i]).squaredNorm();
        }
        error_s=sqrt(error_s/(poses1.size()-1));
    
        cout<<error_s<<endl;

    hat是把6维李代数变成4×4李群矩阵,vee是相反,这里log是对数映射,自动就变成了6维李代数,不需要调用vee或者hat

    调用结果:

    PS:

    Eigen::Isometry3d T; 是SE(3)的eigen写法

    初始化为调用T.pretranslate(t)可以输入t

          T.prerotate(R)输入R

    或者用Isometry3d T(q)用四元数初始化R

    使用时用T.matrix()

  • 相关阅读:
    国内DP厂家的相关资料信息
    【转】挟天子以令诸侯博客关于TCP/IP模型与OSI模型的区别
    TMS320CC657基本外围电路调试
    TMS320C6657双核DSP的图像处理系统开发(1):硬件相关tips
    TI c6657开发资源
    PCIE接口的说明
    Flash Builder4注册机
    myeclipse 方法上加上@Override就报错的处理方法
    oracle安装完成后目录中不论有没有tnsnames.ora和listener.ora文件 PLSQL都能连上的问题解决方法
    POJO和javabean的区别
  • 原文地址:https://www.cnblogs.com/CaptainLL/p/10824064.html
Copyright © 2020-2023  润新知