【51单片机快速入门指南】4.3.3: MPU6050使用Mahony AHRS算法实现六轴姿态融合获取四元数、欧拉角

STC89C516 32MHz
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
上位机:Vofa+ 1.3.10


       移植自MPU6050姿态解算——Mahony互补滤波 —— 大写的小写字母

       加入了输入数据范围的自动处理,即使更改量程也能正确解算。

源码

       为了避免所用RAM超标,部分变量设为idata类型,移植时需注意。
       所用MCU为STC89C516 晶振16MHz 6T模式

       stdint.h【51单片机快速入门指南】1:基础知识和工程创建
       软件I2C程序见【51单片机快速入门指南】4: 软件 I2C
       串口部分见【51单片机快速入门指南】3.3:USART 串口通信
       MPU6050.c、MPU6050.h见【51单片机快速入门指南】4.3: I2C读取MPU6050陀螺仪的原始数据

       Kp和Ki要按需调整,我这里取1.5和0.005

Mahony_6.c

#include <math.h>
#include "MPU6050.h"

#define G 9.80665f					// m/s^2

#define PI 3.141592653589793

idata float halfT = 1;
idata float GYRO_K = 1, ACCEL_K = 1;

void MPU6050_Mahony_Init(float loop_ms)
{
	halfT = loop_ms/1000./2;	//计算周期的一半,单位s
	switch((MPU_Read_Byte(MPU_GYRO_CFG_REG) >> 3) & 3)
	{
		case 0:
			GYRO_K = 1./131/57.3;
			break;
		case 1:
			GYRO_K = 1./65.5/57.3;
			break;
		case 2:
			GYRO_K = 1./32.8/57.3;
			break;
		case 3:
			GYRO_K = 1./16.4/57.3;
			break;
	}
	switch((MPU_Read_Byte(MPU_ACCEL_CFG_REG) >> 3) & 3)
	{
		case 0:
			ACCEL_K = G/16384;
			break;
		case 1:
			ACCEL_K = G/8192;
			break;
		case 2:
			ACCEL_K = G/4096;
			break;
		case 3:
			ACCEL_K = G/2048;
			break;
	}
}

static float invSqrt(float x) 		//快速计算 1/Sqrt(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;
}

#define Kp 1.50f
#define Ki 0.005f

idata float Pitch, Roll, Yaw;
idata float q0 = 1, q1 = 0, q2 = 0, q3 = 0;		//四元数
idata float exInt = 0, eyInt = 0, ezInt = 0;		//叉积计算误差的累计积分

void Imu_Update(float gx, float gy, float gz, float ax, float ay, float az)
{
	unsigned char i;
	float vx, vy, vz;							//实际重力加速度
	float ex, ey, ez;							//叉积计算的误差
	float norm;

	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;

	//将加速度原始AD值转换为m/s^2
	ax = ax * ACCEL_K;
	ay = ay * ACCEL_K;
	az = az * ACCEL_K;
	//将陀螺仪AD值转换为 弧度/s
	gx = gx * GYRO_K;
	gy = gy * GYRO_K;
	gz = gz * GYRO_K;

	if (ax * ay * az == 0)
		return;

	//加速度计测量的重力方向(机体坐标系)
	norm = invSqrt(ax * ax + ay * ay + az * az);			//之前这里写成invSqrt(ax*ax + ay+ay + az*az)是错误的,现在修改过来了
	ax = ax * norm;
	ay = ay * norm;
	az = az * norm;

	//四元数推出的实际重力方向(机体坐标系)
	vx = 2 * (q1q3 - q0q2);
	vy = 2 * (q0q1 + q2q3);
	vz = q0q0 - q1q1 - q2q2 + q3q3;

	//叉积误差
	ex = (ay * vz - az * vy);
	ey = (az * vx - ax * vz);
	ez = (ax * vy - ay * vx);

	//叉积误差积分为角速度
	exInt = exInt + ex * Ki;
	eyInt = eyInt + ey * Ki;
	ezInt = ezInt + ez * Ki;

	//角速度补偿
	gx = gx + Kp * ex + exInt;
	gy = gy + Kp * ey + eyInt;
	gz = gz + Kp * ez + ezInt;

	//更新四元数
	q0 = q0 + (-q1 * gx - q2 * gy - q3 * gz) * halfT;
	q1 = q1 + (q0 * gx + q2 * gz - q3 * gy) * halfT;
	q2 = q2 + (q0 * gy - q1 * gz + q3 * gx) * halfT;
	q3 = q3 + (q0 * gz + q1 * gy - q2 * gx) * halfT;

	//单位化四元数
	norm = invSqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
	q0 = q0 * norm;
	q1 = q1 * norm;
	q2 = q2 * norm;
	q3 = q3 * norm;

	//四元数反解欧拉角
	Yaw = atan2(2.f * (q1q2 + q0q3), q0q0 + q1q1 - q2q2 - q3q3) * 57.3f;
	Pitch = -asin(2.f * (q1q3 - q0q2)) * 57.3f;
	Roll = atan2(2.f * q2q3 + 2.f * q0q1, q0q0 - q1q1 - q2q2 + q3q3) * 57.3f;
}

Mahony_6.h

#ifndef Mahony_6_H_
#define Mahony_6_H_

