当前位置:   article > 正文

【SLAM学习】基于Pangolin绘制运动轨迹_pangolin projectmatrix

pangolin projectmatrix

Pangolin库是一个轻量级的跨平台视图控制库,主要用于可视化、交互和调试三维数据。该库提供了一系列图形界面工具,包括窗口、OpenGL渲染器、3D相机、图像显示等,可以方便地进行三维数据可视化和交互。

Pangolin库的主要特点如下:

  1. 轻量级:Pangolin库的代码量很少,只依赖于少量的第三方库,因此非常轻量级,可以很容易地集成到其他项目中。

  2. 跨平台:Pangolin库支持跨平台开发,可以在Windows、Linux和Mac OS等操作系统上使用。

  3. 多功能:Pangolin库提供了丰富的图形界面工具,包括窗口、OpenGL渲染器、3D相机、图像显示等,可以满足不同需求的开发者。

  4. 易于使用:Pangolin库的接口简单易用,可以快速搭建三维数据可视化和交互界面,同时还提供了丰富的示例程序和文档,方便开发者上手使用。

  5. 开源免费:Pangolin库是开源的,使用MIT许可证发布,可以免费使用、修改和分发。

Pangolin库下载:

https://github.com/stevenlovegrove/Pangolin/tree/1ec721d59ff6b799b9c24b8817f3b7ad2c929b83

百度网盘链接: https://pan.baidu.com/s/1W75vsmctFheksPfExzpU4A 提取码: rwg2 

  1. cd Pagolin
  2. mkdir build
  3. cd build
  4. cmake ..
  5. make

Eigen库是一个开源的C++线性代数库,它提供了快速的有关矩阵的线性代数运算,还包括解方程等功能。但没有提供李代数的支持。

Sophus库提供一个较好的李群和李代数的库,它很好的支持了SO(3),so(3),SE(3)和se(3)。是基于Eigen基础上开发的,继承了Eigen库中的定义的各个类。

安装Eigen库

sudo apt install libeigen3-dev

Eigen 头文件路径默认安装在:

"/usr/include/eigen3/"

注:Eigen是一个纯用头文件搭建起来的库,因此不需要链接库文件,只需要引入Eigen的头文件即可,后续的CMakeLists.txt 文件有体现到。

安装Sophus库

可以通过GitHub下载最新的:(现在是1.22.10版本)

git clone https://github.com/strasdat/Sophus.git

也可以通过百度网盘:(1.0.0)

链接: https://pan.baidu.com/s/1aCJzvDNRAfz_VEtH47DslA 提取码: xtak 

安装: 

  1. cd Sophus
  2. mkdir build
  3. cd build
  4. cmake ..
  5. make

若出现下面问题:

  Could not find a package configuration file provided by "fmt" with any of
  the following names:

    fmtConfig.cmake
    fmt-config.cmake

则需要再安装一个fmt库

  1. git clone https://github.com/fmtlib/fmt.git
  2. cd fmt
  3. mkdir build
  4. cd build
  5. cmake ..
  6. make
  7. sudo make install

