GeekIMU/2.Firmware/STM32/Firmware/sensor/ahrs.c

167 lines
6.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/******************** (C) COPYRIGHT 2020 GEEKIMU *******************************
* File Name : ahrs.c
* Current Version : V2.0 & ST 3.5.0
* Author : zhanli 719901725@qq.com & JustFeng.
* Date of Issued : 2019.5.26 zhanli : Create
* Comments : 加速度、陀螺仪、磁力计解算姿态
********************************************************************************/
#include "ahrs.h"
#include "sys.h"
#include "math.h"
#include "time.h"
#include "usb_lib.h"
extern uint8_t packet_buf[64];
extern int NumOfSensor;
float q0 = 1.0f;
float q1 = 0.0f;
float q2 = 0.0f;
float q3 = 0.0f;
float exInt = 0.0f;
float eyInt = 0.0f;
float ezInt = 0.0f;
#define Kp 2.5f /*加速度计和磁力计对陀螺仪的比例修正参数 */
#define Ki 0.005f /*加速度计和磁力计对陀螺仪的积分修正参数 */
#define Kd 0.0f /*缩放积分误差 */
/**----------------------------------------------------------------------
* Function : AHRSupdate
* Description : 快速计算 1/Sqrt(x)源自雷神3神奇的0x5f3759df
* Input : 加速度计、陀螺仪、磁力计数值
* Output : 四元数 & 欧拉角
* Author : zhanli&719901725@qq.com
* Date : 2016/11/5 : 根据磁力计采样率比较低的特点设置了2种模式
1、融合磁力计 2、不融合磁力计
2016/11/6 :改进了四元数更新算法,原来的四元数更新算法会
导致累计误差。
2016/11/9 : 规定了本算法运行的坐标轴,重写了四元数到欧拉
角变换的代码。
*---------------------------------------------------------------------**/
void AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az,
float mx, float my, float mz,char useMag, char UseTimer)
{
/*原算法的定义*/
float norm, halfT;
float hx, hy, hz, bz, by;
float vx, vy, vz, wx, wy, wz;
float ex, ey, ez;
float qw,qx,qy,qz;
float AccelAjust,MagAjust;
/*方便之后的程序使用,减少计算时间*/
float q0q0 = q0*q0; float q0q1 = q0*q1;
float q0q2 = q0*q2; float q0q3 = q0*q3;
float q1q1 = q1*q1; float q1q2 = q1*q2;
float q1q3 = q1*q3; float q2q2 = q2*q2;
float q2q3 = q2*q3; float q3q3 = q3*q3;
norm = invSqrt(ax*ax + ay*ay + az*az); /*把加速度计向量转换为单位向量*/
if((1.0f/norm)>1.6||(1.0f/norm)<0.4){
AccelAjust = 0;
MagAjust = 1.2f;
}else{
AccelAjust = 1.0f;
MagAjust = 1.0f;
}
ax = ax * norm;
ay = ay * norm;
az = az * norm; /*把磁力计向量转换为单位向量 */
norm = invSqrt(mx*mx + my*my + mz*mz);
mx = mx * norm;
my = my * norm;
mz = mz * norm;
hx = 2.0f*(mx*(0.5f - q2q2 - q3q3) + my*(q1q2 - q0q3) + mz*(q1q3 + q0q2)); /*把(mx,my,mz)转换到地理坐标系的(hx,hy,hz),利用H = Q^-1 * M */
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,by,0),所以我们不关注hx的数值 */
// bx = sqrtf((hx*hx) + (hy*hy)); /*使磁力计正交化*/
by = sqrtf((hx*hx) + (hy*hy));
bz = hz;
/*v代表的是把地理坐标系的加速度(0,0,1g)转换到机体坐标系的加 */
vx = 2.0f*(q1q3 - q0q2); /*速度计(ax,ay,az),其实就是用R*(0,0,1),R为旋转矩阵此矩阵可 */
vy = 2*(q0q1 + q2q3); /*由四元数转换得到 */
vz = q0q0 - q1q1 - q2q2 + q3q3;
wx = 2.0f*(by*(q1q2 + q0q3) + bz*(q1q3 - q0q2)); /*把正交化的H即B从地理坐标系转换到飞行器坐标系利用W = Q * B */
wy = 2.0f*(by*(0.5f - q1q1 - q3q3) + bz*(q0q1 + q2q3)); /*这里认为bx = 0 */
wz = 2.0f*(by*(q2q3 - q0q1) + bz*(0.5f - q1q1 - q2q2));
if(useMag != 0) /*如果使用磁力计,则融合磁力计*/
{
ex = AccelAjust*(ay*vz - az*vy) + MagAjust*(my*wz - mz*wy); /*用当前姿态向量和加速度的姿态向量做叉乘,乘积越小说明两个向量方向越相同*/
ey = AccelAjust*(az*vx - ax*vz) + MagAjust*(mz*wx - mx*wz);
ez = AccelAjust*(ax*vy - ay*vx) + MagAjust*(mx*wy - my*wx);
}else
{
ex = AccelAjust*(ay*vz - az*vy);
ey = AccelAjust*(az*vx - ax*vz);
ez = AccelAjust*(ax*vy - ay*vx);
/*因为加速度计没有校准ez不靠谱*/
}
if(UseTimer == 1) {
halfT = Get_NowTime(); /*得到每次姿态更新的周期的一半t = (1/SampleFrq) * 0.5 */
}else{
halfT = 0.1;
}
if(ex != 0.0f && ey != 0.0f && ez != 0.0f)
{
/*这里使用了PID调节的方式对角速度修正,Ki 是积分修正Kp是直接修正 */
exInt = exInt + ex*Ki * halfT; /*对误差进行积分 */
eyInt = eyInt + ey*Ki * halfT;
ezInt = ezInt + ez*Ki * halfT;
gx = gx + Kp*(1 + Kd*fabs(ex))*ex + exInt; /*使用比例和积分综合对陀螺仪进行修正,由于存在积分修正,所以才能确保回 */
gy = gy + Kp*(1 + Kd*fabs(ey))*ey + eyInt; /*到期望回到的位置 */
gz = gz + Kp*(1 + Kd*fabs(ez))*ez + ezInt;
}
// 利用一阶龙格库塔法对四元数进行更新
qw = (-q1*gx - q2*gy - q3*gz)*halfT;
qx = ( q0*gx + q2*gz - q3*gy)*halfT;
qy = ( q0*gy - q1*gz + q3*gx)*halfT;
qz = ( q0*gz + q1*gy - q2*gx)*halfT;
q0 = q0 + qw;
q1 = q1 + qx;
q2 = q2 + qy;
q3 = q3 + qz;
// 对四元数进行规范化即四元数模为1
norm = invSqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
q0 = q0 * norm; //w
q1 = q1 * norm; //x
q2 = q2 * norm; //y
q3 = q3 * norm; //z
}
/**----------------------------------------------------------------------
* Function : invSqrt
* Description : 快速计算 1/Sqrt(x)源自雷神3神奇的0x5f3759df
* Author : zhanli&719901725@qq.com
* Date : 2019/5/26 zhanli
*---------------------------------------------------------------------**/
float invSqrt(float x)
{
float halfx = 0.5f * x;
float y = x;
long i = *(long*)&y;
i = 0x5f3759df - (i>>1);
y = *(float*)&i;
y = y * (1.5f - (halfx * y * y));
return y;
}