首页 > 其他分享 >MPU6050使用心得(简单分享一下)

MPU6050使用心得(简单分享一下)

时间:2023-11-10 13:03:35浏览次数:42  
标签:gyro accel MPU6050 st data test 分享 心得 result


前言

选用MPU6050做 倾斜检测 功能。

前期准备

开发板:正点原子STM32F103 精英版(STM32F103ZET6)

MPU6050使用心得(简单分享一下)_倾斜检测

模块:GY-521 MPU6050

MPU6050使用心得(简单分享一下)_STM32_02

其他:杜邦线若干、烧录线、FlyMcu、Keil5、正点原子开发板配套的套件(TFTLCD)

例程、资料下载

源自淘宝卖家:https://pan.baidu.com/share/init?surl=dNDqcp76L9QdM7iSZYfz_A 密码:4eum

GY-521 MPU6050模块 三维角度传感器6DOF三轴加速度计电子陀螺仪\MPU6050六轴角度加速度传感器\11,ATK-MPU6050六轴传感器模块\2,程序源码\(库函数版本,适合精英STM32开发板)实验30 MPU6050六轴传感器实验.rar 我们的例程刚好选择的适配 我们开发版的正点原子例程,虽然这个程序是针对ATK-MPU6050,不过其实2个模块差别不大,可以通用例程,非常方便。

原理图

我们可以看下2个模型的原理图,可以发现ATK-MPU6050只是有几个管脚没有接出来,不使用的话,其实没啥区别

GY-521 MPU6050

MPU6050使用心得(简单分享一下)_STM32_03

ATK-MPU6050

这边的AUX_CL、AUX_DA就没接

MPU6050使用心得(简单分享一下)_传感器_04

引脚描述

源自:姿态传感器——MPU6050

SCL、SDA:是连接MCU的IIC接口,MCU通过这个IIC接口来控制MPU6050,此时MPU6050作为一个IIC从机设备,接单片机的I2C_SCL。
XCL、XDA:辅助IIC用来连接其他器件,可用来连接外部从设备,比如磁传感器,这样就可以组成一个九轴传感器,不需要连接单片机。
AD0:地址管脚,可以不接单片机。当MPU6050作为一个IIC从机设备的时候,有8位地址,高7位的地址是固定的,就是WHOAMI寄存器的默认——0x68,最低的一位是由AD0的连线决定的。
AD0接GND时,高8位的最后一位是0,所以iic从机地址是0x68;
AD0接VCC时,高8位的最后一位是1,所以iic从机地址是0x69。
INT:数据输出的中断引脚,可以不接单片机,准备好数据之后,通过中断告诉STM32,从而获取数据。
VCC:接3.3V或5V电源
GND:接地

所以ATK-MPU6050这XCL、XDA不接也罢,不影响。

接线

可以参考资料内的ATK-MPU6050六轴传感器模块用户手册_V1.0.pdf,或者直接看例程源码。

VCC -》 3.3V
GND -》 GND
SCL -》 PB10
SDA -》 PB11
AD0 -》 PA15

源码关键部分分析

main.c

关注的核心部分就在 while(mpu_dmp_init()),DMP初始化这块,注意会出问题的部分

inv_mpu.c

//mpu6050,dmp初始化
//返回值:0,正常
//    其他,失败
u8 mpu_dmp_init(void)
{
	u8 res=0;
	MPU_IIC_Init(); 	//初始化IIC总线
	if(mpu_init()==0)	//初始化MPU6050
	{	 
		res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
		if(res)return 1; 
		res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO
		if(res)return 2; 
		res=mpu_set_sample_rate(DEFAULT_MPU_HZ);	//设置采样率
		if(res)return 3; 
		res=dmp_load_motion_driver_firmware();		//加载dmp固件
		if(res)return 4; 
		res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
		if(res)return 5; 
		res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP|	//设置dmp功能
		    DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
		    DMP_FEATURE_GYRO_CAL);
		if(res)return 6; 
		res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);	//设置DMP输出速率(最大不超过200Hz)
		if(res)return 7;   
		res=run_self_test();		//自检 核心部分,最容易出错的地方
		if(res)return 8;    
		res=mpu_set_dmp_state(1);	//使能DMP
		if(res)return 9;     
	}else return 10;
	return 0;
}

