用四元素法进行姿态解算,其步骤如下:
1. 四元素初始化(第一次解算时计算)
①静止状态下由加速度,磁力计值求取初始${\rm{roll}}\gamma $、${\rm{pitch}}\theta $、${\rm{hdg}}\psi $:
$\gamma = {\rm{ arctan}}\frac{{{\rm{ - ay}}}}{{{\rm{ - az}}}}$
$\theta = {\rm{arctan}}\frac{{{\rm{ax}}}}{{{\rm{ - ay}}}}$
$\psi = {\rm{arctan}}\frac{{{\rm{ - magy}}}}{{{\rm{magx}}}}$
②初始化四元素
$\left[ \begin{array}{l}
{{\rm{q}}_{\rm{0}}}\\
{q_1}\\
{q_2}\\
{q_3}
\end{array} \right] = \left[ \begin{array}{l}
\cos \frac{\gamma }{2}\cos \frac{\theta }{2}\cos \frac{\psi }{2} + \sin \frac{\gamma }{2}\sin \frac{\theta }{2}\sin \frac{\psi }{2}\\
\sin \frac{\gamma }{2}\cos \frac{\theta }{2}\cos \frac{\psi }{2} - \cos \frac{\gamma }{2}\sin \frac{\theta }{2}\sin \frac{\psi }{2}\\
\cos \frac{\gamma }{2}\sin \frac{\theta }{2}\cos \frac{\psi }{2} + \sin \frac{\gamma }{2}\cos \frac{\theta }{2}\sin \frac{\psi }{2}\\
\cos \frac{\gamma }{2}\cos \frac{\theta }{2}\sin \frac{\psi }{2} - \sin \frac{\gamma }{2}\sin \frac{\theta }{2}\cos \frac{\psi }{2}
\end{array} \right]$
例程:
void MargAHRSinit(float ax, float ay, float az, float mx, float my, float mz)
{
float initialRoll, initialPitch;
float cosRoll, sinRoll, cosPitch, sinPitch;
float magX, magY;
float initialHdg, cosHeading, sinHeading;
//使用加速度数据计算欧拉角 ,滚转角和俯仰角
initialRoll = atan2(-ay, -az);
initialPitch = atan2(ax, -az);
magX = 1.0f;
magY = 0.0f;
initialHdg = atan2f(-magY, magX);//解算航向角
cosRoll = cosf(initialRoll * 0.5f);
sinRoll = sinf(initialRoll * 0.5f);
cosPitch = cosf(initialPitch * 0.5f);
sinPitch = sinf(initialPitch * 0.5f);
cosHeading = cosf(initialHdg * 0.5f);
sinHeading = sinf(initialHdg * 0.5f);
q0 = cosRoll * cosPitch * cosHeading + sinRoll * sinPitch * sinHeading;
q1 = sinRoll * cosPitch * cosHeading - cosRoll * sinPitch * sinHeading;
q2 = cosRoll * sinPitch * cosHeading + sinRoll * cosPitch * sinHeading;
q3 = cosRoll * cosPitch * sinHeading - sinRoll * sinPitch * cosHeading;
}
2. 加速度归一化
$\left\{ \begin{array}{l}
norm = \sqrt {a{x^2} + a{y^2} + a{z^2}} \\
ax = \frac{{ax}}{{norm}}\\
ay = \frac{{ay}}{{norm}}\\
az = \frac{{az}}{{norm}}
\end{array} \right.$
3. 提取四元素等效余弦矩阵中的重力分量
四元素等效矩阵
$M = \left[ {\begin{array}{*{20}{c}}
{q_0^2 + q_1^2 - q_2^2 - q_3^2}&{2({q_1}{q_2} - {q_0}{q_3})}&{2({q_1}{q_3} + {q_0}{q_2})}\\
{2({q_1}{q_2} + {q_0}{q_3})}&{q_0^2 - q_1^2 + q_2^2 - q_3^2}&{2({q_2}{q_3} - {q_0}{q_1})}\\
{2({q_1}{q_3} - {q_0}{q_2})}&{2({q_2}{q_3} + {q_0}{q_1})}&{q_0^2 - q_1^2 - q_2^2 + q_3^2}
\end{array}} \right]$
重力矩阵$\left[ {\begin{array}{*{20}{c}}0&0&1\end{array}} \right]$左乘上面矩阵$M$即可提取出重力分量
$\left\{ \begin{array}{l}
vx = 2({q_1}{q_3} - {q_0}{q_2})\\
vy = 2({q_0}{q_1} + {q_2}{q_3})\\
vz = 1 - 2(q_1^2 + q_2^2) = q_0^2 - q_1^2 - q_2^2 + q_3^2
\end{array} \right.$
4. 向量叉积求取姿态误差
$\mathop a\limits^ \to \times \mathop b\limits^ \to = ||a||{\rm{ }}||b||{\rm{ sin}}\theta $
向量的叉积可以用来判断两个向量是否平行,当两个向量都为单位向量的时候,它们之间的叉积就代表了它们之间的平行度,若平行则叉积为0,若垂直则叉积为1,两向量的方向差越小,叉积也越小,因此可以用叉积来表示两归一化向量的方向误差
根据向量的叉乘规则:
若
${\rm{a}} = \left[ \begin{array}{l}{a_x}\\{a_y}\\{a_z}\end{array} \right]$,
$b = \left[ \begin{array}{l}
{b_x}\\
{b_y}\\
{b_z}
\end{array} \right]$,
$c = \left[ \begin{array}{l}
{c_x}\\
{c_y}\\
{c_z}
\end{array} \right]$;
$a = b \times c$
则
$\begin{array}{l}
{a_x} = {b_y}{c_z} - {b_z}{c_y}\\
{a_y} = {b_z}{c_x} - {b_x}{c_z}\\
{a_z} = {b_x}{c_y} - {b_y}{c_x}
\end{array}$
可得
$\begin{array}{l}
ex = vy * az - vz*ay\\
ey = vz * ax - vx*az\\
ez = vx * ay - vy*ax
\end{array}$
5. 对误差进行比例积分运算,更新陀螺仪角速度值
$\begin{array}{l}
exInt = exInt + ex * ki\\
eyInt = eyInt + ey * ki\\
ezInt = ezInt + ez * ki
\end{array}$
$\begin{array}{l}
gx = gx + ex * kp + exInt\\
gy = gy + ey * kp + eyInt\\
gz = gz + ez * kp + ezInt
\end{array}$
6.一阶龙格库塔法求解四元素
${\left( \begin{array}{l}
{q_0}\\
{q_1}\\
{q_2}\\
{q_3}
\end{array} \right)_{t + \Delta t}} = {\left( \begin{array}{l}
{q_0}\\
{q_1}\\
{q_2}\\
{q_3}
\end{array} \right)_t} + \frac{{\Delta t}}{2}\left( \begin{array}{l}
- {\omega _x}.{q_1} - {\omega _y}.{q_2} - {\omega _z}.{q_3}\\
+ {\omega _x}.{q_0} - {\omega _y}.{q_3} + {\omega _z}.{q_2}\\
+ {\omega _x}.{q_3} + {\omega _y}.{q_0} - {\omega _z}.{q_1}\\
- {\omega _x}.{q_2} + {\omega _y}.{q_1} + {\omega _z}.{q_0}
\end{array} \right)$
${\omega _x}$、${\omega _y}$、${\omega _z}$为经过数据融合后的三轴角速度,本例中即为上一步计算出的$gx$、$gy$、$gz$
因为引入了误差,使得变换出来的四元素模不再等于1,即失去了规范性,因此在下一步计算前要进行规范化
7.四元素反正切求取欧拉角
反正切解算出${\rm{roll}}\gamma $、${\rm{pitch}}\theta $、${\rm{hdg}}\psi $:
$\left\{ \begin{array}{l}
\gamma = {\rm{arctan}}\frac{{2\left( {{q_0}{q_1} + {q_2}{q_3}} \right)}}{{{q_0}{q_0} - {q_1}{q_1} - {q_2}{q_2} + {q_3}{q_3}}}\\
\theta = \arcsin \left[ {2({q_0}{q_2} - {q_1}{q_3})} \right]\\
\psi = \arctan \frac{{2\left( {{q_0}{q_3} + {q_1}{q_2}} \right)}}{{{q_0}{q_0} + {q_1}{q_1} - {q_2}{q_2} - {q_3}{q_3}}}
\end{array} \right.$
例程如下:
磁力计部分请参考磁力计四元素融合
void MargAHRSupdate(float gx, float gy, float gz,
float ax, float ay, float az,
float mx, float my, float mz,
uint8_t magDataUpdate, float dt)
{
float norm, normR;
float hx, hy, hz, bx, bz;
float vx, vy, vz, wx, wy, wz;
float q0i, q1i, q2i, q3i;
//-------------------------------------------
if ((MargAHRSinitialized == false)) // HJI && (magDataUpdate == true))
{
MargAHRSinit(ax, ay, az, mx, my, mz);
MargAHRSinitialized = true;
}
//-------------------------------------------
if (MargAHRSinitialized == true)
{
halfT = dt * 0.5f;
norm = sqrt(SQR(ax) + SQR(ay) + SQR(az));
if (norm != 0.0f)
{
calculateAccConfidence(norm);
kpAcc = eepromConfig.KpAcc * accConfidence;
kiAcc = eepromConfig.KiAcc * accConfidence;
normR = 1.0f / norm;
ax *= normR;
ay *= normR;
az *= normR;
// estimated direction of gravity (v)
vx = 2.0f * (q1q3 - q0q2);
vy = 2.0f * (q0q1 + q2q3);
vz = q0q0 - q1q1 - q2q2 + q3q3;
// error is sum of cross product between reference direction
// of fields and direction measured by sensors
exAcc = vy * az - vz * ay;
eyAcc = vz * ax - vx * az;
ezAcc = vx * ay - vy * ax;
gx += exAcc * kpAcc;
gy += eyAcc * kpAcc;
gz += ezAcc * kpAcc;
if (kiAcc > 0.0f)
{
exAccInt += exAcc * kiAcc;
eyAccInt += eyAcc * kiAcc;
ezAccInt += ezAcc * kiAcc;
gx += exAccInt;
gy += eyAccInt;
gz += ezAccInt;
}
}
//-------------------------------------------
norm = sqrt(SQR(mx) + SQR(my) + SQR(mz));
if ((magDataUpdate == true) && (norm != 0.0f))
{
normR = 1.0f / norm;
mx *= normR;
my *= normR;
mz *= normR;
// compute reference direction of flux
hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2));
hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1))
hz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2));
bx = sqrt((hx * hx) + (hy * hy));
bz = hz;
// estimated direction of flux (w)
wx = 2.0f * (bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2));
wy = 2.0f * (bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3));
wz = 2.0f * (bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2));
exMag = my * wz - mz * wy;
eyMag = mz * wx - mx * wz;
ezMag = mx * wy - my * wx;
// use un-extrapolated old values between magnetometer updates
// dubious as dT does not apply to the magnetometer calculation so
// time scaling is embedded in KpMag and KiMag
gx += exMag * eepromConfig.KpMag;
gy += eyMag * eepromConfig.KpMag;
gz += ezMag * eepromConfig.KpMag;
if (eepromConfig.KiMag > 0.0f)
{
exMagInt += exMag * eepromConfig.KiMag;
eyMagInt += eyMag * eepromConfig.KiMag;
ezMagInt += ezMag * eepromConfig.KiMag;
gx += exMagInt;
gy += eyMagInt;
gz += ezMagInt;
}
}
//-------------------------------------------
// integrate quaternion rate
q0i = (-q1 * gx - q2 * gy - q3 * gz) * halfT;
q1i = (q0 * gx + q2 * gz - q3 * gy) * halfT;
q2i = (q0 * gy - q1 * gz + q3 * gx) * halfT;
q3i = (q0 * gz + q1 * gy - q2 * gx) * halfT;
q0 += q0i;
q1 += q1i;
q2 += q2i;
q3 += q3i;
// normalise quaternion
normR = 1.0f / sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);
q0 *= normR;
q1 *= normR;
q2 *= normR;
q3 *= normR;
// auxiliary variables to reduce number of repeated operations
q0q0 = q0 * q0;
q0q1 = q0 * q1;
q0q2 = q0 * q2;
q0q3 = q0 * q3;
q1q1 = q1 * q1;
q1q2 = q1 * q2;
q1q3 = q1 * q3;
q2q2 = q2 * q2;
q2q3 = q2 * q3;
q3q3 = q3 * q3;
sensors.margAttitude500Hz[ROLL ] = atan2f(2.0f * (q0q1 + q2q3), q0q0 - q1q1 - q2q2 + q3q3);
sensors.margAttitude500Hz[PITCH] = -asinf(2.0f * (q1q3 - q0q2));
sensors.margAttitude500Hz[YAW ] = atan2f(2.0f * (q1q2 + q0q3), q0q0 + q1q1 - q2q2 - q3q3);
}
}