当前位置: 首页 > news >正文

KF-GINS 和 OB-GINS 的 Earth类 和 Rotation 类

原始 Markdown文档、Visio流程图、XMind思维导图见:https://github.com/LiZhengXiao99/Navigation-Learning

文章目录

    • 一、Earth 类:地球参数和坐标转换
      • 1、gravity():正常重力计算
      • 2、meridianPrimeVerticalRadius():计算子午圈半径 RM、卯酉圈半径 RN
      • 3、RN():计算卯酉圈主半径 RN
      • 4、cne():n系(导航坐标系)到e系(地心地固坐标系)转换矩阵
      • 5、qne():计算n系(北东地)到e系(ECEF)转换四元数
      • 6、blh():从n系到e系转换四元数得到纬度和经度
      • 7、blh2ecef():大地坐标(经纬高)转地心地固坐标
      • 7、ecef2blh():地心地固坐标转大地坐标
      • 8、DRi(): 计算 n 系相对位置转大地坐标相对位置的矩阵
      • 9、DR():计算大地坐标相对位置转 n 系相对位置的矩阵
      • 10、local2global():局部坐标(在origin处展开)转大地坐标
      • 11、global2local():大地坐标转局部坐标(在origin处展开)
      • 12、iewe():地球自转角速度投影到e系
      • 13、iewn():地球自转角速度投影到n系
      • 14、enwn():n系相对于e系转动角速度投影到n系
    • 二、Rotation 类:姿态转换
      • 1、matrix2quaternion():旋转矩阵转四元数
      • 2、quaternion2matrix():四元数转旋转矩阵
      • 3、matrix2euler():旋转矩阵转欧拉角
      • 4、quaternion2euler():四元数转欧拉角
      • 5、rotvec2quaternion():等效旋转矢量转四元数
      • 6、quaternion2vector():四元数转旋转矢量
      • 7、euler2matrix():欧拉角转旋转矩阵
      • 8、euler2quaternion():欧拉角转四元数
      • 9、skewSymmetric():计算三维向量反对称阵
      • 10、quaternionleft()、quaternionright():四元数矩阵

一、Earth 类:地球参数和坐标转换

image-20230925182052936

Earth 类里都是静态函数,使用的时候直接类名::成员函数(),文件的开头定义了一些椭球参数:

/* WGS84椭球模型参数NOTE:如果使用其他椭球模型需要修改椭球参数 */
const double WGS84_WIE = 7.2921151467E-5;       /* 地球自转角速度*/
const double WGS84_F   = 0.0033528106647474805; /* 扁率 */
const double WGS84_RA  = 6378137.0000000000;    /* 长半轴a */
const double WGS84_RB  = 6356752.3142451793;    /* 短半轴b */
const double WGS84_GM0 = 398600441800000.00;    /* 地球引力常数 */
const double WGS84_E1  = 0.0066943799901413156; /* 第一偏心率平方 */
const double WGS84_E2  = 0.0067394967422764341; /* 第二偏心率平方 */

1、gravity():正常重力计算

重力是万有引力与离心力共同作用的结果,随纬度升高离心力增大但引力减小、随高程升高引力减小,共同作用下重力的计算公式如下:

g L = 9.7803267715 × ( 1 + 0.0052790414 × sin ⁡ 2 L − 0.0000232718 × sin ⁡ 2 2 L ) + h × ( 0.0000000043977311 × sin ⁡ 2 L − 0.0000030876910891 ) + 0.0000000000007211 × sin ⁡ 4 2 L g_{L}=9.7803267715 \times\left(1+0.0052790414 \times \sin ^{2} L-0.0000232718 \times \sin ^{2} 2 L\right) \\ +h\times(0.0000000043977311\times\sin ^{2} L-0.0000030876910891)+0.0000000000007211\times\sin ^{4} 2 L gL=9.7803267715×(1+0.0052790414×sin2L0.0000232718×sin22L)+h×(0.0000000043977311×sin2L0.0000030876910891)+0.0000000000007211×sin42L

static double gravity(const Vector3d &blh) {double sin2 = sin(blh[0]);sin2 *= sin2;return 9.7803267715 * (1 + 0.0052790414 * sin2 + 0.0000232718 * sin2 * sin2) +blh[2] * (0.0000000043977311 * sin2 - 0.0000030876910891) + 0.0000000000007211 * blh[2] * blh[2];
}

2、meridianPrimeVerticalRadius():计算子午圈半径 RM、卯酉圈半径 RN