其中run_self_test()是关键,自检操作会对模块的各角度进行调整到“零度”,所谓的零度,不是真零度,稍后补充。

如果自检这一套不通过,LCD上就会一直跳动MPU6050 Error,当然也许你运气好,直接通过了,也说不准hh

那么按照正常操作,官方demo下,你应该将模块水平地面静止放置,芯片朝上(其实朝下也可以,但是有坑点!!!),然后复位程序(不复位也可以,会死循环跑),顺利的话,你就能看到各个角度值了,当然,细心的你会发现一些端倪,显示的Yaw航向角会跳动,那么这时候,你可以看看这篇文章的讲解:MPU6050常见问题的分析与处理,讲得很好,直击痛点,其实就是硬件问题导致的漂移,需要额外追加磁力计。不过我需要实现的功能只是倾斜检测,所以Yaw航向角可以不做使用,也就不影响了。

MPU6050使用心得(简单分享一下)_倾斜检测_05

那么到了这一步,即使你水平禁止放平了模块,依然无法完成自检,那我们就再继续深入。前情建议:可以换一个模块试试(因为我在深入探究后,最后发现是模块问题)

进入run_self_test(void),一探究竟

//MPU6050自测试
//返回值:0,正常
//    其他,失败
u8 run_self_test(void)
{
	int result;
	//char test_packet[4] = {0};
	long gyro[3], accel[3];
	
	
	//float sens;
	//unsigned short accel_sens;
	
	
	result = mpu_run_self_test(gyro, accel);
	
	// 强制自检成功,万不得已的情况下使用
	//float sens;
	//unsigned short accel_sens;
	/*
	mpu_get_gyro_sens(&sens);
	gyro[0] = (long)(gyro[0] * sens);
	gyro[1] = (long)(gyro[1] * sens);
	gyro[2] = (long)(gyro[2] * sens);
	dmp_set_gyro_bias(gyro);
	mpu_get_accel_sens(&accel_sens);
	accel[0] *= accel_sens;
	accel[1] *= accel_sens;
	accel[2] *= accel_sens;
	dmp_set_accel_bias(accel);
	return 0;
	*/
	
	// 如果加速度和陀螺仪自检通过,就把当前读取到的各个xyz轴数据设置,作为基准值
	if (result == 0x3) 
	{
		/* Test passed. We can trust the gyro data here, so let's push it down
		* to the DMP.
		*/
		float sens;
		unsigned short accel_sens;
		mpu_get_gyro_sens(&sens);
		gyro[0] = (long)(gyro[0] * sens);
		gyro[1] = (long)(gyro[1] * sens);
		gyro[2] = (long)(gyro[2] * sens);
		dmp_set_gyro_bias(gyro);
		mpu_get_accel_sens(&accel_sens);
		accel[0] *= accel_sens;
		accel[1] *= accel_sens;
		accel[2] *= accel_sens;
		dmp_set_accel_bias(accel);
		return 0;
	}else return 1;
}

在看到源码后,其实就显而易见了,自检不通过,其实就是mpu_run_self_test函数返回的结果不为0x3,那我们就继续深入mpu_run_self_test函数