使用Pangolin绘制轨迹 

  1. // pangolin库
  2. #include <pangolin/pangolin.h>
  3. // Eigen库
  4. #include <Eigen/Core>
  5. #include <Eigen/Dense>
  6. #include <unistd.h>
  7. #include <string>
  8. #include <vector>
  9. #include <fstream>
  10. // 演示了如何画出一个预先存储的轨迹
  11. std::string trajectory_file = "./trajectory.txt"; // 轨迹文件
  12. // 格式为:time,tx,ty,tz,qx,qy,qz,qw
  13. void DrawTrajectory(std::vector<Eigen::Isometry3d,Eigen::aligned_allocator<Eigen::Isometry3d>> poses){
  14. // create pangolin window and plot the trajectory
  15. pangolin::CreateWindowAndBind("Trajectory Viewer",1024,768); // 创建窗口
  16. glEnable(GL_DEPTH_TEST); // 开启深度测试
  17. glEnable(GL_BLEND); // 开启混合渲染
  18. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合函数
  19. pangolin::OpenGlRenderState s_cam(
  20. pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000), //投影矩阵
  21. // 屏幕的宽度、高度、相机的水平视角、垂直视角、相机在z轴上的位置、相机到屏幕的距离的最小值和最大值。
  22. pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0) // 视图矩阵
  23. // 相机的位置、相机观察的目标点、相机的朝向向量
  24. );
  25. pangolin::View &d_cam = pangolin::CreateDisplay()
  26. .SetBounds(0.0, 1.0, 0.0, 1.0, -1024.0f / 768.0f)
  27. // 表示窗口在x轴和y轴上的起点和终点位置,以及窗口的宽高比,宽高比为负数,则实际上是768:1024
  28. .SetHandler(new pangolin::Handler3D(s_cam));
  29. while (pangolin::ShouldQuit() == false) {
  30. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空颜色缓冲区和深度缓冲区
  31. d_cam.Activate(s_cam); // 激活显示窗口和渲染状态对象
  32. glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // 设置清屏颜色
  33. glLineWidth(2); // 设置线宽
  34. for (size_t i = 0; i < poses.size(); i++) {
  35. // 画每个位姿的三个坐标轴
  36. Eigen::Vector3d Ow = poses[i].translation(); // 获取相机位姿矩阵中的平移部分,即相机的位置。
  37. Eigen::Vector3d Xw = poses[i] * (0.1 * Eigen::Vector3d(1, 0, 0)); // 获取x轴方向的单位向量,乘以0.1是为了调整坐标轴线段的长度
  38. Eigen::Vector3d Yw = poses[i] * (0.1 * Eigen::Vector3d(0, 1, 0)); // 获取y轴方向的单位向量
  39. Eigen::Vector3d Zw = poses[i] * (0.1 * Eigen::Vector3d(0, 0, 1)); // 获取z轴方向的单位向量
  40. glBegin(GL_LINES); // 开始绘制线段
  41. glColor3f(1.0, 0.0, 0.0); // 设置线段颜色 rgb
  42. // 绘制线段的两个端点
  43. glVertex3d(Ow[0], Ow[1], Ow[2]); // 原点的坐标
  44. glVertex3d(Xw[0], Xw[1], Xw[2]); // x轴方向的坐标 ----> 绘制x轴线段 为红色
  45. glColor3f(0.0, 1.0, 0.0);
  46. glVertex3d(Ow[0], Ow[1], Ow[2]);// 原点的坐标
  47. glVertex3d(Yw[0], Yw[1], Yw[2]);// y轴方向的坐标 ----> 绘制y轴线段 为绿色
  48. glColor3f(0.0, 0.0, 1.0);
  49. glVertex3d(Ow[0], Ow[1], Ow[2]);// 原点的坐标
  50. glVertex3d(Zw[0], Zw[1], Zw[2]);// z轴方向的坐标 ----> 绘制z轴线段 为蓝色
  51. glEnd(); // 结束绘制
  52. }
  53. // 画出连线
  54. for (size_t i = 0; i < poses.size(); i++) {
  55. glColor3f(0.0, 0.0, 0.0); // 黑色
  56. glBegin(GL_LINES); // 开始绘制线段
  57. auto p1 = poses[i], p2 = poses[i + 1]; // 获取相邻相机位姿
  58. glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]); // 绘制线段的两个端点(相邻相机位姿的位置)
  59. glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
  60. glEnd();
  61. }
  62. pangolin::FinishFrame(); // 结束当前帧的绘制
  63. usleep(5000); // sleep 5ms ,每5ms绘画一次
  64. }
  65. }
  66. int main(int argc,char** argv)
  67. {
  68. std::vector<Eigen::Isometry3d,Eigen::aligned_allocator<Eigen::Isometry3d>> poses;
  69. std::ifstream fin(trajectory_file);
  70. if(!fin){
  71. std::cout << "cannot find trajectory file at " << trajectory_file << std::endl;
  72. return -1;
  73. }
  74. while (!fin.eof()) {
  75. double time, tx, ty, tz, qx, qy, qz, qw;
  76. fin >> time >> tx >> ty >> tz >> qx >> qy >> qz >> qw;
  77. Eigen::Isometry3d Twr(Eigen::Quaterniond(qw, qx, qy, qz));
  78. Twr.pretranslate(Eigen::Vector3d(tx, ty, tz));
  79. poses.push_back(Twr);
  80. }
  81. std::cout << "read total " << poses.size() << " pose entries" << std::endl;
  82. // draw trajectory in pangolin
  83. DrawTrajectory(poses);
  84. return 0;
  85. }

 CMakeLists.txt文件:

  1. # 声明要求的cmake最低版本
  2. cmake_minimum_required(VERSION 2.8)
  3. # 声明一个cmake工程
  4. project(Examples)
  5. # 查找系统的库路径
  6. find_package(Pangolin REQUIRED)
  7. # 添加头文件
  8. include_directories(
  9. "/usr/include/eigen3"
  10. ${Pangolin_INCLUDE_DIRS}
  11. )
  12. # 添加可执行程序
  13. add_executable(plotTrajectory plotTrajectory.cc)
  14. # 链接库文件
  15. target_link_libraries(plotTrajectory ${Pangolin_LIBRARIES})

