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

news2024/12/23 5:49:25

前言

选用MPU6050做 倾斜检测 功能。

前期准备

开发板:正点原子STM32F103 精英版(STM32F103ZET6)
在这里插入图片描述

模块:GY-521 MPU6050
在这里插入图片描述

其他:杜邦线若干、烧录线、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

在这里插入图片描述

ATK-MPU6050

这边的AUX_CL、AUX_DA就没接
在这里插入图片描述

引脚描述

源自:姿态传感器——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航向角可以不做使用,也就不影响了。
在这里插入图片描述

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

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

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

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

当你芯片的法线是朝向水平线往上的(朝向天花板),Roll自检后是从0度开始。
当你芯片的法线是朝向水平线往下的(朝向地面),Roll自检后是从-179.9度开始。
坑点出现了,0度左右偏移后是 -1,1。-179.9度左右偏移后是-179,179!!!
那么我们最开始限定的区间判断角度在2种自检情况下,就不再适配了。
那么在我们无法限定模块大致朝向的情况下,这个问题影响很大。
那我给出的方案就是,在自检后,通过判断初始的Roll是靠近0 还是 靠近 -180,从而可以得出芯片的朝向,然后适配2种算法做实现。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/911154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧农场云养猪平台原来是这样的!

随着数字化和智能化的发展&#xff0c;农业行业也逐渐开始融入互联网技术&#xff0c;其中云养猪平台作为新兴的农业数字化解决方案之一&#xff0c;备受关注。本文将探讨如何开发一款具备专业、思考深度和逻辑性的云养猪平台。 一、前期准备阶段&#xff1a; 1.明确目…

通达信指标公式12:关于股票筹码集中度的分析弊端

筹码的集中度&#xff1a;指的是个股的流通筹码被庄家控制的程度&#xff0c;用来观察一支股票是否有大资金吸筹。如果某只股票流通筹码的30%被庄家所控制&#xff0c;就说明这只股票被轻度控盘了。而当大于50%以上的多数流通筹码被控制的时候&#xff0c;就是高度控盘了。利用…