返回值是 Vector2d,第一个是子午圈主曲率半径 RM、第二个是卯酉圈主半径 RN:
R M = R e ( 1 − e 2 ) ( 1 − e 2 sin ⁡ 2 L ) 3 / 2 、 R N = R e 1 − e 2 sin ⁡ 2 L R_{M}=\frac{R_{e}\left(1-e^{2}\right)}{\left(1-e^{2} \sin ^{2} L\right)^{3 / 2}}、R_{N}=\frac{R_{e}}{\sqrt{1-e^{2} \sin ^{2} L}} RM=(1e2sin2L)3/2Re(1e2)RN=1e2sin2L Re

static Eigen::Vector2d meridianPrimeVerticalRadius(double lat) {double tmp, sqrttmp;tmp = sin(lat);	tmp *= tmp;tmp     = 1 - WGS84_E1 * tmp;sqrttmp = sqrt(tmp);return {WGS84_RA * (1 - WGS84_E1) / (sqrttmp * tmp), WGS84_RA / sqrttmp};
}

3、RN():计算卯酉圈主半径 RN

R N = R e 1 − e 2 sin ⁡ 2 L R_{N}=\frac{R_{e}}{\sqrt{1-e^{2} \sin ^{2} L}} RN=1e2sin2L Re

static double RN(double lat) {double sinlat = sin(lat);return WGS84_RA / sqrt(1.0 - WGS84_E1 * sinlat * sinlat);
}

4、cne():n系(导航坐标系)到e系(地心地固坐标系)转换矩阵

C e n = [ − sin ⁡ φ 0 cos ⁡ φ 0 1 0 − cos ⁡ φ 0 − sin ⁡ φ ] [ cos ⁡ λ sin ⁡ λ 0 − sin ⁡ λ cos ⁡ λ 0 0 0 1 ] = [ − sin ⁡ φ cos ⁡ λ − sin ⁡ φ sin ⁡ λ cos ⁡ φ − sin ⁡ λ cos ⁡ λ 0 − cos ⁡ φ cos ⁡ λ − cos ⁡ φ sin ⁡ λ − sin ⁡ φ ] C_{e}^{n}=\left[\begin{array}{ccc}-\sin \varphi & 0 & \cos \varphi \\ 0 & 1 & 0 \\ -\cos \varphi & 0 & -\sin \varphi\end{array}\right]\left[\begin{array}{ccc}\cos \lambda & \sin \lambda & 0 \\ -\sin \lambda & \cos \lambda & 0 \\ 0 & 0 & 1\end{array}\right]=\\ \left[\begin{array}{ccc}-\sin \varphi \cos \lambda & -\sin \varphi \sin \lambda & \cos \varphi \\ -\sin \lambda & \cos \lambda & 0 \\ -\cos \varphi \cos \lambda & -\cos \varphi \sin \lambda & -\sin \varphi\end{array}\right] Cen= sinφ0cosφ010cosφ0sinφ cosλsinλ0sinλcosλ0001 = sinφcosλsinλcosφcosλsinφsinλcosλcosφsinλcosφ0sinφ

static Matrix3d cne(const Vector3d &blh) {double coslon, sinlon, coslat, sinlat;sinlat = sin(blh[0]);sinlon = sin(blh[1]);coslat = cos(blh[0]);coslon = cos(blh[1]);Matrix3d dcm;dcm(0, 0) = -sinlat * coslon;dcm(0, 1) = -sinlon;dcm(0, 2) = -coslat * coslon;dcm(1, 0) = -sinlat * sinlon;dcm(1, 1) = coslon;dcm(1, 2) = -coslat * sinlon;dcm(2, 0) = coslat;dcm(2, 1) = 0;dcm(2, 2) = -sinlat;return dcm;
}

5、qne():计算n系(北东地)到e系(ECEF)转换四元数

位置更新的时候,调用此函数根据上一时刻经纬度,得到上一时刻的 qne,然后 qee * qne * qnn 得到当前时刻的 qne,再调用下面的 blh() 得到经纬度。
q n e = [ cos ⁡ ( − π / 4 − φ / 2 ) cos ⁡ ( λ / 2 ) − sin ⁡ ( − π / 4 − φ / 2 ) sin ⁡ ( λ / 2 ) sin ⁡ ( − π / 4 − φ / 2 ) cos ⁡ ( λ / 2 ) cos ⁡ ( − π / 4 − sin ⁡ / 2 ) sin ⁡ ( λ / 2 ) ] ] \boldsymbol{q}_{n}^{e}=\left[\begin{array}{c}\cos (-\pi / 4-\varphi / 2) \cos (\lambda / 2) \\ -\sin (-\pi / 4-\varphi / 2) \sin (\lambda / 2) \\ \sin (-\pi / 4-\varphi / 2) \cos (\lambda / 2) \\ \cos (-\pi / 4-\sin / 2) \sin (\lambda / 2)]\end{array}\right] qne= cos(π/4φ/2)cos(λ/2)sin(π/4φ/2)sin(λ/2)sin(π/4φ/2)cos(λ/2)cos(π/4sin/2)sin(λ/2)]

