欧拉角的旋转顺规
欧拉角的旋转顺序不能随意改变,要注意描述时所参考的坐标系。
本质上是因为:线性变换不满足乘法交换律。
右手坐标系的任意一个旋转矩阵均属于 ,即 阶特殊正交群(如何通俗地解释李群和李代数的关系? )。将绕 x,y,z 轴的旋转分别用 阶矩阵表示为:
在描述旋转矩阵一般会用到固定坐标系 [公式] 和活动坐标系 [公式] 。固定坐标系在物体旋转的过程中保持不变,而活动坐标系则始终跟随着物体进行旋转或平移。
对于固定坐标系
图 1 中表示的两种旋转变换过程是相对于活动坐标系 而言的,其可以描述为:上一行:先绕 的 轴旋转 弧度,然后绕 的 轴旋转 弧度;下一行:先绕 的 轴旋转 弧度,然后绕 的 轴旋转 弧度;仅仅是改变了欧拉角的旋转顺序,但可以看到,最终的结果却是不一样的。
对于图 1 的上一行变换,有:
对于图 1 的下一行变换,有:
对于固定坐标系
如果是相对于固定坐标系 [公式] ,其矩阵运算的顺序与相对于活动坐标系正好相反。为了对比,这里换个例子,如下图所示的魔方旋转:
其可以描述为:
第一个魔方:先绕 [公式] 的 [公式] 轴旋转 [公式] 弧度,然后绕 [公式] 的 [公式] 轴旋转 [公式] 弧度;第二个魔方:先绕 [公式] 的 [公式] 轴旋转 [公式] 弧度,然后绕 [公式] 的 [公式] 轴旋转 [公式] 弧度;
对于第一个魔方,有:
对于第二个魔方,有:
对比可以发现和前面的相对活动坐标系的结果相反。
slam里常用rpy的旋转顺规,重点看参考坐标系是活动坐标系还是固定坐标系。
左乘: 坐标系不动,点动,则左乘。【若绕静坐标系(世界坐标系)旋转,则左乘,也是变换矩阵乘坐标矩阵;】
右乘: 点不动,坐标系动,则右乘。【若是绕动坐标系旋转(自身建立一个坐标系),则右乘,也就是坐标矩阵乘变换矩阵】
示例代码(loam)
void TransformToStartIMU(pcl::PointXYZHSV *p)
{
/********************************************************************************
Ry*Rx*Rz*Pl, transform point to the global frame,世界坐标系
*********************************************************************************/
// 在使用IMU的时候,laser的坐标系需要转换到全局坐标系的。旋转顺规ZXY
//绕z轴旋转(imuRollCur)
float x1 = cos(imuRollCur) * p->x - sin(imuRollCur) * p->y;
float y1 = sin(imuRollCur) * p->x + cos(imuRollCur) * p->y;
float z1 = p->z;
//绕x轴旋转(imuPitchCur)
float x2 = x1;
float y2 = cos(imuPitchCur) * y1 - sin(imuPitchCur) * z1;
float z2 = sin(imuPitchCur) * y1 + cos(imuPitchCur) * z1;
//绕y轴旋转(imuYawCur)
float x3 = cos(imuYawCur) * x2 + sin(imuYawCur) * z2;
float y3 = y2;
float z3 = -sin(imuYawCur) * x2 + cos(imuYawCur) * z2;
/********************************************************************************
Rz(pitch).inverse * Rx(pitch).inverse * Ry(yaw).inverse * Pg
transfrom global points to the local frame 当前Sweep第一帧时候IMU坐标系
*********************************************************************************/
//绕y轴旋转(-imuYawStart)
float x4 = cos(imuYawStart) * x3 - sin(imuYawStart) * z3;
float y4 = y3;
float z4 = sin(imuYawStart) * x3 + cos(imuYawStart) * z3;
//绕x轴旋转(-imuPitchStart)
float x5 = x4;
float y5 = cos(imuPitchStart) * y4 + sin(imuPitchStart) * z4;
float z5 = -sin(imuPitchStart) * y4 + cos(imuPitchStart) * z4;
//绕z轴旋转(-imuRollStart),然后叠加平移量
p->x = cos(imuRollStart) * x5 + sin(imuRollStart) * y5 + imuShiftFromStartXCur;
p->y = -sin(imuRollStart) * x5 + cos(imuRollStart) * y5 + imuShiftFromStartYCur;
p->z = z5 + imuShiftFromStartZCur;
}