GeekIMU/4.Software/GeekIMUDriver 1.1/Src/SensorImpl.cpp

595 lines
15 KiB
C++
Raw 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 2023 Geek************************************
* File Name : SensorImpl.cpp
* Current Version : V1.0
* Author : zhanli
* Date of Issued : 2023.09.10
* Comments : GeekIMU 传感器实现模块
********************************************************************************/
/* Header File Including -----------------------------------------------------*/
#include "windows.h"
#include "SensorImpl.h"
#include "stdio.h"
#include "quaternion.h"
#include "math.h"
#include "UsbHid_CAPI.h"
#include "MyCls.h"
extern "C" FILE* fpdata;
Quaternion GlobalQ;
BOOL GlobalQ_Updating;
float LocX,LocY,LocZ;
float Yaw,Pitch,Roll;
extern FILE* Getfpdata();
extern int global_calibration_flag;
extern int global_Show;
UINT8 global_key[4] = { 0 };
float gxc=0, gyc=0, gzc=0;
int gyro_cali_count = 0;
#define GYRO_CALIBRATION_SAMPLE_NUMBER 3000
float gx0=0, gy0=0, gz0=0;
BOOL GlobalMag_Updating = false;
Vector3 GlobalMag = {0};
static bool HmdStandByState;
Quaternion CQ_DM = { 1, 0, 0, 0 };
bool DM_Corrected = false;
void MagFilter(float *mx,float *my,float *mz,float sita);
void GyroComputeDrift(float *gx, float *gy, float *gz);
void LowPassFilterYaw(float *z, float alpha);
bool IsHmdIntoStandBy(float *Pitch, float *Roll, float dTime);
/**----------------------------------------------------------------------
* Function : UnpackSensor
* Description : Oculus 数据解包
* Date : 2023/09/10 logzhan : 重新整理代码
*---------------------------------------------------------------------**/
void UnpackSensor(const UCHAR* buffer, long* x, long* y, long* z)
{
struct {INT32 x:21;} s;
*x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3);
*y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) |
((buffer[5] & 0xC0) >> 6);
*z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1);
}
Vector3 GetMagData()
{
Vector3 v = { 0 };
static Vector3 VLast = { 0 };
if (!GlobalMag_Updating)
{
VLast = GlobalMag;
}
v = VLast;
return v;
}
void GetControllerSensor(unsigned char usb_input_buffer[])
{
static UINT8 updatecnt = 0;
INT16 MagX,MagY,MagZ;
long GX,GY,GZ;
long AX,AY,AZ;
UINT NowTime;
float ax, ay, az, gx, gy, gz, mx, my, mz, mx1, my1, mz1;;
static float mxlast=0,mylast=0,mzlast=0;
static int Dtime = 0;
static UINT LastTime = 0;
float q0,q1,q2,q3;
NowTime = *(UINT16* )(usb_input_buffer+2);
if(LastTime != 0)
{
if(NowTime >= LastTime)
{
Dtime = NowTime - LastTime;
LastTime = NowTime;
}else
{
Dtime = (65535 - LastTime) + NowTime;
LastTime = NowTime;
}
}else
{
Dtime = 1;
LastTime = NowTime;
}
if (global_Show==5)
{
//clrscr();
back2zero();
printf("Hex数据\n\n");
for (int i = 0; i < 64; i++)
{
if ((i%16)==0)
{
printf("\n");
}
//if (global_debug)
printf("%2x ", usb_input_buffer[i]);
}
printf("\n");
}
MagX = *(INT16 *)(usb_input_buffer + 56);
MagY = *(INT16 *)(usb_input_buffer + 58);
MagZ = *(INT16 *)(usb_input_buffer + 60);
UnpackSensor(usb_input_buffer+8, &AX,&AY,&AZ);
UnpackSensor(usb_input_buffer+16, &GX,&GY,&GZ);
ax = (float)0.0001*AX;
ay = (float)0.0001*AY;
az = (float)0.0001*AZ;
gx = (float)0.0001*GX;
gy = (float)0.0001*GY;
gz = (float)0.0001*GZ;
printf("%f %f %f\n", ax, ay, az);
float x = 16.4 * 180 / 3.14159265358979323846F;
if (global_Show == 1)
{
printf("gx = %f,gy = %f gz =%f\n", gx*x, gy*x, gz*x);
}
GyroComputeDrift(&gx, &gy, &gz);
if (global_calibration_flag == 1)
{
if (gyro_cali_count > GYRO_CALIBRATION_SAMPLE_NUMBER)
{
gyro_cali_count = 0;
gxc = gyc = gzc = 0;
}
gyro_cali_count++;
gxc += gx*x;
gyc += gy*x;
gzc += gz*x;
if (gyro_cali_count == GYRO_CALIBRATION_SAMPLE_NUMBER)
{
gx0 = gxc / gyro_cali_count;
gy0 = gyc / gyro_cali_count;
gz0 = gzc / gyro_cali_count;
gyro_cali_count++;
global_calibration_flag = 0;
printf("%f %f %f\n", gx0, gy0, gz0);
}
}
global_key[0] = usb_input_buffer[24];
global_key[3] = usb_input_buffer[27];
mx = (float)MagX / 16;
my = (float)MagY / 16;
mz = (float)MagZ / 16;
if (global_calibration_flag==3)
{
GlobalMag_Updating = true;
mx = (float)MagX;
my = (float)MagY;
mz = (float)MagZ;
GlobalMag.x = (float)MagX;
GlobalMag.y = (float)MagY;
GlobalMag.z = (float)MagZ;
GlobalMag_Updating = false;
}
if(mxlast == 0&&mylast == 0&&mzlast == 0)
{
mxlast = mx;
mylast = my;
mzlast = mz;
MagFilter(&mx, &my, &mz, 0.95f);
for (int i = 0; i < 200; i++)
AHRSupdate(0, 0, 0, az, ax, ay, mz, mx, my, 1, Dtime * 400);
}
else
{
updatecnt++;
if (updatecnt > 10)
updatecnt = 0;
if (mx == mxlast&&my == my&&mz == mzlast && updatecnt < 10)
{
AHRSupdate(gz,gx,gy,az,ax,ay,mz,mx,my,1,Dtime);
}
else
{
mxlast = mx;
mylast = my;
mzlast = mz;
MagFilter(&mx,&my,&mz,0.95f);
AHRSupdate(gz,gx,gy,az,ax,ay,mz,mx,my,1,Dtime);
}
}
}
/**********************************************************************************************************
* Function Name : AHRSupdate
* 输入 : 三轴加速度计、陀螺仪、磁力计的值
* 描述 :加速度计、陀螺仪、磁力计融合得到的欧拉角
1这个算法基于标准笛卡尔右手坐标系z轴朝上x轴朝人y轴朝右
2传感器的方向需要变换到和改算法定义的坐标轴相同。
* 修改 2016/11/5 : 根据磁力计采样率比较低的特点设置了2种模式1、融合磁力计 2、不融合磁力计
2016/11/6 :改进了四元数更新算法,原来的四元数更新算法会导致累计误差。
2016/11/9 : 规定了本算法运行的坐标轴,重写了四元数到欧拉角变换的代码。
***********************************************************************************************************/
float q0 = 1, q1 = 0, q2 = 0, q3 = 0;
float exInt = 0, eyInt = 0, ezInt = 0; /*缩放积分误差 */
#define Kp 2.5f /*加速度计和磁力计对陀螺仪的比例修正参数 */
#define Ki 0.005f /*加速度计和磁力计对陀螺仪的积分修正参数 */
#define Kd 0.0f
static int AHRS_Init = 0;
void ResetAHRS()
{
AHRS_Init = 0; q0 = 1; q1 = 0; q2 = 0; q3 = 0;
}
void AHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, char useMag, int Dtime)
{
/*原算法的定义*/
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;
if (AHRS_Init > 200)
{
halfT = 0.0005f*Dtime; /*得到每次姿态更新的周期的一半t = (1/SampleFrq) * 0.5 */
}
else
{
if (useMag == 1)
{
halfT = 0.1;
gx = gy = gz = 0;
AHRS_Init++;
}
else
{
return;
}
}
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不靠谱*/
ez = AccelAjust*(ax*vy - ay*vx);
ez = 0;
}
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*fabsf(ex))*ex + exInt; /*使用比例和积分综合对陀螺仪进行修正,由于存在积分修正,所以才能确保回 */
gy = gy + Kp*(1 + Kd*fabsf(ey))*ey + eyInt; /*到期望回到的位置 */
gz = gz + Kp*(1 + Kd*fabsf(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;
norm = invSqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3); /*对四元数进行规范化即四元数模为1*/
q0 = q0 * norm; //w
q1 = q1 * norm; //x
q2 = q2 * norm; //y
q3 = q3 * norm; //z
/*对原变换公式进行重写避免了以前坐标轴混乱的问题按照标准y上z右x前*/
Pitch = asin(-2.0f*(q2*q1 - q0*q3))* (180.0f / 3.141592f); /*轴进行四元数到欧拉角的转换 */
Yaw = atan2(q3*q1 + q0*q2, 0.5f - q3*q3 - q2*q2)* (180.0f / 3.141592f);
Roll = atan2(q3*q2 + q0*q1, 0.5f - q3*q3 - q1*q1)* (180.0f / 3.141592f);
LowPassFilterYaw(&Yaw, 0.9f);
if (AHRS_Init > 200)
{
GlobalQ_Updating = TRUE;
GlobalQ.w = q0;
GlobalQ.x = q2;
GlobalQ.y = q3;
GlobalQ.z = q1;
GlobalQ_Updating = FALSE;
}
printf("%f %f %f\n", Yaw, Roll, Pitch);
IsHmdIntoStandBy(&Pitch, &Roll, 1.0f);
}
void MagFilter(float *mx, float *my, float *mz, float sita)
{
static float xlast = 0, ylast = 0, zlast = 0;
if (xlast == 0 && ylast == 0 && zlast == 0)
{
xlast = *mx;
ylast = *my;
zlast = *mz;
}
else
{
xlast = sita*xlast + (1 - sita)*(*mx);
ylast = sita*ylast + (1 - sita)*(*my);
zlast = sita*zlast + (1 - sita)*(*mz);
}
}
/*******************************************************************************
* Function Name : invSqrt
* Description : 快速计算 1/Sqrt(x)源自雷神3神奇的0x5f3759df
* 输入 : x
* 输出 1/sqrt(x)
*******************************************************************************/
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;
}
void GyroComputeDrift(float *gx, float *gy, float *gz)
{
static float GxOffect = 0.0f, GyOffect = 0.0f, GzOffect = 0.0f;
static float GxOffectTmp = 0.0f, GyOffectTmp = 0.0f, GzOffectTmp = 0.0f;
static float GxTmp = 0.0f, GyTmp = 0.0f, GzTmp = 0.0f;
static int count = 0;
*gx = *gx - GxOffect;
*gy = *gy - GyOffect;
*gz = *gz - GzOffect;
if (HmdStandByState == true){
GxTmp = GxTmp + *gx;
GyTmp = GyTmp + *gy;
GzTmp = GzTmp + *gz;
count++;
if (count == 3000){
//printf("陀螺仪校准完成\n");
GxOffectTmp = GxTmp / count;
GyOffectTmp = GyTmp / count;
GzOffectTmp = GzTmp / count;
GxOffect = GxOffect + GxOffectTmp;
GyOffect = GyOffect + GyOffectTmp;
GzOffect = GzOffect + GzOffectTmp;
GxTmp = 0.0f;
GyTmp = 0.0f;
GzTmp = 0.0f;
count = 0;
}
}
else{
count = 0;
GxTmp = 0.0f;
GyTmp = 0.0f;
GzTmp = 0.0f;
}
}
void LowPassFilterYaw(float *z, float alpha)
{
static float lastz = 0;
if (lastz == 0)
{
lastz = *z;
}
else
{
*z = alpha*lastz + (1 - alpha)*(*z);
lastz = *z;
}
}
bool IsHmdIntoStandBy(float *Pitch, float *Roll, float dTime)
{
static float LastPitch = 0.0f, LastRoll = 0.0f;
static float LastGyroX = 0.0f, LastGyroY = 0.0f;
static int count = 0;
float GyroX = 0.0f, GyroY = 0.0f, alpha = 0.98f;
/* 初始化静态变量 */
if (LastPitch == 0.0f&&LastRoll == 0.0f){
LastPitch = *Pitch;
LastRoll = *Roll;
}
/*对Pitch和Roll微分求Gx和Gy */
GyroX = fabsf(*Roll - LastRoll) / dTime;
GyroY = fabsf(*Pitch - LastPitch) / dTime;
/*初始化静态变量*/
if (LastGyroX == 0.0f&&LastGyroY == 0.0f){
LastGyroX = GyroX;
LastGyroY = GyroY;
}
/*进行一个滑动窗口滤波降低偶然误差*/
GyroX = alpha*LastGyroX + (1 - alpha)*GyroX;
GyroY = alpha*LastGyroY + (1 - alpha)*GyroY;
printf("%f %f\n", GyroX, GyroY);
if (GyroX < 0.0055f&&GyroY < 0.0015f){
count++;
if (count == 3000){
count = 0;
HmdStandByState = true;
printf("头显进入休眠\n");
}
}
else{
count = 0;
HmdStandByState = false;
}
/* 更新静态变量 */
LastRoll = *Roll;
LastPitch = *Pitch;
LastGyroX = GyroX;
LastGyroY = GyroY;
return HmdStandByState;
}
float DM_GetYaw(void)
{
return Yaw;
}
float DM_GetPitch(void)
{
return Pitch;
}
float DM_GetRoll(void)
{
return Roll;
}
Quaternion CorrectQuaterinonYaw(Quaternion q)
{
Quaternion qcorr;
qcorr.w = q.w; qcorr.x = 0.0f;
qcorr.y = q.y; qcorr.z = 0.0f;
qcorr = QuaternionInversion(qcorr);
float mod = QuaternionMod(qcorr);
qcorr.w = qcorr.w / mod;
qcorr.x = qcorr.x / mod;
qcorr.y = qcorr.y / mod;
qcorr.z = qcorr.z / mod;
return qcorr;
}
int ResetHeading(void)
{
Quaternion q = { GlobalQ.w, 0, GlobalQ.y, 0 };
CQ_DM = CorrectQuaterinonYaw(q);
DM_Corrected = true;
return DM_Corrected ? 1 : 0;
}
Quaternion GetPoseQuat(void)
{
static Quaternion QLast = { 1.0f, 0.0f, 0.0f, 0.0f };
if (!GlobalQ_Updating)
{
QLast.w = GlobalQ.w;
QLast.x = GlobalQ.x;
QLast.y = GlobalQ.y;
QLast.z = GlobalQ.z;
}
if (DM_Corrected){
return QuaternionMulti(QLast, CQ_DM);
}
return QLast;
}