/* n系(导航坐标系)到e系(地心地固坐标系)转换四元数 */
static Quaterniond qne(const Vector3d &blh) {Quaterniond quat;double coslon, sinlon, coslat, sinlat;coslon = cos(blh[1] * 0.5);sinlon = sin(blh[1] * 0.5);coslat = cos(-M_PI * 0.25 - blh[0] * 0.5);sinlat = sin(-M_PI * 0.25 - blh[0] * 0.5);quat.w() = coslat * coslon;quat.x() = -sinlat * sinlon;quat.y() = sinlat * coslon;quat.z() = coslat * sinlon;return quat;
}

6、blh():从n系到e系转换四元数得到纬度和经度

位置更新的时候,通过算当前时刻 n 系到 e 系转换四元数 qne,然后调用此函数得到经纬度。

/* 从n系到e系转换四元数得到纬度和经度 */
static Vector3d blh(const Quaterniond &qne, double height) {return {-2 * atan(qne.y() / qne.w()) - M_PI * 0.5, 2 * atan2(qne.z(), qne.w()), height};
}

7、blh2ecef():大地坐标(经纬高)转地心地固坐标

x = ( R N + h ) cos ⁡ L cos ⁡ λ y = ( R N + h ) cos ⁡ L sin ⁡ λ z = [ R N ( 1 − e 2 ) + h ] sin ⁡ L \begin{array}{l}x=\left(R_{N}+h\right) \cos L \cos \lambda \\ y=\left(R_{N}+h\right) \cos L \sin \lambda \\ z=\left[R_{N}\left(1-e^{2}\right)+h\right] \sin L\end{array} x=(RN+h)cosLcosλy=(RN+h)cosLsinλz=[RN(1e2)+h]sinL

/* 大地坐标(纬度、经度和高程)转地心地固坐标 */
static Vector3d blh2ecef(const Vector3d &blh) {double coslat, sinlat, coslon, sinlon;double rnh, rn;coslat = cos(blh[0]);sinlat = sin(blh[0]);coslon = cos(blh[1]);sinlon = sin(blh[1]);rn  = RN(blh[0]);rnh = rn + blh[2];return {rnh * coslat * coslon, rnh * coslat * sinlon, (rnh - rn * WGS84_E1) * sinlat};
}

7、ecef2blh():地心地固坐标转大地坐标

B 0 = arctan ⁡ ( Z ( 1 − e 2 ) p ) N k = a 1 − e 2 sin ⁡ 2 B k − 1 H k = p cos ⁡ B k − 1 − N k B k = arctan ⁡ ( z ( 1 − e 2 N k N k ) p ) \begin{array}{c}B_{0}=\arctan \left(\frac{Z}{\left(1-e^{2}\right) p}\right) \\ N_{k}=\frac{a}{\sqrt{1-e^{2} \sin ^{2} B_{k-1}}} \\ H_{k}=\frac{p}{\cos B_{k-1}}-N_{k} \\ B_{k}=\arctan \left(\frac{z}{\left(1-\frac{e^{2} N_{k}}{N_{k}}\right)\ p }\right)\end{array} B0=arctan((1e2)pZ)Nk=1e2sin2Bk1 aHk=cosBk1pNkBk=arctan (1Nke2Nk) pz

static Vector3d ecef2blh(const Vector3d &ecef) {double p = sqrt(ecef[0] * ecef[0] + ecef[1] * ecef[1]);double rn;double lat, lon;double h = 0, h2;// 初始状态lat = atan(ecef[2] / (p * (1.0 - WGS84_E1)));lon = 2.0 * atan2(ecef[1], ecef[0] + p);do {h2  = h;rn  = RN(lat);h   = p / cos(lat) - rn;lat = atan(ecef[2] / (p * (1.0 - WGS84_E1 * rn / (rn + h))));} while (fabs(h - h2) > 1.0e-4);return {lat, lon, h};
}

8、DRi(): 计算 n 系相对位置转大地坐标相对位置的矩阵

通过算出来的矩阵实现 ENU 和 LLH 之间的转换:

  • 捷联惯导求出的速度是 n 系的,要转成经纬度增量就得用这个矩阵。
  • 杆臂误差补偿时也需要用这个函数,因为杆臂是 n 系的,算出的 IMU 坐标和给的 GNSS 解都是经纬高。
  • 存的位置是经纬高,GNSS 量测更新时候计算的是 ENU 下位置的增量,反馈的时候也需要此矩阵。

