传感器数据融合
MPU6050有了驱动之后就可以考虑数据融合了。简单来说,陀螺仪精度高,但时间长了会有漂移,加速度动态精度差,但没有长期漂移。综合利用陀螺仪和加速度计的特点,优势互补获得准确的姿态角度,方法就是用卡尔曼滤波做数据融合。
Snake0301使用的是MPU6050内置的DMP,直接输出姿态数据,使用方法网上有例程,可见这里:
https://gist.github.com/jannson/11280208
但是很多人说用DMP后Z轴会有漂移,Snake0301也有反映,且DMP的输出速率不是很高,最快200Hz,所以准备舍弃这种方法,还是中规中矩的用滤波吧。
今天看了"让四轴飞"网友的工程,发现程序还真是简洁,基本上一个模块一个.c文件即可实现,总共才那么六七个模块,重新移植修改起来就很方便了。反观crazyfliie的工程,代码量多了好几倍,又加入了操作系统FreeRTOS,真担心MCU的处理能力,不过看他们的视频,还是很有信心的。
附链接地址:
http://v.youku.com/v_show/id_XNTEwODM4MzEy/v.swf.html
上面分析了Crazyflie的acc采样数值的滤波,接着分析其他函数吧。上代码
void imu6Read(Axis3f* gyroOut, Axis3f* accOut)
{
mpu6050GetMotion6(&accelMpu.x, &accelMpu.y, &accelMpu.z, &gyroMpu.x, &gyroMpu.y, &gyroMpu.z);
imuAddBiasValue(&gyroBias, &gyroMpu);
if (!accelBias.isBiasValueFound)
imuAddBiasValue(&accelBias, &accelMpu);
if (!gyroBias.isBiasValueFound)
imuFindBiasValue(&gyroBias);
#ifdef IMU_TAKE_ACCEL_BIAS
if (gyroBias.isBiasValueFound &&
!accelBias.isBiasValueFound)
{
Axis3i32 mean;
imuCalculateBiasMean(&accelBias, &mean);
accelBias.bias.x = mean.x;
accelBias.bias.y = mean.y;
accelBias.bias.z = mean.z - IMU_1G_RAW;
accelBias.isBiasValueFound = TRUE;
// uartPrintf("Accel bias: %i, %i, %i\n",
// accelBias.bias.x, accelBias.bias.y, accelBias.bias.z);
}
#endif
imuAccIIRLPFilter(&accelMpu, &accelLPF, &accelStoredFilterValues,
(int32_t)imuAccLpfAttFactor);
imuAccAlignToGravity(&accelLPF, &accelLPFAligned);
// Re-map outputs
gyroOut->x = (gyroMpu.x - gyroBias.bias.x) * IMU_DEG_PER_LSB_CFG;
gyroOut->y = (gyroMpu.y - gyroBias.bias.y) * IMU_DEG_PER_LSB_CFG;
gyroOut->z = (gyroMpu.z - gyroBias.bias.z) * IMU_DEG_PER_LSB_CFG;
accOut->x = (accelLPFAligned.x - accelBias.bias.x) * IMU_G_PER_LSB_CFG;
accOut->y = (accelLPFAligned.y - accelBias.bias.y) * IMU_G_PER_LSB_CFG;
accOut->z = (accelLPFAligned.z - accelBias.bias.z) * IMU_G_PER_LSB_CFG;
// uartSendData(sizeof(Axis3f), (uint8_t*)gyroOut);
// uartSendData(sizeof(Axis3f), (uint8_t*)accOut);
}
typedef struct
{
int16_t x;
int16_t y;
int16_t z;
} Axis3i16;
typedef struct
{
int32_t x;
int32_t y;
int32_t z;
} Axis3i32;
typedef struct
{
float x;
float y;
float z;
} Axis3f;
typedef struct
{
Axis3i16 bias;
bool isBiasValueFound;
bool isBufferFilled;
Axis3i16* bufHead;
Axis3i16 buffer[IMU_NBR_OF_BIAS_SAMPLES];
} BiasObj;
BiasObj gyroBias;
BiasObj accelBias;
int32_t varianceSampleTime;
Axis3i16 gyroMpu;
Axis3i16 accelMpu;
Axis3i16 accelLPF;
Axis3i16 accelLPFAligned;
Axis3i16 mag;
Axis3i32 accelStoredFilterValues;
uint8_t imuAccLpfAttFactor;
mpu6050GetMotion6(&accelMpu.x, &accelMpu.y, &accelMpu.z, &gyroMpu.x, &gyroMpu.y, &gyroMpu.z);
从MPU6050读取6个数值,不表。
2、imuAddBiasValue(&gyroBias, &gyroMpu);
函数原型:
/**
* Adds a new value to the variance buffer and if it is full
* replaces the oldest one. Thus a circular buffer.
*/
static void imuAddBiasValue(BiasObj* bias, Axis3i16* dVal)
{
bias->bufHead->x = dVal->x;
bias->bufHead->y = dVal->y;
bias->bufHead->z = dVal->z;
bias->bufHead++;
if (bias->bufHead >= &bias->buffer[IMU_NBR_OF_BIAS_SAMPLES])
{
bias->bufHead = bias->buffer;
bias->isBufferFilled = TRUE;
}
}
函数原型:
/**
* Checks if the variances is below the predefined thresholds.
* The bias value should have been added before calling this.
* @param bias The bias object
*/
static bool imuFindBiasValue(BiasObj* bias)
{
bool foundBias = FALSE;
if (bias->isBufferFilled)
{
Axis3i32 variance;
Axis3i32 mean;
imuCalculateVarianceAndMean(bias, &variance, &mean);
//uartSendData(sizeof(variance), (uint8_t*)&variance);
//uartSendData(sizeof(mean), (uint8_t*)&mean);
//uartPrintf("%i, %i, %i", variance.x, variance.y, variance.z);
//uartPrintf("%i, %i, %i\n", mean.x, mean.y, mean.z);
if (variance.x < GYRO_VARIANCE_THRESHOLD_X &&
variance.y < GYRO_VARIANCE_THRESHOLD_Y &&
variance.z < GYRO_VARIANCE_THRESHOLD_Z &&
(varianceSampleTime + GYRO_MIN_BIAS_TIMEOUT_MS < xTaskGetTickCount()))
{
varianceSampleTime = xTaskGetTickCount();
bias->bias.x = mean.x;
bias->bias.y = mean.y;
bias->bias.z = mean.z;
foundBias = TRUE;
bias->isBiasValueFound = TRUE;
}
}
return foundBias;
}
在填充完128个值后做如下处理:
imuCalculateVarianceAndMean(bias, &variance, &mean);
函数原型:
/**
* Calculates the variance and mean for the bias buffer.
*/
static void imuCalculateVarianceAndMean(BiasObj* bias, Axis3i32* varOut, Axis3i32* meanOut)
{
uint32_t i;
int32_t sum[GYRO_NBR_OF_AXES] = {0};
int64_t sumSq[GYRO_NBR_OF_AXES] = {0};
for (i = 0; i < IMU_NBR_OF_BIAS_SAMPLES; i++)
{
sum[0] += bias->buffer[i].x;
sum[1] += bias->buffer[i].y;
sum[2] += bias->buffer[i].z;
sumSq[0] += bias->buffer[i].x * bias->buffer[i].x;
sumSq[1] += bias->buffer[i].y * bias->buffer[i].y;
sumSq[2] += bias->buffer[i].z * bias->buffer[i].z;
}
varOut->x = (sumSq[0] - ((int64_t)sum[0] * sum[0]) / IMU_NBR_OF_BIAS_SAMPLES);
varOut->y = (sumSq[1] - ((int64_t)sum[1] * sum[1]) / IMU_NBR_OF_BIAS_SAMPLES);
varOut->z = (sumSq[2] - ((int64_t)sum[2] * sum[2]) / IMU_NBR_OF_BIAS_SAMPLES);
meanOut->x = sum[0] / IMU_NBR_OF_BIAS_SAMPLES;
meanOut->y = sum[1] / IMU_NBR_OF_BIAS_SAMPLES;
meanOut->z = sum[2] / IMU_NBR_OF_BIAS_SAMPLES;
isInit = TRUE;
}
可以看出varOut的每一项(x,y,z)为该项的方差
。
meanOut为128个数的均值。
若计算出的3个方差小于设定值,且当前时间距上次采样时间小于1ms,则更新3个偏移值,并更新BiasObj的isBiasValueFound成员。
4、imuCalculateBiasMean(&accelBias, &mean);该函数Calculates the mean for the bias buffer.
5、 imuAccIIRLPFilter(&accelMpu, &accelLPF, &accelStoredFilterValues,
(int32_t)imuAccLpfAttFactor);
该函数上楼已经分析了,pass。
6、imuAccAlignToGravity(&accelLPF, &accelLPFAligned);
// Pre-calculated values for accelerometer alignment
float cosPitch;
float sinPitch;
float cosRoll;
float sinRoll;
/**
* Compensate for a miss-aligned accelerometer. It uses the trim
* data gathered from the UI and written in the config-block to
* rotate the accelerometer to be aligned with gravity.
*/
static void imuAccAlignToGravity(Axis3i16* in, Axis3i16* out)
{
Axis3i16 rx;
Axis3i16 ry;
// Rotate around x-axis
rx.x = in->x;
rx.y = in->y * cosRoll - in->z * sinRoll;
rx.z = in->y * sinRoll + in->z * cosRoll;
// Rotate around y-axis
ry.x = rx.x * cosPitch - rx.z * sinPitch;
ry.y = rx.y;
ry.z = -rx.x * sinPitch + rx.z * cosPitch;
out->x = ry.x;
out->y = ry.y;
out->z = ry.z;
}
7、最终结果
// Re-map outputs
gyroOut->x = (gyroMpu.x - gyroBias.bias.x) * IMU_DEG_PER_LSB_CFG;
gyroOut->y = (gyroMpu.y - gyroBias.bias.y) * IMU_DEG_PER_LSB_CFG;
gyroOut->z = (gyroMpu.z - gyroBias.bias.z) * IMU_DEG_PER_LSB_CFG;
accOut->x = (accelLPFAligned.x - accelBias.bias.x) * IMU_G_PER_LSB_CFG;
accOut->y = (accelLPFAligned.y - accelBias.bias.y) * IMU_G_PER_LSB_CFG;
accOut->z = (accelLPFAligned.z - accelBias.bias.z) * IMU_G_PER_LSB_CFG;
对陀螺仪的数据去偏差后赋值给gyroOut单位为度/s,对补偿后的加速度值赋值给accOut,单位为G。
回复
| 有奖活动 | |
|---|---|
| 硬核工程师专属补给计划——填盲盒 | |
| “我踩过的那些坑”主题活动——第002期 | |
| 【EEPW电子工程师创研计划】技术变现通道已开启~ | |
| 发原创文章 【每月瓜分千元赏金 凭实力攒钱买好礼~】 | |
| 【EEPW在线】E起听工程师的声音! | |
| 高校联络员开始招募啦!有惊喜!! | |
| 【工程师专属福利】每天30秒,积分轻松拿!EEPW宠粉打卡计划启动! | |
| 送您一块开发板,2025年“我要开发板活动”又开始了! | |
我要赚赏金