Python实现SSA智能麻雀搜索算法优化卷积神经网络回归模型(CNN回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新型的群智能优化算法&#xff0c;在2020年提出&a…

38岁啦:说点什么好呢?

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 1985年的8月21日&#xff0c;农历七月初六。正好今天也是2023年8月21日&#xff0c;农历七月初六。该说点什么好呢? 1985年&#xff0c;我就出生在这里&#xff0c;箭头所指的方向就是我出生的地…

Idea 设置实体类里面的serialVersionUID

private static final long serialVersionUID 8728382276986043874L;选中serialVersionUID &#xff0c;然后使用Altenter&#xff0c;选择随机更改serialVersionUID 的初始值

温故知新之:接口和抽象类有什么区别?

本文以下内容基于 JDK 8 版本。 1、接口介绍 接口是 Java 语言中的一个抽象类型&#xff0c;用于定义对象的公共行为。它的创建关键字是 interface&#xff0c;在接口的实现中可以定义方法和常量&#xff0c;其普通方法是不能有具体的代码实现的&#xff0c;而在 JDK 8 之后&…

南京医科大学采购ZJ-3精密压电测试仪和PZT-JH30/3型复合压电极化装置

南京医科大学采购ZJ-3精密压电测试仪和PZT-JH30/3型复合压电极化装置 南京医科大学&#xff08;Nanjing Medical University&#xff09;&#xff0c;位于江苏省南京市&#xff0c;是教育部、中华人民共和国国家卫生健康委员会、江苏省人民政府共建高校和江苏高水平大学建设高校…

长胜证券:怎么看k线图?

K线图是股票、期货、外汇等金融商场中常用的一种图表方式&#xff0c;用来展示必定时刻内的价格走势。关于投资者来说&#xff0c;学会怎么正确地剖析K线图是非常重要的。本文将从多个视点来剖析怎么看K线图&#xff0c;协助投资者更好地把握商场走势和做出正确的买卖决议计划。…

Java:集合LinkedSet底层原理、TreeSet底层原理;开发中如何选择集合使用

LinkedSet底层原理 TreeSet底层原理 如果你想对自定义类型的对象进行排序&#xff0c;有两种方式 但是执行出来&#xff0c;如果两个比较的数据相等&#xff0c;就会造成一个数据丢失 第二种

天翼物联、汕头电信与汕头大学共建新一代信息技术与数字创新(物联网)联合实验室

近日&#xff0c;在工业和信息化部和广东省人民政府共同主办的2023中国数字经济创新发展大会上&#xff0c;天翼物联、汕头电信与汕头大学共建“新一代信息技术与数字创新&#xff08;物联网&#xff09;”联合实验室签约仪式举行。汕头大学校长郝志峰、中国电信广东公司总经理…

ssm物流管理系统源码和论文

ssm物流管理系统设计与实现037 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 1&#xff0e;选题的意义与目的、文献综述与研究现状 随着我国经济的快速发展&#xff0c;以及信息化步伐的快&#xff0c;物流企…

使用GEWE框架进行微信个人资料管理、批量管理

友情链接 geweapi.com 点击即可访问 更新个人资料 请求URL&#xff1a; http://域名地址/api/personal/updateprofile 请求方式&#xff1a; POST 请求头&#xff1a; Content-Type&#xff1a;application/json X-GEWE-TOKEN: 后台获取 参数&#xff1a; 参数名必填…

【正点原子STM32连载】第十八章 通用定时器PWM输出实验 摘自【正点原子】APM32F407最小系统板使用指南

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html# 第十…

【附安装包】Solid Edge2023安装教程最强CAD选择

软件下载 软件&#xff1a;Solid Edge版本&#xff1a;2023语言&#xff1a;简体中文大小&#xff1a;3.85G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.bai…

Python 如何复制列表,高效复制列表,列表复制效率对比,copy 与 deepcopy 差距对比

背景 在python中&#xff0c;避免不了经常使用 list 类型的变量&#xff0c;list 变量的复制也是经常遇到的需求&#xff0c;那么如何高效的复制一个 list呢&#xff1f; 代码 下面代码给出了 4 种不同的list复现方法&#xff0c;观察其代码效率&#xff1a; # -*- coding:…

Vue3 中引入液晶数字字体(通常用于大屏设计)

一、下载 .ttf 字体文件到本地&#xff0c;放在 src 中的 assets 文件下 下载液晶字体 DS-Digital.ttf 二、在 css 文件中引入字体 /* src/assets/fonts/dsfont.css */ font-face {font-family: electronicFont;src: url(./DS-Digital.ttf);font-weight: normal;font-styl…

CS144 计算机网络 Lab2:TCP Receiver

前言 Lab1 中我们使用双端队列实现了字节流重组器&#xff0c;可以将无序到达的数据重组为有序的字节流。Lab2 将在此基础上实现 TCP Receiver&#xff0c;在收到报文段之后将数据写入重组器中&#xff0c;并回复发送方。 实验要求 TCP 接收方除了将收到的数据写入重组器中外…

记录:ubuntu20.04+ORB_SLAM2_with_pointcloud_map+ROS noetic

由于相机实时在线运行需要ROS&#xff0c;但Ubuntu22.04只支持ROS2&#xff0c;于是重装Ubuntu20.04。上一篇文章跑通的是官方版本的ORB_SLAM2&#xff0c;不支持点云显示。高翔修改版本支持RGB-D相机的点云显示功能。 高翔修改版本ORB_SLAM2&#xff1a;https://github.com/ga…

vue中css修改滚动条样式

vue中css修改滚动条样式 效果图&#xff1a; 代码(在app.vue中全局增加下面样式即可)&#xff1a; &::-webkit-scrollbar {width: 8px;height: 8px;border-radius: 3px;}/*定义滚动条轨道 内阴影圆角*/&::-webkit-scrollbar-track {//-webkit-box-shadow: inset 0 0 …

服务器的介绍

1.服务器概述 1.1 服务器的基本概念 服务器是计算机的一种&#xff0c;是网络中为客户端计算机提供各种服务的高性能计算机&#xff1b; 服务器在网络操作系统的控制下&#xff0c;将与其相连的硬盘、磁带、 打印机及昂贵的专用通讯设备提供给网络上的客户站点共享&#xf…