[ δ φ δ L δ H ] = [ ( R M + H ) − 1 0 0 0 ( R N + H ) − 1 0 0 0 − 1 ] [ δ p N δ p E δ p B ] \left[\begin{array}{l}\delta \varphi \\ \delta L \\ \delta H\end{array}\right]=\left[\begin{array}{ccc}\left(R_{M}+H\right)^{-1} & 0 & 0 \\ 0 & \left(R_{N}+H\right)^{-1} & 0 \\ 0 & 0 & -1 \end{array}\right]\left[\begin{array}{l}\delta \boldsymbol{p}_{N} \\ \delta \boldsymbol{p}_{E} \\ \delta \boldsymbol{p}_{B}\end{array}\right] δφδLδH = (RM+H)1000(RN+H)10001 δpNδpEδpB

/* n系相对位置转大地坐标相对位置 */
static Matrix3d DRi(const Vector3d &blh) {Matrix3d dri = Matrix3d::Zero();Eigen::Vector2d rmn = meridianPrimeVerticalRadius(blh[0]);dri(0, 0) = 1.0 / (rmn[0] + blh[2]);dri(1, 1) = 1.0 / ((rmn[1] + blh[2]) * cos(blh[0]));dri(2, 2) = -1;return dri;
}

9、DR():计算大地坐标相对位置转 n 系相对位置的矩阵

就是上面 DRI() 计算矩阵的倒数。
[ δ φ δ L δ H ] = [ ( R M + H ) 0 0 0 ( R N + H ) 0 0 0 − 1 ] [ δ p N δ p E δ p B ] \left[\begin{array}{l}\delta \varphi \\ \delta L \\ \delta H\end{array}\right]=\left[\begin{array}{ccc}\left(R_{M}+H\right) & 0 & 0 \\ 0 & \left(R_{N}+H\right) & 0 \\ 0 & 0 & -1 \end{array}\right]\left[\begin{array}{l}\delta \boldsymbol{p}_{N} \\ \delta \boldsymbol{p}_{E} \\ \delta \boldsymbol{p}_{B}\end{array}\right] δφδLδH = (RM+H)000(RN+H)0001 δpNδpEδpB

/* 大地坐标相对位置转n系相对位置 */
static Matrix3d DR(const Vector3d &blh) {Matrix3d dr = Matrix3d::Zero();Eigen::Vector2d rmn = meridianPrimeVerticalRadius(blh[0]);dr(0, 0) = rmn[0] + blh[2];dr(1, 1) = (rmn[1] + blh[2]) * cos(blh[0]);dr(2, 2) = -1;return dr;
}

10、local2global():局部坐标(在origin处展开)转大地坐标

enwn() 中被调用,为了方便能直接传入北东地(n 系)坐标计算 n 系相对于 e 系转动角速度在 n 系的投影。

static Vector3d local2global(const Vector3d &origin, const Vector3d &local) {Vector3d ecef0 = blh2ecef(origin);Matrix3d cn0e  = cne(origin);Vector3d ecef1 = ecef0 + cn0e * local;Vector3d blh1  = ecef2blh(ecef1);return blh1;
}

11、global2local():大地坐标转局部坐标(在origin处展开)

好像整个程序中都没用到这个函数。

static Vector3d global2local(const Vector3d &origin, const Vector3d &global) {Vector3d ecef0 = blh2ecef(origin);Matrix3d cn0e  = cne(origin);Vector3d ecef1 = blh2ecef(global);return cn0e.transpose() * (ecef1 - ecef0);
}
static Pose global2local(const Vector3d &origin, const Pose &global) {Pose local;Vector3d ecef0 = blh2ecef(origin);Matrix3d cn0e  = cne(origin);Vector3d ecef1 = blh2ecef(global.t);Matrix3d cn1e  = cne(global.t);local.t = cn0e.transpose() * (ecef1 - ecef0);local.R = cn0e.transpose() * cn1e * global.R;return local;
}

12、iewe():地球自转角速度投影到e系

ω i e e = [ 0 0 ω e ] T \boldsymbol{\omega}_{i e}^{e}=\left[\begin{array}{lll}0 & 0 & \omega_{e}\end{array}\right]^{T} ωiee=[00ωe]T

static Vector3d iewe() {return {0, 0, WGS84_WIE};
}

13、iewn():地球自转角速度投影到n系

ω i e n = [ ω e cos ⁡ φ 0 − ω e sin ⁡ φ ] T \boldsymbol{\omega}_{i e}^{n}=\left[\begin{array}{lll}\omega_{e} \cos \varphi & 0 & -\omega_{e} \sin \varphi\end{array}\right]^{T} ωien=[ωecosφ0ωesinφ]T

static Vector3d iewn(double lat) {return {WGS84_WIE * cos(lat), 0, -WGS84_WIE * sin(lat)};
}

也可以直接传入北东地(n 系)坐标计算:

static Vector3d iewn(const Vector3d &origin, const Vector3d &local) {Vector3d global = local2global(origin, local);return iewn(global[0]);
}

14、enwn():n系相对于e系转动角速度投影到n系