/* 
这段注释解释了一个触发传感器自检的函数。自检过程会返回一个掩码,用于指示哪些传感器通过了自检,哪些未通过。
其中,第 0 位表示陀螺仪,第 1 位表示加速度计,第 2 位表示磁罗盘。
注释中还提到目前 MPU6500 不支持硬件自检,但仍然可以通过此函数获取加速度计和陀螺仪的偏移。
另外,为了保证自检的准确性,调用此函数时设备应该处于面朝上或面朝下的状态,即 z 轴与重力平行。
函数会返回一个结果掩码,用于表示哪些传感器未通过自检,哪些通过了。
*/
/**
 *  @brief      Trigger gyro/accel/compass self-test.
 *  On success/error, the self-test returns a mask representing the sensor(s)
 *  that failed. For each bit, a one (1) represents a "pass" case; conversely,
 *  a zero (0) indicates a failure.
 *
 *  \n The mask is defined as follows:
 *  \n Bit 0:   Gyro.
 *  \n Bit 1:   Accel.
 *  \n Bit 2:   Compass.
 *
 *  \n Currently, the hardware self-test is unsupported for MPU6500. However,
 *  this function can still be used to obtain the accel and gyro biases.
 *
 *  \n This function must be called with the device either face-up or face-down
 *  (z-axis is parallel to gravity). 
目前,MPU6500 不支持硬件自检。然而,该函数仍可用于获取加速度计和陀螺仪的偏移。
调用此函数时,设备必须处于面朝上或面朝下的状态(z 轴与重力平行)。
 *  @param[out] gyro        Gyro biases in q16 format.
 *  @param[out] accel       Accel biases (if applicable) in q16 format.
 *  @return     Result mask (see above).
 */
 // 此时的gyro,accel是前一个函数定义的局部变量,没有初始值
int mpu_run_self_test(long *gyro, long *accel)
{
#ifdef MPU6050
	  // 原来这个参数为2,堆栈中为0x02,即使改为0x03,在仿真堆栈中任然为0x02
    const unsigned char tries = 2;
    long gyro_st[3], accel_st[3];
    unsigned char accel_result, gyro_result;
#ifdef AK89xx_SECONDARY
    unsigned char compass_result;
#endif
    int ii;
#endif
    int result;
    unsigned char accel_fsr, fifo_sensors, sensors_on;
    unsigned short gyro_fsr, sample_rate, lpf;
    unsigned char dmp_was_on;

    if (st.chip_cfg.dmp_on) {
        mpu_set_dmp_state(0);
        dmp_was_on = 1;
    } else
        dmp_was_on = 0;

    /* Get initial settings. */
		// 0x07D0
    mpu_get_gyro_fsr(&gyro_fsr);
		// 0x02
    mpu_get_accel_fsr(&accel_fsr);
		// 0x002A
    mpu_get_lpf(&lpf);
		// 0x0064
    mpu_get_sample_rate(&sample_rate);
		// 0x78 'x'
    sensors_on = st.chip_cfg.sensors;
		// 0x78 'x'
    mpu_get_fifo_config(&fifo_sensors);

    /* For older chips, the self-test will be different. */
#if defined MPU6050
		
    for (ii = 0; ii < tries; ii++)
        if (!get_st_biases(gyro, accel, 0))
            break;
    if (ii == tries) {
        /* If we reach this point, we most likely encountered an I2C error.
         * We'll just report an error for all three sensors.
         */
        result = 0;
        goto restore;
    }
    for (ii = 0; ii < tries; ii++)
        if (!get_st_biases(gyro_st, accel_st, 1))
            break;
    if (ii == tries) {
        /* Again, probably an I2C error. */
        result = 0;
        goto restore;
    }
		
	printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
	printf("accel_st=%ld, %ld, %ld\r\n", accel_st[0], accel_st[1], accel_st[2]);
	printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
	printf("gyro_st=%ld, %ld, %ld\r\n", gyro_st[0], gyro_st[1], gyro_st[2]);
		
	// 加速度传感器自检
    accel_result = accel_self_test(accel, accel_st);
	// 陀螺仪传感器的自检 
    gyro_result = gyro_self_test(gyro, gyro_st);
		
	printf("accel_result=%d, gyro_result=%d\r\n", accel_result, gyro_result);

    result = 0;
    if (!gyro_result)
        result |= 0x01;
    if (!accel_result)
        result |= 0x02;

#ifdef AK89xx_SECONDARY
    compass_result = compass_self_test();
    if (!compass_result)
        result |= 0x04;
#endif
restore:
#elif defined MPU6500
    /* For now, this function will return a "pass" result for all three sensors
     * for compatibility with current test applications.
     */
    get_st_biases(gyro, accel, 0);
    result = 0x7;
#endif
    /* Set to invalid values to ensure no I2C writes are skipped. */
    st.chip_cfg.gyro_fsr = 0xFF;
    st.chip_cfg.accel_fsr = 0xFF;
    st.chip_cfg.lpf = 0xFF;
    st.chip_cfg.sample_rate = 0xFFFF;
    st.chip_cfg.sensors = 0xFF;
    st.chip_cfg.fifo_enable = 0xFF;
    st.chip_cfg.clk_src = INV_CLK_PLL;
    mpu_set_gyro_fsr(gyro_fsr);
    mpu_set_accel_fsr(accel_fsr);
    mpu_set_lpf(lpf);
    mpu_set_sample_rate(sample_rate);
    mpu_set_sensors(sensors_on);
    mpu_configure_fifo(fifo_sensors);

    if (dmp_was_on)
        mpu_set_dmp_state(1);

    return result;
}

