【自动驾驶】欧拉角和旋转矩阵之间的转换

欧拉角和旋转矩阵之间的转换

在使用Eigen时,经常会遇到旋转矩阵,旋转向量,四元数,欧拉角之间的两两相互转换。这里最常见、最容易出错的是欧拉角和旋转矩阵之间的相互转换。下面就欧拉角和旋转矩阵之间的转换进行详细分析。

在这里插入图片描述
图1 旋转顺序Z-Y-X,正方向内旋

1. 欧拉角

(1)欧拉角的叫法:
  • 欧拉角的叫法不固定,跟坐标轴的定义强相关。
  • 在图1中,假设 X X X是车头, Y Y Y是车左方, Z Z Z是车上方,那么绕X轴旋转得到的是 r o l l roll roll,绕 Y Y Y 轴旋转得到的是 p i t c h pitch pitch,绕 Z Z Z轴得到的是 y a w yaw yaw
  • 在图1中,假设 Y Y Y是车头, X X X是车右方, Z Z Z是车上方,那么绕X轴旋转得到的是 p i t c h pitch pitch,绕 Y Y Y 轴旋转得到的是 r o l l roll roll,绕 Z Z Z轴得到的是 y a w yaw yaw
(2)欧拉角正负:
  • 如果是右手系,旋转轴正方向面对观察者时,逆时针方向的旋转是正、顺时针方向的旋转是负。
  • 亦可这样描述:使用右手的大拇指指向旋转轴正方向,其他4个手指在握拳过程中的指向便是正方向。
  • 如图1中的三次旋转都是正向旋转。
(3)欧拉角的范围:
  • 这个要具体问题具体对待。
  • 假如是车体坐标系( x x x-前, y y y-左, z z z-上),那么 r o l l roll roll p i t c h pitch pitch 应该定义在(-90°,+90°), y a w yaw yaw 应该定义在(-180°,+180°)。
  • 假如是飞机坐标系,那么 r o l l roll roll p i t c h pitch pitch y a w yaw yaw 都应该定义在(-180°,+180°)。
  • Eigen中的默认范围 r o l l roll roll p i t c h pitch pitch y a w yaw yaw都是(-180°,+180°)。
(4)明确旋转顺序和旋转轴:
  • 对于x,y,z三个轴的不同旋转顺序一共有( x − y − z , y − z − x , z − x − y , x − z − y , z − y − x , y − x − z x-y-z, y-z-x,z-x-y,x-z-y, z-y-x,y-x-z xyz,yzx,zxy,xzy,zyx,yxz)六种组合,在旋转相同的角度的情况下不同的旋转顺序得到的姿态是不一样的。
  • 比如,先绕 x x x轴旋转 α \alpha α,再绕 y y y轴旋转 β \beta β;先绕 y y y轴旋转 β \beta β,再绕 x x x轴旋转 α \alpha α。这两种顺序得到的姿态是不一样的。
(5)内旋和外旋:
  • 每次旋转是绕固定轴(一个固定参考系,比如世界坐标系)旋转,称为外旋
  • 每次旋转是绕自身旋转之后的轴旋转,称为内旋
  • 下图说明了内旋外旋的区别。

在这里插入图片描述

2. 旋转矩阵

假设绕 X 、 Y 、 Z X、Y、Z XYZ三个轴旋转的角度分别为 α 、 β 、 γ \alpha、\beta、\gamma αβγ,则三次旋转的旋转矩阵计算方法如下:

[公式]

[公式]

[公式]

按照内旋方式,Z-Y-X旋转顺序(指先绕自身轴Z,再绕自身轴Y,最后绕自身轴X),可得旋转矩阵(内旋是右乘):

[公式]

按照外旋方式,X-Y-Z旋转顺序(指先绕固定轴X,再绕固定轴Y,最后绕固定轴Z),可得旋转矩阵(外旋是左乘):

[公式]