由载体运动线速度和地球曲率引起,与东向、北向速度有关,与天向速度无关
ω e n n = [ v E R N + h − v N R M + h − v E tan ⁡ φ R N + h ] T \boldsymbol{\omega}_{e n}^{n}=\left[\begin{array}{lll}\frac{v_{E}}{R_{N}+h} & \frac{-v_{N}}{R_{M}+h} & -\frac{v_{E} \tan \varphi}{R_{N}+h}\end{array}\right]^{T} ωenn=[RN+hvERM+hvNRN+hvEtanφ]T

static Vector3d enwn(const Eigen::Vector2d &rmn, const Vector3d &blh, const Vector3d &vel) {return {vel[1] / (rmn[1] + blh[2]), -vel[0] / (rmn[0] + blh[2]), -vel[1] * tan(blh[0]) / (rmn[1] + blh[2])};
}

同样也可以直接传入北东地(n 系)坐标计算:

static Vector3d enwn(const Vector3d &origin, const Vector3d &local, const Vector3d &vel) {Vector3d global     = local2global(origin, local);Eigen::Vector2d rmn = meridianPrimeVerticalRadius(global[0]);return enwn(rmn, global, vel);
}

二、Rotation 类:姿态转换

image-20231020105459334

1、matrix2quaternion():旋转矩阵转四元数

Eigen 中的四元数可以直接传入旋转矩阵(三维矩阵)构造:

static Quaterniond matrix2quaternion(const Matrix3d &matrix) {return Quaterniond(matrix);
}

2、quaternion2matrix():四元数转旋转矩阵

四元数调用 toRotationMatrix() 函数,转为旋转矩阵:

static Matrix3d quaternion2matrix(const Quaterniond &quaternion) {return quaternion.toRotationMatrix();
}

3、matrix2euler():旋转矩阵转欧拉角

ZYX 旋转顺序,前右下的 IMU,输出 RPY:

static Vector3d matrix2euler(const Eigen::Matrix3d &dcm) {Vector3d euler;euler[1] = atan(-dcm(2, 0) / sqrt(dcm(2, 1) * dcm(2, 1) + dcm(2, 2) * dcm(2, 2)));if (dcm(2, 0) <= -0.999) {euler[0] = atan2(dcm(2, 1), dcm(2, 2));euler[2] = atan2((dcm(1, 2) - dcm(0, 1)), (dcm(0, 2) + dcm(1, 1)));} else if (dcm(2, 0) >= 0.999) {euler[0] = atan2(dcm(2, 1), dcm(2, 2));euler[2] = M_PI + atan2((dcm(1, 2) + dcm(0, 1)), (dcm(0, 2) - dcm(1, 1)));} else {euler[0] = atan2(dcm(2, 1), dcm(2, 2));euler[2] = atan2(dcm(1, 0), dcm(0, 0));}// heading 0~2PIif (euler[2] < 0) {euler[2] = M_PI * 2 + euler[2];}return euler;
}

4、quaternion2euler():四元数转欧拉角

先调用 toRotationMatrix() 转为旋转矩阵,再调用 matrix2euler() 转欧拉角:

static Vector3d quaternion2euler(const Quaterniond &quaternion) {return matrix2euler(quaternion.toRotationMatrix());
}

5、rotvec2quaternion():等效旋转矢量转四元数

根据传入的旋转矢量,计算向量的长度作为旋转的角度,计算向量的归一化版本作为旋转的轴,然后调用 AngleAxisd(),将角度和轴转换为四元数。

static Quaterniond rotvec2quaternion(const Vector3d &rotvec) {double angle = rotvec.norm();       // 计算向量的长度作为旋转的角度Vector3d vec = rotvec.normalized(); // 计算向量的归一化版本作为旋转的轴return Quaterniond(Eigen::AngleAxisd(angle, vec));  // 调用 AngleAxisd(),将角度和轴转换为四元数
}

6、quaternion2vector():四元数转旋转矢量

传入的四元数通过 Eigen::AngleAxisd 类的构造函数转换为角度轴(angle-axis)表示。角度轴是一个描述旋转的方法,其中旋转角度和旋转轴是两个独立的部分。然后,该函数返回这个角度轴表示的旋转的角度乘以旋转的轴,得到一个三维向量。这个向量的 x、y 和 z 分量分别对应于旋转轴在x、y 和 z 轴上的分量,而其长度(或者说范数)等于旋转角度。

static Vector3d quaternion2vector(const Quaterniond &quaternion) {Eigen::AngleAxisd axisd(quaternion);return axisd.angle() * axisd.axis();
}

7、euler2matrix():欧拉角转旋转矩阵

三个欧拉角分别转为 ZYX 角轴,相乘之后构造旋转矩阵