进来后其实可以发现,影响result值的其实就是这两个自检函数,两个传感器只要有一个不通过,就GG,同样可以通过返回值判断是哪块的错误。

// 加速度传感器自检
accel_result = accel_self_test(accel, accel_st);
// 陀螺仪传感器的自检 
gyro_result = gyro_self_test(gyro, gyro_st);

当时我的错误是在加速度这块accel_self_test,其实这两块实现也差不多,我们继续深入accel_self_test函数。

/*
用于加速度传感器自检的函数,主要是通过比较两组偏移值来检查传感器是否正常工作。
函数的目标是检测传感器在不同轴向上的偏移值是否在允许的范围内,如果都在范围内,则返回0,表示自检通过。
*/
// bias_regular[0] = 0xFFFF14F6, bias_st[0] = 0xFFFFA5DD
static int accel_self_test(long *bias_regular, long *bias_st)
{
    int jj, result = 0;
    float st_shift[3], st_shift_cust, st_shift_var;

	// 计算加速度传感器的标定偏移
	// st_shift[3] = {0.561419249, 0.47499004, 0.60024482}
    get_accel_prod_shift(st_shift);
    for(jj = 0; jj < 3; jj++) {
		// 强制跳过z轴检测
		if(2 == jj) break;
			
	    // 计算当前轴向的自定义偏移,即标定偏移和测试偏移之间的差值除以一个常数
		// 第三组数据时,st_shift_cust = 0,0xFFF10000
        st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
		// 如果标定偏移不为零(即不等于零),则进入这个条件判断
        if (st_shift[jj]) {
			// 计算偏移变化,即自定义偏移相对于标定偏移的比例减去1
			// test.max_accel_var设置于全局变量,为0.14
			// 如果偏移变化的绝对值大于某个阈值 test.max_accel_var,则表示偏移变化超过了允许范围,将当前轴向的位设置到 result 变量中
            if (fabs(st_shift_var) > test.max_accel_var)
                result |= 1 << jj;
				printf("accel_self_test fabs(st_shift_var)[%d]=%f\r\n", jj, fabs(st_shift_var));
				// min_g=0.3, max_g=0.95
        } else if ((st_shift_cust < test.min_g) ||
            (st_shift_cust > test.max_g)) {
            result |= 1 << jj;
					printf("st_shift_cust[%d]=%f\r\n", jj, st_shift_cust);
			}
    }

    return result;
}

