• SLAM中旋转向量(旋转轴/旋转角)、旋转矩阵、四元数、李代数的相互转化(附C++ Eigen库代码实例)


    旋转轴/旋转角、旋转矩阵、四元数、李代数都可以表示旋转,那么这几者的转换是如何实现的呢?

    绕一个轴n,旋转角度\theta的旋转,例如,在三维空间中,以(0,0.7071,0.7071)为轴,旋转45°,表示为n=(0,0.7071,0.7071), \theta=\pi/4,注意,旋转轴模长要化为1,旋转角度乘旋转轴即为旋转向量

    假如有一个点P(1,2,3), 那么点P绕轴(0,0.7071,0.7071)转\pi/4,后的位置{P}'=RP,R为旋转矩阵,旋转向量到旋转矩阵的转化通过罗德里格斯公式实现

     n右上面一个小帽子表示将向量 n(n1, n2, n3) 转化为反对称矩阵(skew-symmetirc)

    n^{\wedge}=\left [ \begin{matrix} 0& -n_{3} &n_{2} \\ n_{3}& 0& -n_{1} \\ -n_{2}& n_{1}& 0 \end{matrix} \right ]

     从 旋转矩阵 转化回 旋转轴和旋转角 的方法是对旋转矩阵求迹tr(R)

     转轴n是旋转矩阵R特征值1所对应的特征向量。通过特征向量的求解方法求解旋转轴(Eigen库中有现成的轮子)

    旋转轴/旋转角到四元数的方法

     四元数到旋转矩阵的转换方法

    下面说明 旋转矩阵 到 李代数 的转化方法,介绍李代数之前,先说明李群的概念,一种集合加上一种运算,就构成了群,比如:旋转矩阵和矩阵乘法构成一个群,如果一个群,具有连续(光滑)性质,则称为李群,旋转矩阵和矩阵乘法构成一个李群。每个李群,都有一个与之对应的李代数,因此,每个旋转矩阵,都有一个与之对应的李代数。

    旋转矩阵R所属的李群用SO(3)表示,它对应的李代数用so(3)表示(应该用哥特体写,但是我不知道csdn怎么打哥特体),李代数体现为一个三维向量\phi

    \phi=\theta n,即:旋转角乘旋转轴得到的旋转向量,就是旋转矩阵R对应的李代数。

    已知李代数,转为旋转矩阵,首先将\phi转成模长为1的旋转轴和旋转角,在根据旋转轴和旋转角求旋转矩阵。

    上面说明了旋转,如果一个旋转加上平移该如何求解?(比如沿向量t(t_{1},t_{2},t_{3})进行平移)

    将旋转和平移结合,将三维坐标末尾加1,变成四维向量,成为齐次坐标

    对于一个点a,经过平移和旋转,得到点{a}'

    变换矩阵T同样属于李群,用SE(3)表示,同理,每一个T都有对应的李代数se(3),体现为一个六维向量\xi=\begin{bmatrix} \rho\\ \phi \end{bmatrix}\subseteq R^{6},式中,\phi是旋转矩阵的李代数,\rho通过平移向量t求解。

     

     (注意,求J的公式里的a是旋转轴,这里把字母n换成了a

    c++ Eigen库代码示例如下:注,Eigen库中没有李代数,再引用Sophus库

    1. #include
    2. #include
    3. #include
    4. // Eigen 几何模块
    5. #include
    6. #include
    7. // Sophus库中李群和李代数运算
    8. #include "so3.cpp"
    9. #include "se3.cpp"
    10. #define M_PI 3.1415926
    11. using namespace std;
    12. int main(int argc, char** argv){
    13. double theta = M_PI / 4; // 旋转角
    14. Eigen::Vector3d a = Eigen::Vector3d(0, 0.7071, 0.7071); // 旋转轴
    15. Eigen::AngleAxisd rotation_vector(theta, a); // 旋转向量
    16. // 旋转向量到旋转矩阵
    17. Eigen::Matrix3d R = rotation_vector.toRotationMatrix();
    18. // 旋转矩阵到旋转向量
    19. cout << "theta=" << acos((R.trace() - 1) / 2) << endl;
    20. Eigen::EigenSolver es(R);
    21. cout << "第一种:" << endl;
    22. cout << "特征值为:" << endl;
    23. cout << es.eigenvalues() << endl;
    24. cout << "特征向量为:" << endl;
    25. cout << es.eigenvectors() << endl << endl;
    26. // se(3)中J的求解
    27. Eigen::Matrix3d I = Eigen::Matrix3d::Identity();
    28. Eigen::Matrix3d J = sin(theta) / theta * I + (1 - sin(theta) / theta) * a * a.transpose() + (1 - cos(theta)) / theta * Sophus::SO3::hat(a);
    29. Eigen::Vector3d t = Eigen::Vector3d(1, 3, 4)
    30. Eigen::Vector3d rho = J.inverse() * t;
    31. cout << "rho:\n" << endl;
    32. cout << rho << endl;
    33. // 直接用Sophus库求李代数
    34. Sophus::SO3 SO3_R(R);
    35. Eigen::Vector3d so3 = SO3_R.log();
    36. // 四元数
    37. // 可以直接把AngleAxis赋值给四元数,反之亦然
    38. Eigen::AngleAxisd rotation_vector(M_PI / 4, Eigen::Vector3d(0, 0.7071, 0.7071));
    39. Eigen::Matrix3d rotation_matrix = rotation_vector.toRotationMatrix();
    40. Eigen::Quaterniond q = Eigen::Quaterniond(rotation_vector);
    41. cout << "quaternion = \n" << q.coeffs() << endl; // 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
    42. // 也可以把旋转矩阵赋给它
    43. q = Eigen::Quaterniond(rotation_matrix);
    44. cout << "quaternion = \n" << q.coeffs() << endl;
    45. // 使用四元数旋转一个向量,使用重载的乘法即可
    46. v_rotated = q * v; // 注意数学上是qvq^{-1}
    47. cout << "(1,0,0) after rotation = " << v_rotated.transpose() << endl;
    48. return 0;
    49. }

    参考文献:《视觉SLAM十四讲》——高翔,张涛

  • 相关阅读:
    CMAKE_CUDA_ARCHITECTURES set to ‘native’多版本与版本号矛盾问题,报错
    Tuskr 用例平台 快捷操作栏(基于 油猴)
    元宇宙:技术演进、产业生态与大国博弈
    MySQL复合查询
    关于前端就业前景的一点看法
    基于Javamail的邮件收发系统免费系统+LW
    软考 系统架构设计师系列知识点之软件架构风格(1)
    淘宝/天猫获得淘宝商品详情API(含测试示例)
    做个简单的音视频摄像头录像小程序,100元
    swift枚举(一)
  • 原文地址:https://blog.csdn.net/qq_41816368/article/details/127763879