static Matrix3d euler2matrix(const Vector3d &euler) {return Matrix3d(Eigen::AngleAxisd(euler[2], Vector3d::UnitZ()) *Eigen::AngleAxisd(euler[1], Vector3d::UnitY()) *Eigen::AngleAxisd(euler[0], Vector3d::UnitX()));
}

8、euler2quaternion():欧拉角转四元数

三个欧拉角分别转为 ZYX 角轴,相乘之后构造四元数

static Quaterniond euler2quaternion(const Vector3d &euler) {return Quaterniond(Eigen::AngleAxisd(euler[2], Vector3d::UnitZ()) *Eigen::AngleAxisd(euler[1], Vector3d::UnitY()) *Eigen::AngleAxisd(euler[0], Vector3d::UnitX()));
}

9、skewSymmetric():计算三维向量反对称阵

static Matrix3d skewSymmetric(const Vector3d &vector) {Matrix3d mat;mat << 0, -vector(2), vector(1), vector(2), 0, -vector(0), -vector(1), vector(0), 0;return mat;
}

10、quaternionleft()、quaternionright():四元数矩阵

P ∘ Q = [ p 0 − p 1 − p 2 − p 3 p 1 p 0 − p 3 p 2 p 2 p 3 p 0 − p 1 p 3 − p 2 p 1 p 0 ] [ q 0 q 1 q 2 q 3 ] = M P Q = [ q 0 − q 1 − q 2 − q 3 q 1 q 0 q 3 − q 2 q 2 − q 3 q 0 q 1 q 3 q 2 − q 1 q 0 ] [ p 0 p 1 p 2 p 3 ] = M Q ′ P \boldsymbol{P} \circ \boldsymbol{Q}=\left[\begin{array}{cccc}p_{0} & -p_{1} & -p_{2} & -p_{3} \\ p_{1} & p_{0} & -p_{3} & p_{2} \\ p_{2} & p_{3} & p_{0} & -p_{1} \\ p_{3} & -p_{2} & p_{1} & p_{0}\end{array}\right]\left[\begin{array}{l}q_{0} \\ q_{1} \\ q_{2} \\ q_{3}\end{array}\right]=\boldsymbol{M}_{P} \boldsymbol{Q}=\left[\begin{array}{cccc}q_{0} & -q_{1} & -q_{2} & -q_{3} \\ q_{1} & q_{0} & q_{3} & -q_{2} \\ q_{2} & -q_{3} & q_{0} & q_{1} \\ q_{3} & q_{2} & -q_{1} & q_{0}\end{array}\right]\left[\begin{array}{l}p_{0} \\ p_{1} \\ p_{2} \\ p_{3}\end{array}\right]=\boldsymbol{M}_{Q}^{\prime} \boldsymbol{P} PQ= p0p1p2p3p1p0p3p2p2p3p0p1p3p2p1p0 q0q1q2q3 =MPQ= q0q1q2q3q1q0q3q2q2q3q0q1q3q2q1q0 p0p1p2p3 =MQP

M P = [ p 0 − p 1 − p 2 − p 3 p 1 p 0 − p 3 p 2 p 2 p 3 p 0 − p 1 p 3 − p 2 p 1 p 0 ] = [ p 0 − p v T p v p 0 I + ( p v × ) ] \boldsymbol{M}_{P}=\left[\begin{array}{cccc}p_{0} & -p_{1} & -p_{2} & -p_{3} \\ p_{1} & p_{0} & -p_{3} & p_{2} \\ p_{2} & p_{3} & p_{0} & -p_{1} \\ p_{3} & -p_{2} & p_{1} & p_{0}\end{array}\right]=\left[\begin{array}{cc}p_{0} & -\boldsymbol{p}_{v}^{\mathrm{T}} \\ \boldsymbol{p}_{v} & p_{0} \boldsymbol{I}+\left(\boldsymbol{p}_{v} \times\right)\end{array}\right] MP= p0p1p2p3p1p0p3p2p2p3p0p1p3p2p1p0 =[p0pvpvTp0I+(pv×)]

static Eigen::Matrix4d quaternionleft(const Quaterniond &q) {Eigen::Matrix4d ans;ans(0, 0)             = q.w();ans.block<1, 3>(0, 1) = -q.vec().transpose();ans.block<3, 1>(1, 0) = q.vec();ans.block<3, 3>(1, 1) = q.w() * Eigen::Matrix3d::Identity() + skewSymmetric(q.vec());return ans;
}