// 陀螺仪传感器的自检,通过计算偏移值和变化来检测陀螺仪是否在正常工作范围内。如果所有轴向的偏移值都在允许范围内,函数返回0,表示自检通过
// bias_regular[0] = 0xFFFF7447, bias_st[0] = 0x0031289B
static int gyro_self_test(long *bias_regular, long *bias_st)
{
	 // 定义了整型变量 jj 和 result,用于迭代循环和存储自检结果
    int jj, result = 0;
	 // 定义了一个长度为 3 的无符号字符数组 tmp,用于存储临时数据
    unsigned char tmp[3];
	 // 定义了三个浮点数变量 st_shift,st_shift_cust 和 st_shift_var,分别用于存储陀螺仪的标定偏移、自定义偏移和偏移变化
    float st_shift, st_shift_cust, st_shift_var;

	 // 从 I2C 设备中读取数据,并将其存储在 tmp 数组中。如果读取失败,返回错误代码 0x07
    if (i2c_read(st.hw->addr, 0x0D, 3, tmp))
        return 0x07;

	// 通过按位与操作,将读取的数据中的高三位清零,以获得有效的偏移码
    tmp[0] &= 0x1F;
    tmp[1] &= 0x1F;
    tmp[2] &= 0x1F;

		
    for (jj = 0; jj < 3; jj++) {
		// 强制跳过z轴检测
		if(2 == jj) break;
			
			
		// 计算了自定义偏移和偏移变化,并进行了自检。如果偏移变化超过了阈值,或者自定义偏移值超出了允许的范围,则将当前轴向的位设置到 result 变量中
        st_shift_cust = labs(bias_regular[jj] - bias_st[jj]) / 65536.f;
        if (tmp[jj]) {
            st_shift = 3275.f / test.gyro_sens;
			// 将 st_shift 初始化为一个计算值。然后根据偏移码逐次进行迭代,每次将 st_shift 乘以 1.046,以计算标定偏移值
            while (--tmp[jj])
                st_shift *= 1.046f;
            st_shift_var = st_shift_cust / st_shift - 1.f;
            if (fabs(st_shift_var) > test.max_gyro_var) {
                result |= 1 << jj;
					printf("gyro_self_test fabs(st_shift_var)[%d]=%f\r\n", jj, fabs(st_shift_var));
			}
        } else if ((st_shift_cust < test.min_dps) ||
            (st_shift_cust > test.max_dps)) {
            result |= 1 << jj;
					printf("st_shift_cust[%d]=%f\r\n", jj, st_shift_cust);
			}
    }
    return result;
}

可以发现我加了个z轴跳过的部分,大家可以忽略。那么我们来看一看都是什么,其实注释我也加上去了,简单来说就是计算2个偏移值,然后判断是否在限定的正常区间内,不在就会修改result,可以用于判断是那一块出的错,那么一共是3组数据的校验,xyz轴,其实到这里,已经可以定位到具体哪个部分不符合检查标准了,那么你可以通过修改检查区间来降低检查标准,当然可能修改了也仍然不行,那么就只好再进一步了。

我们这个自检的数据源自哪里?往上一级,来自get_st_biases函数。

