文章目录
-
目录
文章目录
前言
一、VIO系统数据的获取:图像传感器选型、IMU传感器选型
二、建立linux系统+ROS环境
三、如何读取图像数据
四、如何读取IMU传感器数据
五、标定图像和IMU数据的外参、相机的内参
六、移植VINS-MONO或者VINS-FUSION:主要调试获取图像和imu的订阅数据
七、实践VIO中遇到的问题
总结
前言
分享一些自己学习+实践 VIO视觉融合惯导的定位算法系统工程化过程
一、VIO系统数据的获取:图像传感器选型、IMU传感器选型
二、建立linux系统+ROS环境
三、如何读取图像数据
四、如何读取IMU传感器数据
五、标定图像和IMU数据的外参、相机的内参
六、移植VINS-MONO或者VINS-FUSION:主要调试获取图像和imu的订阅数据
七、实践VIO中遇到的问题
1、在运行vins-mono中发现,Eigen报错,vins_estimator: /usr/include/eigen3/Eigen/src/Core/DenseStorage.h:109: Eigen::internal::plain_array<T, Size, MatrixOrArrayOptions, 16>::plain_array() [with T = double; int Size = 270; int MatrixOrArrayOptions = 0]: Assertion `(internal::UIntPtr(eigen_unaligned_array_assert_workaround_gcc47(array)) & (15)) == 0 && "this assertion is explained here: " "http://eigen.tuxfamily.org/dox-devel/group__TopicUnalignedArrayAssert.html" " **** READ THIS WEB PAGE !!! ****"' failed.
解决问题:
1)查阅Eigen官方文档找到问题所在。原因是Eigen库为了使用SSE加速,在内存上分配了128位的指针,涉及字节对齐问题,该问题在编译时不会报错,只在运行时报错。
原因1:类中有Eigen类型的成员变量
class Foo
{
//...
Eigen::Vector2d v;
//...
};
//...
Foo *foo = new Foo;
在生成定长的Matrix或Vector对象时,需要开辟内存,调用默认构造函数,通常x86下的指针是32位,内存位数没对齐就会导致程序运行出错。而对于动态变量(例如Eigen::VectorXd)会动态分配内存,因此会自动地进行内存对齐。
解决方法:在public下写一个宏EIGEN_MAKE_ALIGNED_OPERATOR_NEW
class Foo
{
...
Eigen::Vector2d v;
...
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
...
Foo *foo = new Foo;
这个宏在new一个对象时会总是返回一个对齐的指针。
如果想要通过模板参数来定义一个定长变量,可以使用宏EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)。若NeedsToAlign是True,则进行对齐运算;若NeedsToAlign为False,则采用默认的对齐方式。
template<int n> class Foo
{
typedef Eigen::Matrix<float,n,1> Vector;
enum { NeedsToAlign = (sizeof(Vector)%16)==0 };
...
Vector v;
...
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign)
};
...
Foo<4> *foo4 = new Foo<4>; // foo4 is guaranteed to be 128bit-aligned
Foo<3> *foo3 = new Foo<3>; // foo3 has only the system default alignment guarantee
如果觉得到处放EIGEN_MAKE_ALIGNED_OPERATOR_NEW太过繁琐,还可以采用以下的两种措施。
1)禁用对齐
class Foo
{
...
Eigen::Matrix<double,2,1,Eigen::DontAlign> v;
...
};
这种方法在将变量赋值到一个临时的对齐向量时,仍然有可能重新启用矢量化。
void Foo::bar()
{
Eigen::Vector2d av(v);
// use av instead of v
...
// if av changed, then do:
v = av;
}
2)私有结构体
将定长的对象存储到私有结构体中,在构建主对象时固定分配内存。这里的明显优势是类Foo在对齐问题上保持不变。缺点是无论如何都需要堆分配。
struct Foo_d
{
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
Vector2d v;
...
};
struct Foo {
Foo() { init_d(); }
~Foo() { delete d; }
void bar()
{
// use d->v instead of v
...
}
private:
void init_d() { d = new Foo_d; }
Foo_d* d;
};
原因2:STL容器中的元素是Eigen数据结构
使用Eigen::aligned_allocator告诉容器进行16字节对齐。
std::map<int, Eigen::Vector4f>
将其改成
std::map<int, Eigen::Vector4f, std::less<int>,
Eigen::aligned_allocator<std::pair<const int, Eigen::Vector4f> > >
另一种方法,使用宏EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION
#include<Eigen/StdVector>
/* ... */
EIGEN_DEFINE_STL_VECTOR_SPECIALIZATION(Matrix2d)
std::vector<Eigen::Vector2d>
原因3:通过值传递定长Eigen对象
void func(Eigen::Vector4d v);
在c++中使用值传递都是一个很糟糕的选项,意味着很多无用拷贝,这儿还会造成程序崩溃。通常采用引用传递:
void my_function(const Eigen::Vector2d& v);
原因4:编译器对栈对齐做了错误假设(例如Windows上的GCC)
void foo()
{
Eigen::Quaternionf q;
//...
}
GCC中编译器假设堆栈是16字节对齐,但在Windows中堆栈只能保证4字节对齐。当从另一个线程或另一个编译器编译的二进制文件调用函数时,可能会破坏堆栈对齐。
有三种方法可供参考:
1)局部
__attribute__((force_align_arg_pointer)) void foo()
{
Eigen::Quaternionf q;
//...
}
2)全局
-mincoming-stack-boundary=2
相当于告诉GCC堆栈需要2*2=4字节对齐
3) 全局
-mstackrealign
相当于向所有函数添加force_align_arg_pointer
如果不想要进行向量化,可以在编译时使用EIGEN_DONT_ALIGN_STATICALLY选项禁用所有16字节静态对齐,但保持16位堆对齐。或者同时使用EIGEN_DONT_VECTORIZE和EIGEN_DISABLE_UNALIGNED_ARRAY_ASSERT,这保持了16位的对齐代码,但不支持向量化。
2、ubuntu 16.4 的 ros kinetic 依赖的是boost158版本,使用167版本编译程序,就会报 or -rpath-link)
/usr/bin/ld: warning: libboost_thread.
总结
1、基础矩阵、单一性矩阵、本征矩阵为什么能计算出相机的旋转和平移;其原理是什么
2、相机旋转的时候为什么能用IMU进行测量其旋转的角度