M Q ′ = [ q 0 − q 1 − q 2 − q 3 q 1 q 0 q 3 − q 2 q 2 − q 3 q 0 q 1 q 3 q 2 − q 1 q 0 ] = [ q 0 − q v T q v q 0 I − ( q v × ) ] \boldsymbol{M}_{Q}^{\prime}=\left[\begin{array}{cccc}q_{0} & -q_{1} & -q_{2} & -q_{3} \\ q_{1} & q_{0} & q_{3} & -q_{2} \\ q_{2} & -q_{3} & q_{0} & q_{1} \\ q_{3} & q_{2} & -q_{1} & q_{0}\end{array}\right]=\left[\begin{array}{cc}q_{0} & -\boldsymbol{q}_{v}^{\mathrm{T}} \\ \boldsymbol{q}_{v} & q_{0} \boldsymbol{I}-\left(\boldsymbol{q}_{v} \times\right)\end{array}\right] MQ= q0q1q2q3q1q0q3q2q2q3q0q1q3q2q1q0 =[q0qvqvTq0I(qv×)]

static Eigen::Matrix4d quaternionright(const Quaterniond &p) {Eigen::Matrix4d ans;ans(0, 0)             = p.w();ans.block<1, 3>(0, 1) = -p.vec().transpose();ans.block<3, 1>(1, 0) = p.vec();ans.block<3, 3>(1, 1) = p.w() * Eigen::Matrix3d::Identity() - skewSymmetric(p.vec());return ans;
}

egin{array}{cc}q_{0} & -\boldsymbol{q}{v}^{\mathrm{T}} \ \boldsymbol{q}{v} & q_{0} \boldsymbol{I}-\left(\boldsymbol{q}_{v} \times\right)\end{array}\right]
$$

static Eigen::Matrix4d quaternionright(const Quaterniond &p) {Eigen::Matrix4d ans;ans(0, 0)             = p.w();ans.block<1, 3>(0, 1) = -p.vec().transpose();ans.block<3, 1>(1, 0) = p.vec();ans.block<3, 3>(1, 1) = p.w() * Eigen::Matrix3d::Identity() - skewSymmetric(p.vec());return ans;
}

相关文章:

KF-GINS 和 OB-GINS 的 Earth类 和 Rotation 类

原始 Markdown文档、Visio流程图、XMind思维导图见&#xff1a;https://github.com/LiZhengXiao99/Navigation-Learning 文章目录 一、Earth 类&#xff1a;地球参数和坐标转换1、gravity()&#xff1a;正常重力计算2、meridianPrimeVerticalRadius()&#xff1a;计算子午圈半径…...

2017年亚太杯APMCM数学建模大赛B题喷雾轨迹规划问题求解全过程文档及程序

2017年亚太杯APMCM数学建模大赛 B题 喷雾轨迹规划问题 原题再现 喷釉工艺用喷釉枪或喷釉机在压缩空气下将釉喷入雾中&#xff0c;使釉附着在泥体上。这是陶瓷生产过程中一个容易实现自动化的过程。由于不均匀的釉料在烧制过程中会产生裂纹&#xff0c;导致工件报废&#xff0…...

柏拉图式爱情是同性之爱,绘画是理念世界的二次模仿

公元前427年&#xff0c;柏拉图出生在雅典。 柏拉图20岁成为苏格拉底的弟子。 有一次&#xff0c;柏拉图问苏格拉底&#xff1a;“什么是爱情&#xff1f;”苏格拉底说&#xff1a;“请穿越麦田&#xff0c;摘一株最大最金黄的麦穗回来。不走回头路&#xff0c;只能摘一次。”…...

【滴滴出行安全应急响应平台DSRC2倍积分卡】

1、使用方法 2、券&#xff08;记得点个关注&#xff0c;做一下数据&#xff09;...

HashMap 元素添加流程

在Java 1.8中&#xff0c;HashMap的元素添加流程&#xff1a; 计算键的哈希值&#xff1a;当调用put(key, value)方法时&#xff0c;首先会计算键&#xff08;key&#xff09;的哈希值&#xff0c;这个哈希值用来确定元素在内部数组中的位置。确定位置&#xff1a;通过哈希值&…...

甲亢_甲状腺功能亢进_Methimazole甲巯基咪唑

美国医生 Methimazole甲巯基咪唑 is used to treat hyperthyroidism, a condition where the thyroid gland produces too much thyroid hormone. It is also used before thyroid surgery or radioactive iodine treatment. Methimazole is an antithyroid medicine. It wor…...

【Maven】VSCode Java+Maven 环境配置

0x00 前言 没写过 Java&#xff0c;得配个带 Maven 的编码环境&#xff0c;不太明白&#xff0c;试试看顺便记录一下 0x01 配置过程 安装 jdk1.8 后&#xff0c;找到安装位置&#xff1a; (base) dianCD-Ali doraemon % /usr/libexec/java_home -V Matching Java Virtual Ma…...

【目标检测】非极大值抑制NMS的原理与实现

非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是目标检测中常用的一种技术&#xff0c;它的主要作用是去除冗余和重叠过高的框&#xff0c;并保留最佳的几个。 NMS计算的具体步骤如下&#xff1a; 首先根据目标检测模型输出结果&#xff0c;得…...