估计轨迹的误差

在实际工程当中,需要估计一个算法估计轨迹(estimate trajectory)和真实轨迹(groundtruth trajectory) 的差异来评价算法的精度。

绝对轨迹误差(Absolute Trajectory Error,ATE

这实际上是每个位姿李代数的均方根误差(Root-Mean-Squared Error,RMSE

  1. #include <iostream>
  2. #include <fstream>
  3. #include <unistd.h>
  4. #include <string>
  5. #include <vector>
  6. // Pangolin库
  7. #include <pangolin/pangolin.h>
  8. // Sophus库
  9. #include <sophus/se3.hpp>
  10. // 轨迹文件路径
  11. std::string groundtruth_file = "./groundtruth.txt";
  12. std::string estimated_file = "./estimated.txt";
  13. using TrajectoryType = std::vector<Sophus::SE3d,Eigen::aligned_allocator<Sophus::SE3d> >;
  14. // 读取轨迹txt文件
  15. TrajectoryType ReadTrajectory(const std::string &path) {
  16. std::ifstream fin(path);
  17. TrajectoryType trajectory;
  18. if (!fin) {
  19. std::cerr << "trajectory " << path << " not found." << std::endl;
  20. return trajectory;
  21. }
  22. while (!fin.eof()) {
  23. double time, tx, ty, tz, qx, qy, qz, qw;
  24. fin >> time >> tx >> ty >> tz >> qx >> qy >> qz >> qw;
  25. Sophus::SE3d p1(Eigen::Quaterniond(qw, qx, qy, qz), Eigen::Vector3d(tx, ty, tz));
  26. trajectory.push_back(p1);
  27. }
  28. return trajectory;
  29. }
  30. // 绘制两条轨迹(一个是真实轨迹,一个是估计轨迹)
  31. void DrawTrajectory(const TrajectoryType &gt, const TrajectoryType &esti){
  32. // create pangolin window and plot the trajectory
  33. pangolin::CreateWindowAndBind("Trajectory Viewer",1024,768); // 创建窗口
  34. glEnable(GL_DEPTH_TEST); // 开启深度测试
  35. glEnable(GL_BLEND); // 开启混合渲染
  36. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 设置混合函数
  37. pangolin::OpenGlRenderState s_cam(
  38. pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000), //投影矩阵
  39. // 屏幕的宽度、高度、相机的水平视角、垂直视角、相机在z轴上的位置、相机到屏幕的距离的最小值和最大值。
  40. pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0) // 视图矩阵
  41. // 相机的位置、相机观察的目标点、相机的朝向向量
  42. );
  43. pangolin::View &d_cam = pangolin::CreateDisplay()
  44. .SetBounds(0.0, 1.0, 0.0, 1.0, -1024.0f / 768.0f)
  45. // 表示窗口在x轴和y轴上的起点和终点位置,以及窗口的宽高比,宽高比为负数,则实际上是768:1024
  46. .SetHandler(new pangolin::Handler3D(s_cam));
  47. while (pangolin::ShouldQuit() == false) {
  48. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空颜色缓冲区和深度缓冲区
  49. d_cam.Activate(s_cam); // 激活显示窗口和渲染状态对象
  50. glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // 设置清屏颜色
  51. glLineWidth(2); // 设置线宽
  52. // 画出真实轨迹
  53. for (size_t i = 0; i < gt.size() - 1; i++) {
  54. glColor3f(0.0, 0.0, 1.0); // 蓝色
  55. glBegin(GL_LINES); // 开始绘制线段
  56. auto p1 = gt[i], p2 = gt[i + 1]; // 获取相邻相机位姿
  57. glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]); // 绘制线段的两个端点(相邻相机位姿的位置)
  58. glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
  59. glEnd();
  60. }
  61. // 画出估计轨迹
  62. for (size_t i = 0; i < esti.size() - 1; i++) {
  63. glColor3f(1.0, 0.0, 0.0); // 红色
  64. glBegin(GL_LINES); // 开始绘制线段
  65. auto p1 = esti[i], p2 = esti[i + 1]; // 获取相邻相机位姿
  66. glVertex3d(p1.translation()[0], p1.translation()[1], p1.translation()[2]); // 绘制线段的两个端点(相邻相机位姿的位置)
  67. glVertex3d(p2.translation()[0], p2.translation()[1], p2.translation()[2]);
  68. glEnd();
  69. }
  70. pangolin::FinishFrame(); // 结束当前帧的绘制
  71. usleep(5000); // sleep 5ms ,每5ms绘画一次
  72. }
  73. }
  74. int main(int argc,char** argv)
  75. {
  76. TrajectoryType groundtruth = ReadTrajectory(groundtruth_file);
  77. TrajectoryType estimated = ReadTrajectory(estimated_file);
  78. // assert(!groundtruth.empty() && !estimated.empty());
  79. // assert(groundtruth.size() == estimated.size());
  80. if(groundtruth.empty() && estimated.empty() && (groundtruth.size() == estimated.size())){
  81. // 判断读取的真实轨迹文件和估计轨迹文件是否为空,并且判断两个文件的数据大小是否一致
  82. std::cout << "groudtruth file or estimated_file is error!" << std::endl;
  83. return -1;
  84. }
  85. // 计算ATE(绝对轨迹误差)
  86. double ate = 0;
  87. for (size_t i = 0; i < estimated.size();i++){
  88. Sophus::SE3d p1 = estimated[i];
  89. Sophus::SE3d p2 = groundtruth[i];
  90. double error = (p2.inverse()*p1).log().norm();
  91. ate += (error*error);
  92. }
  93. ate = ate / double(estimated.size());
  94. ate = sqrt(ate);
  95. std::cout << "ATE: " << ate << std::endl;
  96. // 绘制两条轨迹
  97. DrawTrajectory(groundtruth,estimated);
  98. return 0;
  99. }

  CMakeLists.txt文件:

  1. # 声明要求的cmake最低版本
  2. cmake_minimum_required(VERSION 3.16)
  3. # 声明一个cmake工程
  4. project(example)
  5. # 查找系统的库路径
  6. find_package(Sophus REQUIRED)
  7. find_package(Pangolin REQUIRED)
  8. # 添加头文件
  9. include_directories("/usr/include/eigen3")
  10. # 添加可执行程序
  11. add_executable(trajectoryError trajectoryError.cc)
  12. # 链接库文件
  13. target_link_libraries(trajectoryError
  14. Sophus::Sophus
  15. ${Pangolin_LIBRARIES}
  16. )

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/103033
推荐阅读
相关标签
  

闽ICP备14008679号