// 此时传入的gyro accel
static int get_st_biases(long *gyro, long *accel, unsigned char hw_test)
{
    unsigned char data[MAX_PACKET_LENGTH];
    unsigned char packet_count, ii;
    unsigned short fifo_count;

    data[0] = 0x01;
    data[1] = 0;
    if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 2, data))
        return -1;
    delay_ms(200);
    data[0] = 0;
    if (i2c_write(st.hw->addr, st.reg->int_enable, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->i2c_mst, 1, data))
        return -1;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
		// 0x0c
    data[0] = BIT_FIFO_RST | BIT_DMP_RST;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
    delay_ms(15);
		// 0x01
    data[0] = st.test->reg_lpf;
    if (i2c_write(st.hw->addr, st.reg->lpf, 1, data))
        return -1;
		// 0x00
    data[0] = st.test->reg_rate_div;
    if (i2c_write(st.hw->addr, st.reg->rate_div, 1, data))
        return -1;
    // 此处在计算2个比较值的地方有所出入
		if (hw_test)
			  // 0xE0
        data[0] = st.test->reg_gyro_fsr | 0xE0;
    else
        data[0] = st.test->reg_gyro_fsr;
    if (i2c_write(st.hw->addr, st.reg->gyro_cfg, 1, data))
        return -1;

		// 此处在计算2个比较值的地方有所出入
    if (hw_test)
			  // 0XF8
        data[0] = st.test->reg_accel_fsr | 0xE0;
    else
        data[0] = test.reg_accel_fsr;
    if (i2c_write(st.hw->addr, st.reg->accel_cfg, 1, data))
        return -1;
		// 此处在计算2个比较值的地方有所出入
    if (hw_test)
        delay_ms(200);

    /* Fill FIFO for test.wait_ms milliseconds. */
		// 0x40
    data[0] = BIT_FIFO_EN;
    if (i2c_write(st.hw->addr, st.reg->user_ctrl, 1, data))
        return -1;
		
		// 0x78
    data[0] = INV_XYZ_GYRO | INV_XYZ_ACCEL;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;
    delay_ms(test.wait_ms);
    data[0] = 0;
    if (i2c_write(st.hw->addr, st.reg->fifo_en, 1, data))
        return -1;

		// data[0]=0x04
    if (i2c_read(st.hw->addr, st.reg->fifo_count_h, 2, data))
        return -1;

		// 0x0400
    fifo_count = (data[0] << 8) | data[1];
		// 0x55
    packet_count = fifo_count / MAX_PACKET_LENGTH;
    gyro[0] = gyro[1] = gyro[2] = 0;
    accel[0] = accel[1] = accel[2] = 0;
		
		printf("packet_count=%d\r\n", packet_count);

    for (ii = 0; ii < packet_count; ii++) {
        short accel_cur[3], gyro_cur[3];
	    // 读取寄存器地址st.reg->fifo_r_w数据到data
        if (i2c_read(st.hw->addr, st.reg->fifo_r_w, MAX_PACKET_LENGTH, data))
            return -1;
		// printf("data[2, 3]=%d, %d\r\n", data[2], data[3]);
		// printf("data[4, 5]=%d, %d\r\n", data[4], data[5]);
        accel_cur[0] = ((short)data[0] << 8) | data[1];
        accel_cur[1] = ((short)data[2] << 8) | data[3];
        accel_cur[2] = ((short)data[4] << 8) | data[5];
        accel[0] += (long)accel_cur[0];
        accel[1] += (long)accel_cur[1];
        accel[2] += (long)accel_cur[2];
        gyro_cur[0] = (((short)data[6] << 8) | data[7]);
        gyro_cur[1] = (((short)data[8] << 8) | data[9]);
        gyro_cur[2] = (((short)data[10] << 8) | data[11]);
        gyro[0] += (long)gyro_cur[0];
        gyro[1] += (long)gyro_cur[1];
        gyro[2] += (long)gyro_cur[2];
    }
#ifdef EMPL_NO_64BIT
    gyro[0] = (long)(((float)gyro[0]*65536.f) / test.gyro_sens / packet_count);
    gyro[1] = (long)(((float)gyro[1]*65536.f) / test.gyro_sens / packet_count);
    gyro[2] = (long)(((float)gyro[2]*65536.f) / test.gyro_sens / packet_count);
    if (has_accel) {
        accel[0] = (long)(((float)accel[0]*65536.f) / test.accel_sens /
            packet_count);
        accel[1] = (long)(((float)accel[1]*65536.f) / test.accel_sens /
            packet_count);
        accel[2] = (long)(((float)accel[2]*65536.f) / test.accel_sens /
            packet_count);
        /* Don't remove gravity! */
        accel[2] -= 65536L;
    }