应用程序架构是如何演变的

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 如果您一直在开发或以某种方式参与应用程序架构&#xff0c;那么在过去的几年中您肯定看到了许多变化。有很多不同类型的架构和技术陆续出现然后消失&#xff0c;以至于…...

云原生Docker Cgroups资源控制操作

目录 资源控制 cgroups四大功能 CPU 资源控制 设置CPU使用率上限 进行CPU压力测试 设置50%的比例分配CPU使用时间上限 设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09; 设置容器绑定指定的CPU 对内存使用的限制 限制容器可以使用的最大内存 限制可用的…...

【Java集合类面试二十五】、有哪些线程安全的List?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;有哪些线程安全的List&a…...

分布式系统的链路追踪,让你轻松解决订单无法查看的问题!

你好&#xff0c;我是积极活泼的小米&#xff01;今天我要跟大家聊聊分布式系统的链路追踪&#xff0c;这个话题对于我们在技术领域工作的小伙伴们来说&#xff0c;可是非常重要的哦&#xff01; 背景 昨天&#xff0c;产品大佬丰哥找到了我&#xff0c;他抱怨说分销员的订单…...

基于生产数据测试设计、测试回归

问题背景 QA搬砖日常中&#xff0c;你会不会有这样的问题&#xff0c;测试设计时有些场景没考虑到&#xff0c;上线就因为测试中没覆盖到的场景而导致缺陷溢出。从缺陷分类统计来看&#xff0c;类似这样的例子占比是很高的。 解决措施 仅依靠测试者设置的场景&#xff0c;模拟…...

装了mac os 14.0 sonoma 在腾讯会议投屏时候,无法设置麦克风权限问题

愿意&#xff1a;界面上直接空白的&#xff0c;无法手动或自动弹出要配置授权的软件 解决思路&#xff1a; 给 TCC.db 增加1条权限记录 添加到数据库里 /usr/bin/sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db "INSERT INTO main.access (service, cli…...

ARM 汇编指令 orreq 的使用

orreq 阅读代码时&#xff0c;发现有个【组合指令】 orreq&#xff0c; orr 一般是 OR&#xff0c;也就是或操作&#xff0c;后面加个 eq 表示什么呢&#xff1f; 比如下面的代码&#xff1a;前面一个操作&#xff0c; tst&#xff0c;好像没做实际的操作&#xff0c;可能影响…...

Python---练习:for循环 求1-100的和/所有偶数的和

案例&#xff1a; 使用for循环&#xff0c;求1 ~ 100的和 之前用while循环&#xff0c;做过算出1--100的和。 相关链接&#xff1a; Python--练习&#xff1a;使用while循环求1..100的和-CSDN博客 结合着看看for循环怎么实现。 思考&#xff1a; 先把for循环的基本语法写…...

APP逆向基础(APK流程)

APK的基本结构 Android体系结构和APK基本结构-CSDN博客 APK 打包流程 【Android 安装包优化】APK 打包流程 ( 文件结构 | 打包流程 | 安装流程 | 安卓虚拟机 )_adnroid 安装包优化,打指定资源_韩曙亮的博客-CSDN博客 APK安装流程...

Tomcat调试端口被占用解决办法

Tomcat调试端口被占用解决办法 tomcat启动报错&#xff1a; Unable to open debugger port (127.0.0.1:52718): java.net.BindException "Address already in use: NET_Bind"这个错误表明Tomcat服务器在启动时尝试打开调试端口&#xff08;通常是在调试模式下使用的…...

feign之间相互通信RequestInterceptor拦截器失效

1、问题描述 前段时间碰到一个问题&#xff0c;有两个服务A和服务B&#xff0c;通过feign进行通信。因为feign之间无法直接传递请求头&#xff0c;只能传请求体。因此需要借助RequestInterceptor拦截器获取到请求头。服务B中设置了RequestInterceptor拦截器&#xff0c;但是在A…...

GoLong的学习之路(十)语法之函数

书接上回&#xff0c;上回书说到&#xff0c;结构体&#xff0c;一言之重在于体。一体之重在于经。经之重甚于骨。这张就说go的经络—函数。 文章目录 函数函数如何定义参数可变参数 返回值多返回值 函数类型与变量 高阶函数函数作为参数函数作为返回值匿名函数闭包defer语句底…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...

CTF show 数学不及格

拿到题目先查一下壳&#xff0c;看一下信息 发现是一个ELF文件&#xff0c;64位的 ​ 用IDA Pro 64 打开这个文件 ​ 然后点击F5进行伪代码转换 可以看到有五个if判断&#xff0c;第一个argc ! 5这个判断并没有起太大作用&#xff0c;主要是下面四个if判断 ​ 根据题目…...