故R1=R2,具体不在此证明,记住即可。这个结论说明ZYX顺序的内旋等价于XYZ顺序的外旋。

slam十四讲中提到的常用旋转顺序是Z-Y-X,对应rpy,指的就是内旋(绕自身轴)Z-Y-X顺序。而欧拉角转换成旋转矩阵(相对于世界坐标系的旋转矩阵)通常是按外旋方式(绕固定轴),即X-Y-Z顺序,所以旋转矩阵为:

[公式]

3. 欧拉角和旋转矩阵之间的转换程序示例

下面程序分别使用Eigen和自定义函数测试欧拉角和旋转矩阵之间的转换:

#include <iostream>
#include <Eigen/Core>
#include <Eigen/Geometry>

using namespace std;

Eigen::Matrix3d eulerAnglesToRotationMatrix(Eigen::Vector3d &theta);
bool isRotationMatirx(Eigen::Matrix3d R);
Eigen::Vector3d rotationMatrixToEulerAngles(Eigen::Matrix3d &R);

const double ARC_TO_DEG = 57.29577951308238;
const double DEG_TO_ARC = 0.0174532925199433;

int main()
{
    // 设定车体欧拉角(角度),绕固定轴
    double roll_deg = 0.5;      // 绕X轴
    double pitch_deg = 0.8;     // 绕Y轴
    double yaw_deg = 108.5;     // 绕Z轴

    // 转化为弧度
    double roll_arc = roll_deg * DEG_TO_ARC;    // 绕X轴
    double pitch_arc = pitch_deg * DEG_TO_ARC;  // 绕Y轴
    double yaw_arc = yaw_deg * DEG_TO_ARC;      // 绕Z轴

    cout << endl;
    cout << "roll_arc = " << roll_arc << endl;
    cout << "pitch_arc = " << pitch_arc << endl;
    cout << "yaw_arc = " << yaw_arc << endl;

    // 初始化欧拉角(rpy),对应绕x轴,绕y轴,绕z轴的旋转角度
    Eigen::Vector3d euler_angle(roll_arc, pitch_arc, yaw_arc);

    // 使用Eigen库将欧拉角转换为旋转矩阵
    Eigen::Matrix3d rotation_matrix1, rotation_matrix2;
    rotation_matrix1 = Eigen::AngleAxisd(euler_angle[2], Eigen::Vector3d::UnitZ()) *
                       Eigen::AngleAxisd(euler_angle[1], Eigen::Vector3d::UnitY()) *
                       Eigen::AngleAxisd(euler_angle[0], Eigen::Vector3d::UnitX());
    cout << "\nrotation matrix1 =\n" << rotation_matrix1 << endl << endl;

    // 使用自定义函数将欧拉角转换为旋转矩阵
    rotation_matrix2 = eulerAnglesToRotationMatrix(euler_angle);
    cout << "rotation matrix2 =\n" << rotation_matrix2 << endl << endl;

    // 使用Eigen将旋转矩阵转换为欧拉角
    Eigen::Vector3d eulerAngle1 = rotation_matrix1.eulerAngles(2,1,0); // ZYX顺序,yaw,pitch,roll
    cout << "roll_1 pitch_1 yaw_1 = " << eulerAngle1[2] << " " << eulerAngle1[1] 
         << " " << eulerAngle1[0] << endl << endl;

    // 使用自定义函数将旋转矩阵转换为欧拉角
    Eigen::Vector3d eulerAngle2 = rotationMatrixToEulerAngles(rotation_matrix1); // roll,pitch,yaw
    cout << "roll_2 pitch_2 yaw_2 = " << eulerAngle2[0] << " " << eulerAngle2[1] 
         << " " << eulerAngle2[2] << endl << endl;

    return 0;
}