#else
		
	/*
	printf("******* before\r\n");
	printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
	printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
	printf("*******\r\n");
	*/
		
    gyro[0] = (long)(((long long)gyro[0]<<16) / test.gyro_sens / packet_count);
    gyro[1] = (long)(((long long)gyro[1]<<16) / test.gyro_sens / packet_count);
    gyro[2] = (long)(((long long)gyro[2]<<16) / test.gyro_sens / packet_count);
    accel[0] = (long)(((long long)accel[0]<<16) / test.accel_sens /
        packet_count);
    accel[1] = (long)(((long long)accel[1]<<16) / test.accel_sens /
        packet_count);
    accel[2] = (long)(((long long)accel[2]<<16) / test.accel_sens /
        packet_count);
    /* Don't remove gravity! */
		printf("******* after\r\n");
		printf("accel=%ld, %ld, %ld\r\n", accel[0], accel[1], accel[2]);
		printf("gyro=%ld, %ld, %ld\r\n", gyro[0], gyro[1], gyro[2]);
		printf("*******\r\n");
		
    if (accel[2] > 0L)
        accel[2] -= 65536L;
    else
        accel[2] += 65536L;
#endif

    return 0;
}

看到这里,其实数据来源就是通过i2c从模块寄存器中读取数据。那么也将到达我们的尾声了,对应的寄存器是什么内容。参考资料:MPU-6000 & MPU-6050 寄存器表及其描述(中文版).pdf 然后层层上推,找到4.18 S REGISTERS 9 59 TO 4 64 – – ACCELEROMETER MEASUREMENTS

MPU6050使用心得(简单分享一下)_倾斜检测_06


MPU6050使用心得(简单分享一下)_传感器_07


那么此时,通过前面我追加的打印,和我当时模块输出的值,其加速度的第三组数据,也就是Z轴的值不会发现变化,无论我怎么摆放,怎么晃动。其通过计算后,结果为0,然后导致result不为0,最终导致自检永远无法通过,不过强制跳过z轴校验后,其横滚角和俯仰角也勉强看着能用(勉强)。

那么到这,我的判断就是模块损坏导致的这个问题,之后更换模块后,轻松秒杀。。。

既然模块算是测试通过了,我们再深入下,看看 倾斜检测,其实说是模块需要水平静止进行自检,实际上不是水平也可以自检通过,基本上只要静止不动就行,某些角度可能会自检失败,主要问题仍然是加速度z轴这块。不过问题不大,那么这就在最后收一个伏笔,在main中获取到3个角度后,做个简单的区间限定,就能判断是否超出了限定的倾斜阈值了,但是使用中我发现,自检中,Roll横滚角,会有问题!

当你芯片的法线是朝向水平线往上的(朝向天花板),Roll自检后是从0度开始。
当你芯片的法线是朝向水平线往下的(朝向地面),Roll自检后是从-179.9度开始。
坑点出现了,0度左右偏移后是 -1,1。-179.9度左右偏移后是-179,179,另外朝向地面的情况Roll计算会不怎么正常!!!
那么我们最开始限定的区间判断角度在2种自检情况下,就不再适配了。
那么在我们无法限定模块大致朝向的情况下,这个问题影响很大。
那我给出的方案就是,在自检后,通过判断初始的Roll是靠近0 还是 靠近 -180,从而可以得出芯片的朝向,然后适配2种算法做实现,但是仅使用于完全平行地面朝下,斜朝下的话,Roll值读取出来不太正常,整体的角度比例会失调!!!

ps:芯片刚自检完,数据还会波动一会,需要等待一段时间,稳定后,才能正常使用。(尤其是在芯片朝向地面的情况)

需要注意的问题点:
偏航角(yaw)零飘:MPU6050常见问题的分析与处理 万向节锁:欧拉角、万向节死锁理解


标签:gyro,accel,MPU6050,st,data,test,分享,心得,result
From: https://blog.51cto.com/ikaros/8296154

