SLAM 2、代码基础
参考这个链接下的安装路径:https://github.com/gaoxiang12/slambook2/tree/master/3rdparty
一、安装环境
安装Eigen
Eigen有效支持线性代数,矩阵和矢量运算,数值分析及其相关的算法。
安装:
sudo apt install libeigen3-dev
安装Pangolin
Pangolin是SLAM常用的库之一,主要用于SLAM系统的可视化。它是基于OpenGL的,主要优势在于使用比OpenGL简单
使用tar -zxvf解压Pangolin资源包,并先下载以下内容:
sudo apt install doxygen
sudo apt install libglew-dev
# 如果在后续安装步骤,还报错缺什么,就百度一下安装一下
命令行cd到解压好的Pangolin文件夹内,按照以下步骤执行:
cd Pangolin
mkdir build && cd build
cmake -DCPP11_NO_BOOST=1 ..
make
sudo make install
此时,如果你有出现以下报错:
: error: ‘PixelFormat’ does not name a type; did you mean ‘AVPixelFormat’?
PixelFormat fmtdst;
^~~~~~~~~~~
AVPixelFormat
需要前往Pangolin/src底下,修改里面的CMakeLists,从第266行开始,注释以下内容:
find_package(FFMPEG QUIET)
# if(BUILD_PANGOLIN_VIDEO AND FFMPEG_FOUND)
# set(HAVE_FFMPEG 1)
# list(APPEND INTERNAL_INC ${FFMPEG_INCLUDE_DIRS} )
# list(APPEND LINK_LIBS ${FFMPEG_LIBRARIES} )
# list(APPEND HEADERS ${INCDIR}/video/drivers/ffmpeg.h)
# list(APPEND SOURCES video/drivers/ffmpeg.cpp)
# message(STATUS "ffmpeg Found and Enabled")
# endif()
删除并重新建立一个build文件夹,重复上述编译安装步骤即可。
安装ceres
ceres是同g2o一样,运用在slam前端或后端,对相机位姿和路标点进行非线性寻优的算法,特别适用于解决后端Boundle Adjustment (BA)问题。
ceres可能需要的依赖项有:
sudo apt-get install liblapack-dev libsuitesparse-dev libcxsparse3 libgflags-dev libgoogle-glog-dev libgtest-dev
tar -zxvf解压程序包,执行以下操作:
cd ceres-solver
mkdir build && cd build
cmake ..
make
sudo make install
安装gtsam
GTSAM(Georgia Tech Smoothing and Mapping)是基于因子图的C++库,可以解决slam和sfm的问题,当然它也可以解决简单或者更加复杂的估计
问题。
因子图是一种图模型,非常适合于复杂的估计问题的建模,比如SLAM或者是SFM( Structure from Motion)。贝叶斯网络属于一种无环图模型
安装gtsam之前需要确保boost是可以正常运行的。坑比较多,有需要可以参考boost官网
tar -zxvf解压gtsam程序包,执行以下操作:
cd gtsam-4.0.2
mkdir build && cd build
cmake ..
make
sudo make install
安装Sophus
Eigen库提供了集合模块,但没有提供李代数的支持。一个较好的李群和李代数的库是Sophus库,它很好的支持了SO(3),so(3),SE(3)和se(3)。Sophus库是基于Eigen基础上开发的,继承了Eigen库中的定义的各个类。因此在使用Eigen库中的类时,既可以使用Eigen命名空间,也可以使用Sophus命名空间。
Sophus依赖fmt的,所以需要先安装fmt
git clone https://github.com/fmtlib/fmt.git
cd fmt
mkdir build
cd build
cmake ..
make
sudo make install
然后才安装Sophus。这Sophus源码也需要修改一下,前往Sophus/sophus/so2.cpp
,对第30行开始的地方做如下修改:
SO2::SO2()
{
// unit_complex_.real() = 1.;
// unit_complex_.imag() = 0.;
unit_complex_.real(1.);
unit_complex_.imag(0.);
}
然后再按规矩编译安装
cd Sophus
mkdir build
cd build
cmake -Wno-dev ..
make
sudo make install
安装g2o
g2o是一个通用图优化算法库。 目前主流的SLAM研究基本都是基于图优化的
先安装一些依赖
sudo apt-get install qt5-qmake qt5-default libqglviewer-dev-qt5 libsuitesparse-dev libcxsparse3 libcholmod3 libqglviewer-dev-qt4 libqt4-dev qt4-qmake libqglviewer-headers
安装g2o
cd g2o
mkdir build
cd build
cmake ..
make
sudo make install
如果中途出现找不到GL的报错,可以替换 g2o/cmake_modules里面的 FindQGLViewer.cmake文件,能够找到 libqglviewer
# Need to find both Qt{4,5} and QGLViewer if the QQL support is to be built
FIND_PACKAGE(Qt4 COMPONENTS QtCore QtXml QtOpenGL QtGui)
IF(NOT Qt4_FOUND)
FIND_PACKAGE(Qt5 QUIET COMPONENTS Core Xml OpenGL Gui Widgets)
IF(NOT Qt4_FOUND AND NOT Qt5_FOUND)
MESSAGE("Qt{4,5} not found. Install it and set Qt{4,5}_DIR accordingly")
IF (WIN32)
MESSAGE(" In Windows, Qt5_DIR should be something like C:/Qt/5.4/msvc2013_64_opengl/lib/cmake/Qt5")
ENDIF()
ENDIF()
ENDIF()
FIND_PATH(QGLVIEWER_INCLUDE_DIR qglviewer.h
/usr/include/QGLViewer
/opt/local/include/QGLViewer
/usr/local/include/QGLViewer
/sw/include/QGLViewer
ENV QGLVIEWERROOT
)
find_library(QGLVIEWER_LIBRARY_RELEASE
NAMES qglviewer-qt4 qglviewer QGLViewer QGLViewer2 QGLViewer-qt5
PATHS /usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
ENV QGLVIEWERROOT
ENV LD_LIBRARY_PATH
ENV LIBRARY_PATH
PATH_SUFFIXES QGLViewer QGLViewer/release
)
find_library(QGLVIEWER_LIBRARY_DEBUG
NAMES dqglviewer dQGLViewer dQGLViewer2 QGLViewerd2 QGLViewer-qt5
PATHS /usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
ENV QGLVIEWERROOT
ENV LD_LIBRARY_PATH
ENV LIBRARY_PATH
PATH_SUFFIXES QGLViewer QGLViewer/release
)
if(QGLVIEWER_LIBRARY_RELEASE)
if(QGLVIEWER_LIBRARY_DEBUG)
set(QGLVIEWER_LIBRARY optimized ${QGLVIEWER_LIBRARY_RELEASE} debug ${QGLVIEWER_LIBRARY_DEBUG})
else()
set(QGLVIEWER_LIBRARY ${QGLVIEWER_LIBRARY_RELEASE})
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(QGLVIEWER DEFAULT_MSG
QGLVIEWER_INCLUDE_DIR QGLVIEWER_LIBRARY)
最后,如果你运行代码遇到诸如此类的错误:
undefined symbol: _ZN3g2o27OptimizationAlgorithmDoglegC1EPNS_15BlockSolverBaseE
那是因为库没连接好,命令行运行以下语句可以解决:
export LD_LIBRARY_PATH=./lib
二、代码基础
这里只先介绍Eigen
和Pangolin
的基础,其他部分在后续再详细展开。
参考第一篇slam三维刚体运动基础,以里面曾介绍过的数学原理为学习基础。
Eigen基本类型
示例代码中,我们会用到以下几个头文件:
#include <iostream>
// Eigen部分
#include <Eigen/Core>
// 稠密矩阵的代数运算(逆,特征值等)
#include <Eigen/Dense>
// 计时用
#include <ctime>
#define MATRIX_SIZE = 50
主函数中:申明一个矩阵,常用以下几种方式:
// Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类。它的前三个参数为:数据类型,行,列
// 声明一个2*3的float矩阵
Eigen::Matrix<float,2,3> matrix_23;
// Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix
// 例如 Vector3d 实质上是 Eigen::Matrix<double, 3, 1>,也就是三维向量
Eigen::Vector3d v_3d;
// 这是一样的写法
Eigen::Matrix<float,3,1> matrix_31;
// Matrix3d 实际上是 Eigen::Matrix<double, 3, 3>
Eigen::Matrix3d matrix_33;
// 矩阵初始化为零
matrix_33 = Eigen::Matrix3d::Zero();
// 如果不确定矩阵大小,可以使用动态大小的矩阵
Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> matrix_dynamic;
// 更简单的方式
Eigen::MatrixXd matrix_x;
数据初始化,可以直接用<<
的方式赋值或访问:
// 输入数据(初始化)
matrix_23 << 1,4,8,3,6,1;
std::cout << "matrix_23:\n" << matrix_23 << std::endl;
// 用()访问矩阵中的元素
std::cout<<"用()访问矩阵中的元素"<<std::endl;
for (int i=0; i<2; i++) {
for (int j=0; j<3; j++)
std::cout<<matrix_23(i,j)<<"\t";
std::cout<<std::endl;
}
不同向量或矩阵可能有类型上的不同,在Eigen里你不能混合两种不同类型的矩阵,这是错误示范:
Eigen::Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d;
转换类型需要用显式转换,如下:
// 矩阵和向量相乘(实际上还是矩阵和矩阵)
v_3d << 3, 2, 1;
matrix_33 << 4, 5, 6;
// Eigen里的不同类型之间需要类型转换,要用显式转换的方式,如下:
Eigen::Matrix<double,2,1> result_1 = matrix_23.cast<double>() * v_3d;
std::cout<<"result1:\n"<<result_1<<std::endl;
Eigen::Matrix<float,2,1> result_2 = matrix_23 * matrix_31;
std::cout<<"result2:\n"<<result_2<<std::endl;
基本运算举例:
// 一些简单的运算举例
Eigen::Matrix<double,3,3> Rmatrix_33;
Rmatrix_33 = Eigen::Matrix3d::Random();
std::cout << "Random Matrix:\n"<<Rmatrix_33<<std::endl;
std::cout << Rmatrix_33.transpose() << std::endl; // 转置
std::cout << Rmatrix_33.sum() << std::endl; // 各元素和
std::cout << Rmatrix_33.trace() << std::endl; // 迹
std::cout << 10*Rmatrix_33 << std::endl; // 数乘
std::cout << Rmatrix_33+Rmatrix_33 << std::endl; // 加减
std::cout << Rmatrix_33.inverse() << std::endl; // 逆
std::cout << Rmatrix_33.determinant() << std::endl; // 行列式
矩阵特征值计算:
// 特征值
// Eigen::SelfAdjointEigenSolver
// 实对称矩阵可以保证对角化成功
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver ( matrix_33.transpose()*matrix_33 );
std::cout << "Eigen values = \n" << eigen_solver.eigenvalues() << std::endl;
std::cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << std::endl;
解方程:
// 解方程
// 求解 matrix_NN * x = v_Nd 方程
// N的大小在前边的宏里定义,它由随机数生成
// 直接求逆自然是最直接的,但是求逆运算量大
Eigen::Matrix<double,MATRIX_SIZE,MATRIX_SIZE> matrix_NN;
matrix_NN = Eigen::MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
Eigen::Matrix<double,MATRIX_SIZE,1> v_Nd;
v_Nd = Eigen::MatrixXd::Random(MATRIX_SIZE,1);
求逆:
clock_t time_stt = clock(); // 计时用
// 直接求逆 inverse
Eigen::Matrix<double,MATRIX_SIZE,1> x = matrix_NN.inverse()*v_Nd;
std::cout <<"time use in normal inverse is: " << 1000*(clock()-time_stt)/(double)CLOCKS_PER_SEC << "ms"<< std::endl;
// 通常用矩阵分解来求,例如QR分解,速度会快很多
// colPivHouseholderQr
time_stt = clock();
x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
std::cout <<"time use in Qr decomposition is: " <<1000*(clock()-time_stt)/(double)CLOCKS_PER_SEC <<"ms" << std::endl;
Cmake
Cmake是(跨平台的)安装编译工具, 可以用简单的语句来描述所有平台的安装(编译过程) 。本篇只先介绍简单的cmake语法
。
既然是面向编译,就需要有程序用来编译。以下是举例的三个c++程序:
// hello.cpp
#include <iostream>
using namespace std;
int main( int argc, char** argv )
{
cout<<"Hello Pokemon!"<<endl;
return 0;
}
以及可以用库的方式:
// libHello.cpp
// 这是一个库文件
#include <iostream>
using namespace std;
void printHello()
{
cout<<"Hello SLAM"<<endl;
}
对应头文件:
// libHello.h
#ifndef LIBHELLOSLAM_H_
#define LIBHELLOSLAM_H_
// 上面的宏定义是为了防止重复引用这个头文件而引起的重定义错误
void printHello();
#endif
使用这个库文件:
// useHello.cpp
#include "libHello.h"
// 使用 libHelloSLAM.h 中的 printHello() 函数
int main( int argc, char** argv )
{
printHello();
return 0;
}
有了上述程序,现在来写CMakeLists
# 声明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )
# 声明一个 cmake 工程
project( Hello )
# 设置编译模式
set( CMAKE_BUILD_TYPE "Debug" )
# 添加一个可执行程序
# 语法:add_executable( 程序名 源代码文件 )
add_executable( hello hello.cpp )
# 添加一个库
add_library( libhello libHello.cpp )
# 共享库
add_library( libhello_shared SHARED libHello.cpp )
add_executable( useHello useHello.cpp )
# 将库文件链接到可执行程序上
target_link_libraries( useHello libhello_shared )
编译的时候,建议参考以下步骤,将源码和编译文件区分开来:
mkdir build && cd bulid
cmake ..
make
本代码生成的文件目录如下:
CMakeCache.txt CMakeFiles cmake_install.cmake hello liblibhello.a liblibhello_shared.so Makefile useHello
运行效果请自行动手验证
现在来编写Eigen代码所需的CMakeLists:
# 声明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )
# 声明一个 cmake 工程
project( Eigen_learn )
# 设置编译模式
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-O3" )
# 添加Eigen头文件
include_directories( "/usr/include/eigen3" )
# 添加一个可执行程序
# 语法:add_executable( 程序名 源代码文件 )
add_executable( Eigen_learn EigenMatrix.cpp )
其中set( CMAKE_CXX_FLAGS "-O3" )
是指定了g++,并选择了优化选项。
O0选项不进行任何优化,在这种情况下,编译器尽量的缩短编译消耗(时间,空间),此时,debug会产出和程序预期的结果。当程序运行被断点打断,此时程序内的各种声明是独立的,我们可以任意的给变量赋值,或者在函数体内把程序计数器指到其他语句,以及从源程序中 精确地获取你期待的结果.
O1优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。
O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。
O3在O2的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。
Eigen几何模块
这里直接上代码,一些要点整理在注释了:
#include <iostream>
using namespace std;
#include <Eigen/Core>
// Eigen 几何模块
#include <Eigen/Geometry>
int main(int argc,char** argv)
{
// Eigen/Geometry 模块提供了各种旋转和平移的表示
// 3D 旋转矩阵直接使用 Matrix3d 或 Matrix3f
// Identity() 初始一个单位矩阵
Eigen::Matrix3d rotation_matrix = Eigen::Matrix3d::Identity();
// 旋转向量使用 AngleAxis, 它底层不直接是Matrix,但运算可以当作矩阵(因为重载了运算符)
// 沿 Z 轴旋转 45 度
// #define M_PI 3.14159265358979323846
Eigen::AngleAxisd rotation_vector (M_PI/4, Eigen::Vector3d ( 0,0,1 ));
// cout的一个成员函数,规定小数后保留几位,如3位的写法如下:
cout.precision(3);
// 用.matrix()转换成矩阵
cout<<"rotation matrix =\n"<<rotation_vector.matrix() <<endl;
// 也可以直接赋值
rotation_matrix = rotation_vector.toRotationMatrix();
cout<<"toRotationMatrix =\n"<< rotation_matrix <<endl;
// 用 AngleAxis 可以进行坐标变换
Eigen::Vector3d v (1,0,0);
Eigen::Vector3d v_rotated = rotation_vector * v;
cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl;
// 或者用旋转矩阵
v_rotated = rotation_matrix * v;
cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl;
// 欧拉角: 可以将旋转矩阵直接转换成欧拉角
// ZYX顺序,即roll pitch yaw顺序
// pitch是围绕X轴旋转,俯仰角
// yaw 是围绕Y轴旋转,偏航角
// roll 是围绕Z轴旋转,翻滚角
Eigen::Vector3d euler_angles = rotation_matrix.eulerAngles ( 2,1,0 );
cout<<"yaw pitch roll = "<<euler_angles.transpose()<<endl;
// 欧氏变换矩阵使用 Eigen::Isometry
// 对于仿射和射影变换,使用 Eigen::Affine3d、Eigen::Projective3d
// 虽然称为3d,实质上是4*4的矩阵
Eigen::Isometry3d T = Eigen::Isometry3d::Identity();
// 按照rotation_vector进行旋转
T.rotate ( rotation_vector );
// 把平移向量设成(1,3,4)
T.pretranslate ( Eigen::Vector3d ( 1,3,4 ) );
cout << "Transform matrix = \n" << T.matrix() <<endl;
// 用变换矩阵进行坐标变换
// 相当于R*v+t
Eigen::Vector3d v_transformed = T*v;
cout<<"v tranformed = "<<v_transformed.transpose()<<endl;
// 四元数
// 可以直接把AngleAxis赋值给四元数,反之亦然
Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_vector );
// 注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
cout<<"quaternion = \n"<<q.coeffs() <<endl;
// 也可以把旋转矩阵赋给它
q = Eigen::Quaterniond ( rotation_matrix );
cout<<"quaternion = \n"<< q.coeffs() <<endl;
// 使用四元数旋转一个向量,使用重载的乘法即可
v_rotated = q*v; // 注意数学上是qvq^{-1}
cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl;
return 0;
}
对应的cmake为:
cmake_minimum_required( VERSION 2.8 )
project( Eigen_Geo_learn )
# 添加Eigen头文件
include_directories( "/usr/include/eigen3" )
add_executable( eigenGeometry EigneGeometry.cpp )
Pangolin
参考如下代码,注意看其中的注释:
#include <pangolin/pangolin.h>
int main(int argc, char** argv)
{
// 创建名称为“Main”的GUI窗口,尺寸为640×640
pangolin::CreateWindowAndBind("Main",640,480);
// 启动深度测试
glEnable(GL_DEPTH_TEST);
// 创建一个观察相机视图
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640,480,420,420,320,320,0.2,100),
pangolin::ModelViewLookAt(2,0,2, 0,0,0, pangolin::AxisY)
);
// 创建交互视图
pangolin::Handler3D handler(s_cam); //交互相机视图句柄
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f)
.SetHandler(&handler);
while(!pangolin::ShouldQuit())
{
// 清空颜色和深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
d_cam.Activate(s_cam);
// 在原点绘制一个立方体
pangolin::glDrawColouredCube();
// 绘制坐标系
glLineWidth(3);
glBegin ( GL_LINES );
glColor3f ( 0.8f,0.f,0.f );
glVertex3f( -1,-1,-1 );
glVertex3f( 0,-1,-1 );
glColor3f( 0.f,0.8f,0.f);
glVertex3f( -1,-1,-1 );
glVertex3f( -1,0,-1 );
glColor3f( 0.2f,0.2f,1.f);
glVertex3f( -1,-1,-1 );
glVertex3f( -1,-1,0 );
glEnd();
// 运行帧循环以推进窗口事件
pangolin::FinishFrame();
}
return 0;
}
所需的CMakeLists为:
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})
add_executable(HelloPangolin main.cpp)
target_link_libraries(HelloPangolin ${Pangolin_LIBRARIES})
最终效果图中,可以通过拖动画面与按动鼠标滚轮的方式实现基本的平移旋转功能
接下来参考如下代码,把物体旋转与平移的描述也展示出来了,以下是头文件部分:
#include <iostream>
#include <iomanip>
using namespace std;
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace Eigen;
#include <pangolin/pangolin.h>
为了计算旋转矩阵、选转向量、四元数,里面重载过了运算符。主要是为了便于书写与ui展示,其展示效果可以看文末的插图。为了方便,这里把重载的部分单独拿出来。
struct RotationMatrix
{
Matrix3d matrix = Matrix3d::Identity();
};
ostream& operator << ( ostream& out, const RotationMatrix& r )
{
out.setf(ios::fixed);
Matrix3d matrix = r.matrix;
out<<'=';
out<<"["<<setprecision(2)<<matrix(0,0)<<","<<matrix(0,1)<<","<<matrix(0,2)<<"],"
<< "["<<matrix(1,0)<<","<<matrix(1,1)<<","<<matrix(1,2)<<"],"
<< "["<<matrix(2,0)<<","<<matrix(2,1)<<","<<matrix(2,2)<<"]";
return out;
}
istream& operator >> (istream& in, RotationMatrix& r )
{
return in;
}
struct TranslationVector
{
Vector3d trans = Vector3d(0,0,0);
};
ostream& operator << (ostream& out, const TranslationVector& t)
{
out<<"=["<<t.trans(0)<<','<<t.trans(1)<<','<<t.trans(2)<<"]";
return out;
}
istream& operator >> ( istream& in, TranslationVector& t)
{
return in;
}
struct QuaternionDraw
{
Quaterniond q;
};
ostream& operator << (ostream& out, const QuaternionDraw quat )
{
auto c = quat.q.coeffs();
out<<"=["<<c[0]<<","<<c[1]<<","<<c[2]<<","<<c[3]<<"]";
return out;
}
istream& operator >> (istream& in, const QuaternionDraw quat)
{
return in;
}
主函数
int main ( int argc, char** argv )
{
// 创建名称为“visualize geometry”的GUI窗口,尺寸为1000×600
pangolin::CreateWindowAndBind ( "visualize geometry", 1000, 600 );
// 启动深度测试
glEnable ( GL_DEPTH_TEST );
// 创建一个观察相机视图
pangolin::OpenGlRenderState s_cam (
pangolin::ProjectionMatrix ( 1000, 600, 420, 420, 500, 300, 0.1, 1000 ),
pangolin::ModelViewLookAt ( 3,3,3,0,0,0,pangolin::AxisY )
);
const int UI_WIDTH = 500;
// 创建交互视图
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, pangolin::Attach::Pix(UI_WIDTH), 1.0, -1000.0f/600.0f)
.SetHandler(new pangolin::Handler3D(s_cam));// SetHandler,交互相机视图句柄
// ui,设置需要显示的值
pangolin::Var<RotationMatrix> rotation_matrix("ui.R", RotationMatrix());
pangolin::Var<TranslationVector> translation_vector("ui.t", TranslationVector());
pangolin::Var<TranslationVector> euler_angles("ui.rpy", TranslationVector());
pangolin::Var<QuaternionDraw> quaternion("ui.q", QuaternionDraw());
pangolin::CreatePanel("ui")
.SetBounds(0.0, 1.0, 0.0, pangolin::Attach::Pix(UI_WIDTH));
while ( !pangolin::ShouldQuit() )
{
// 清空颜色和深度缓存
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
d_cam.Activate( s_cam );
pangolin::OpenGlMatrix matrix = s_cam.GetModelViewMatrix();
Matrix<double,4,4> m = matrix;
// m = m.inverse();
RotationMatrix R;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
R.matrix(i,j) = m(j,i);
rotation_matrix = R;
TranslationVector t;
t.trans = Vector3d(m(0,3), m(1,3), m(2,3));
t.trans = -R.matrix*t.trans;
translation_vector = t;
TranslationVector euler;
euler.trans = R.matrix.transpose().eulerAngles(2,1,0);
euler_angles = euler;
QuaternionDraw quat;
quat.q = Quaterniond(R.matrix);
quaternion = quat;
glColor3f(1.0,1.0,1.0);
// 在原点绘制一个立方体
pangolin::glDrawColouredCube();
// 绘制坐标系
glLineWidth(3);
glColor3f ( 0.8f,0.f,0.f );
glBegin ( GL_LINES );
glVertex3f( 0,0,0 );
glVertex3f( 10,0,0 );
glColor3f( 0.f,0.8f,0.f);
glVertex3f( 0,0,0 );
glVertex3f( 0,10,0 );
glColor3f( 0.2f,0.2f,1.f);
glVertex3f( 0,0,0 );
glVertex3f( 0,0,10 );
glEnd();
// 运行帧循环以推进窗口事件
pangolin::FinishFrame();
}
}
对应的cmake文件
cmake_minimum_required( VERSION 2.8 )
project( visualizeGeometry )
set(CMAKE_CXX_FLAGS "-std=c++11")
# 添加Eigen头文件
include_directories( "/usr/include/eigen3" )
# 添加Pangolin依赖
find_package( Pangolin )
include_directories( ${Pangolin_INCLUDE_DIRS} )
add_executable( visualizeGeometry visualizeGeometry.cpp )
target_link_libraries( visualizeGeometry ${Pangolin_LIBRARIES} )
运行效果