Eigen::Matrix3d eulerAnglesToRotationMatrix(Eigen::Vector3d &theta)
{
    Eigen::Matrix3d R_x;    // 计算旋转矩阵的X分量
    R_x <<
            1,              0,               0,
            0,  cos(theta[0]),  -sin(theta[0]),
            0,  sin(theta[0]),   cos(theta[0]);

    Eigen::Matrix3d R_y;    // 计算旋转矩阵的Y分量
    R_y <<
            cos(theta[1]),   0, sin(theta[1]),
            0,   1,             0,
            -sin(theta[1]),  0, cos(theta[1]);

    Eigen::Matrix3d R_z;    // 计算旋转矩阵的Z分量
    R_z <<
            cos(theta[2]), -sin(theta[2]), 0,
            sin(theta[2]),  cos(theta[2]), 0,
            0,              0,             1;
    Eigen::Matrix3d R = R_z * R_y * R_x;
    return R;
}


bool isRotationMatirx(Eigen::Matrix3d R)
{
    double err=1e-6;
    Eigen::Matrix3d shouldIdenity;
    shouldIdenity=R*R.transpose();
    Eigen::Matrix3d I=Eigen::Matrix3d::Identity();
    return (shouldIdenity - I).norm() < err;
}

Eigen::Vector3d rotationMatrixToEulerAngles(Eigen::Matrix3d &R)
{
    assert(isRotationMatirx(R));
    double sy = sqrt(R(0,0) * R(0,0) + R(1,0) * R(1,0));
    bool singular = sy < 1e-6;
    double x, y, z;
    if (!singular)
    {
        x = atan2( R(2,1), R(2,2));
        y = atan2(-R(2,0), sy);
        z = atan2( R(1,0), R(0,0));
    }
    else
    {
        x = atan2(-R(1,2), R(1,1));
        y = atan2(-R(2,0), sy);
        z = 0;
    }
    return {x, y, z};
}

程序输出如下:
在这里插入图片描述

4. 总结

欧拉角和旋转矩阵之间的转换要注意的细节很多,在使用时我们一定要明确欧拉角的旋转顺序、内旋还是外旋、正向旋转还是反向旋转以及欧拉角范围。另外,文章中如果有错误的地方,还请大佬不要喷,在评论区指出来,我核实后会修改的,谢谢。

  • 15
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
欧拉角旋转矩阵的ZYX顺序表示先绕Z轴旋、再绕Y轴旋、最后绕X轴旋。下面是一个示例代码,可以实现欧拉角旋转矩阵的ZYX顺序: ```c #include <stdio.h> #include <math.h> typedef struct { float m[3][3]; } Matrix3x3; void euler_to_matrix_zyx(float roll, float pitch, float yaw, Matrix3x3* matrix) { float cy = cos(yaw); float sy = sin(yaw); float cp = cos(pitch); float sp = sin(pitch); float cr = cos(roll); float sr = sin(roll); matrix->m[0][0] = cy * cp; matrix->m[0][1] = cy * sp * sr - sy * cr; matrix->m[0][2] = cy * sp * cr + sy * sr; matrix->m[1][0] = sy * cp; matrix->m[1][1] = sy * sp * sr + cy * cr; matrix->m[1][2] = sy * sp * cr - cy * sr; matrix->m[2][0] = -sp; matrix->m[2][1] = cp * sr; matrix->m[2][2] = cp * cr; } int main() { float roll = 0.1; // 横滚角 float pitch = 0.2; // 俯仰角 float yaw = 0.3; // 偏航角 Matrix3x3 matrix; euler_to_matrix_zyx(roll, pitch, yaw, &matrix); printf("Rotation Matrix (ZYX):\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { printf("%.4f\t", matrix.m[i][j]); } printf("\n"); } return 0; } ``` 在示例代码中,定义了一个Matrix3x3结构体用于存储旋转矩阵。然后,使用`euler_to_matrix_zyx`函数来实现欧拉角旋转矩阵转换。最后,在main函数中给定欧拉角的值,将其转换旋转矩阵,并打印输出结果。 请注意,示例中的欧拉角单位是弧度。如果需要使用角度作为输入,可以在转换前将角度转换为弧度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值