相关文章

  • 【专题】2023智能汽车发展趋势洞察报告PDF合集分享(附原数据表)
    原文链接:https://tecdat.cn/?p=34219原文出处:拓端数据部落公众号至2025年,智能网联汽车产业规模将突破5000亿。预计具备L2及以上自动驾驶能力的车型销量将突破千万级,渗透率将跃升至42.9%。阅读原文,获取专题报告合集全文,解锁文末56份智能汽车相关行业研究报告。智能汽车发展水平......
  • 【专题】2022-2023中国跨境出口B2C电商报告PDF合集分享(附原数据表)
    报告链接:http://tecdat.cn/?p=32805原文出处:拓端数据部落公众号全球疫情的爆发对于全球经济和消费市场都带来了很大的冲击,特别是在消费者的消费行为和零售市场格局方面发生了重大变革。同时由于全球供应链的重新调整,产业分化现象也加速出现。中国跨境电商已经历了十年以上的发......
  • 数据库设计心得
    我们的项目是基于隐私包含的众包系统。在设计之前,我们先把需求搞清楚。业务需求雇主能发布需求,工人能接单,完成任务。哈哈,听上去还挺简单的,但涉及到雇主取消需求,雇主取消任务,工人根据标签查询需求,工人提交工作文件,工人取消订单这些功能。初步设计确定了工人,雇主,管理员,需求,订单,......
  • 开发自动类软件的经验分享!
    随着科技的不断发展,自动类软件已经成为各行各业不可或缺的一部分,自动类软件可以大大提高工作效率,减少人工干预,避免人为错误,为企业和个人带来更多的便利和效益,本文将分享一些开发自动类软件的经验,帮助大家更好地了解这个领域。一、确定需求和目标在开发自动类软件之前,需要明确软件的......
  • 游戏类软件开发常用代码分享!
    随着科技的迅速发展,游戏软件开发已经成为当今最热门的行业之一,许多开发者和游戏爱好者都渴望了解游戏开发的底层技术和常用的代码,本文将分享游戏类软件开发中一些常见的代码和相关技术,帮助大家更好地了解这个领域。一、游戏引擎与开发工具游戏引擎是游戏开发的核心,它为开发者提供了......
  • 硝烟后的茶歇 | 中睿天下谈攻防演练之邮件攻击溯源实战分享
    近日,由中国信息协会信息安全专业委员会、深圳市CIO协会、PCSA安全能力者联盟主办的《硝烟后的茶歇·广东站》主题故事会在深圳成功召开。活动已连续举办四年四期,共性智慧逐步形成《年度红蓝攻防系列全景图》、《三化六防“挂图作战”》等共性研究重要成果。此次会议邀请到了政府、......
  • 队内pwn训练营资料分享
    目录队内pwn训练营资料分享前言培训材料写在最后队内pwn训练营资料分享分享队内第一期pwn训练营的资料。前言为了提升队内师傅的pwn技巧,之前在内部举办了一次为期约两个月的pwn训练营。从结果来看,培训后师傅们的能力有着显著的提升,此次训练营取得了预期的效果。本着......
  • 文件存储引擎模块封装和使用分享
    背景需求在项目开发过程中,经常会使用到文件存储相关的功能,如:存储发票文件提供发票下载地址……调研诸如此类的功能就需要使用到本地存储或云服务商提供的存储功能。当然,这对于开发高手的zone来说都是小意思,上网一查,对象存储哪家强?​​第一位赫然显示了百家号创作者......
  • 文件存储引擎模块封装和使用分享
    背景需求在项目开发过程中,经常会使用到文件存储相关的功能,如:存储发票文件提供发票下载地址……调研诸如此类的功能就需要使用到本地存储或云服务商提供的存储功能。当然,这对于开发高手的zone来说都是小意思,上网一查,对象存储哪家强?​​第一位赫然显示了百家号创作者......
  • 2023码尚教育接口自动化框架的实现源码分享pytest+allure+jenkins几乎零代码少量的代
    本框架适合对Pytest有大概认识(比如看完X站3天课程的水平),职场新人没有雄厚的资本去参加专业的培训、功能测试转自动化测试、开发转测试的小伙伴们。首先介绍下这个框架的使用,看看是不是你所需要的。第一步、添加模块PY文件,编写接口代码。classTestJuhe: #upwei:fanfanzb2023......