extern idata float Pitch, Roll, Yaw;
extern idata float q0, q1, q2, q3;		//四元数

void MPU6050_Mahony_Init(float loop_ms);
void Imu_Update(float gx, float gy, float gz, float ax, float ay, float az);

#endif

使用方法

先调用MPU6050_Mahony_Init(dt),参数为一次循环的时间,单位为ms
再使用Imu_Update姿态融合函数。

测试程序

main.c

#include <STC89C5xRC.H>
#include "intrins.h"
#include "stdint.h"
#include "USART.h"
#include "./MPU6050/MPU6050.h"
#include "./MPU6050/Mahony_6.h"

void Delay1ms()		//@32MHz
{
	unsigned char i, j;

	i = 6;
	j = 44;
	do
	{
		while (--j);
	} while (--i);
}


void Delay_ms(int i)
{
	while(i--)
		Delay1ms();
}

void main(void)
{
	int16_t aacx,aacy,aacz;		//加速度传感器原始数据
	int16_t gyrox,gyroy,gyroz;	//陀螺仪原始数据

	USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 32000000, 4800, DOUBLE_BAUD_DISABLE, USART_TIMER_2);
	MPU_Init(); 
	MPU6050_Mahony_Init(85);
	while(1)
	{	
		MPU_Get_Accelerometer(&aacx, &aacy, &aacz);	//得到加速度传感器数据
		MPU_Get_Gyroscope(&gyrox, &gyroy, &gyroz);	//得到陀螺仪数据
		Imu_Update(gyrox, gyroy, gyroz, aacx, aacy, aacz);
		printf("%f, ", Pitch);
		printf("%f, ", Roll);
		printf("%f\r\n", Yaw);
	}
}

效果

       个人感觉在51上,Mahony的效果要远远好于DMP及其他滤波算法的表现,如果把零偏处理了,效果会更好。
在这里插入图片描述

  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
### 回答1: SAD算法是用来实现立体匹配的一种常用算法。立体匹配是指在两幅图像之间进行匹配,以确定它们之间的深度信息。 以下是使用SAD算法实现立体匹配的伪代码: 1. 读入左右两幅图像。 2. 对于左图像中的每个像素,在右图像中找到与其对应的像素。 3. 计算左图像像素与右图像像素的像素差值。 4. 对于每对像素,累加像素差值的绝对值。 5. 对于每对像素,如果累加的像素差值最小,则记录该像素对的深度信息。 6. 将深度信息输出到文件或显示在屏幕上。 请注意,这仅是一个大致的伪代码,并不是完整的代码。在实际实现中,还需要考虑许多细节,如边界处理、像素差值的权重计算等。 ### 回答2: 伪代码如下所示: 1. 初始化两幅图像的灰度值矩阵:left_gray_img和right_gray_img。 2. 初始化目标图像的视差值矩阵:disparity_map。 3. 设定窗口大小:window_size。 4. 对于每一个像素点(x, y): 4.1. 初始化最小匹配代价:min_cost = 正无穷大。 4.2. 初始化最优视差值:best_disparity = 0。 4.3. 对于每一个视差值(disparity): 4.3.1. 如果 (x - disparity) < 0,则跳过本次迭代。 4.3.2. 计算当前像素点和(x - disparity)对应像素点窗口内的灰度值差距:cost。 4.3.3. 如果 cost < min_cost,则更新 min_cost = cost,并记录 best_disparity = disparity。 4.4. 将 best_disparity 赋值给 disparity_map 中的 (x, y)位置。 5. 返回 disparity_map。 以上的伪代码主要描述了使用SAD算法实现立体匹配的过程。其中,SAD(Sum of Absolute Differences)是一种计算灰度值差距的方法,通过比较两幅图像窗口内对应像素点灰度值的绝对差值之和来确定最匹配的视差值。在第4.3.3步骤中,选择最小的灰度值差距作为最优匹配代价,并记录对应的视差值。最后将每个像素点的最优视差值记录在disparity_map中,用于生成立体图像。 ### 回答3: 伪代码如下: 1. 输入左右两个图像,分别为左图(leftImage)和右图(rightImage); 2. 设置搜索范围(disRange),表示在左图中搜索右图的像素点的最大偏移量; 3. 创建一个与左图大小相同的视差图(disparityMap),用于存储计算得到的视差值; 4. 遍历左图每个像素点的位置(x, y): - 初始化最小总和差异(minSumDiff)为正无穷大,最小视差(minDisparity)为0; - 在搜索范围内遍历右图的像素点的位置(x_r, y_r): - 计算当前左图像素点与右图像素点的差异(diff); - 根据差异更新最小总和差异和最小视差(如果diff小于minSumDiff,则将当前diff赋值给minSumDiff,将右图像素点的x坐标赋值给minDisparity); - 将最小视差赋值给对应位置的视差图像素点(x, y); 5. 返回视差图(disparityMap)。 以上的伪代码基于SAD算法实现立体匹配。在算法中,通过计算左图像素点与右图像素点的差异(使用SAD方法),从而找到最匹配的像素点位置,将其视差作为匹配点的深度信息。用户可以根据实际需求进行相应的改进和调整,以适应具体的立体匹配应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乙酸氧铍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值