精华内容
下载资源
问答
  • MPU6050_Streaming 将MPU 6050数据流式传输到Web浏览器 从NodeMCU将MPU 6050陀螺仪和加速度计数据流传输到网页进行绘图的示例。 使用UDP发送数据,并且WebSite使用SSE实时捕获和绘制数据图形。
  • 由于wiringPI读取MPU6050数据比较慢,找到了一个满足树莓派MPU6050数据读取200HZ的demo
  • MPU9250,MPU6050数据手册资料,英文版,详细介绍了MPU9250的各项参数和使用方法
  • STM32 MPU6050数据获取、数据处理

    千次阅读 多人点赞 2020-08-20 22:56:33
    2.4 STM32 MPU6050数据获取(IIC + DMP) 本篇文章主要针对廉价的MPU6050模块。我们这里完成了MPU6050的数据获取、零偏自动设置、温漂抑制。这里提供源码工程文件,供大家下载,在公众号:小白学移动机器人,发送:...

    2.4 STM32 MPU6050数据获取(IIC + DMP)

    本篇文章主要针对廉价的MPU6050模块。我们这里完成了MPU6050的数据获取、零偏自动设置、零漂抑制。这里提供源码工程文件,供大家下载,在公众号:小白学移动机器人,发送:MPU6050,即可获得

    2.4.1 解决的问题

    DMP库的移植(文件已被更改过,更好的移植)

    MPU6050数据的获取(通过DMP获取的四元数,做姿态解算)

    零偏自动校准(实现DMP方式上电既是0角度)

    有效的零漂抑制(针对yaw值无法滤波的情况,使用特殊的方法,实现原本4分钟漂1度,到30分钟漂一度的改变)

    2.4.2 实现工具

    STM32F103单片机、GY521六轴模块、keil5

    img

    具体线路连接:

    这里MCU使用模拟IIC的方式和GY521连接在一起,具体线路连接如下:
    GY521                  MCU
    VCC    ----------->    VCC         5v
    GND    ----------->    GND
    SCL    ----------->    SCL         模拟SCL的引脚
    SDA    ----------->    SDA		   模拟SDA的引脚
    XDA    ----------->    不接         外接地磁计SDA,这里不接
    XCL    ----------->    不接		   外接地磁计SCL,这里不接
    AD0    ----------->    不接         
    INT    ----------->    PA12        外部中断使用,这里使用PA12,可以做更改
    

    2.4.3 GY521

    MPU-60X0是世界上第一款集成 6 轴MotionTracking设备。它集成了3轴MEMS陀螺仪,3轴MEMS加速度计,以及一个可扩展的数字运动处理器 DMP( DigitalMotion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。

    关于MPU6050的工作原理、以及重要寄存器这里就不做介绍了。网上很多大神都介绍的很清楚,大家可以浏览一下。

    参考点击查看

    就算不看也不影响大家解决上面的问题。

    2.4.4 DMP库的移植

    主要就是移植下面的几个文件,文件会和源码工程文件一起发给大家,网上自己下载的需要更改,这里给的已经更改好了。

    image-20200820212209728

    移植步骤:

    (1)将存放上面文件的DMP文件夹复制到STM32工程文件的HAREWARE(存放配置文件的目录,不同的工程可能名字不一样)文件夹下

    image-20200820213355895

    (2)设置路径(按序号依次点击)

    image-20200820214133693
    关于IIC的文件和MPU6050的文件,按照步骤(1)(2)同样操作既可。确保都完成路径添加。

    image-20200820214506741

    (3)引入文件

    image-20200820214757231
    image-20200820215058980

    IIC和MPU6050的文件也是上面同样的操作,确保所有的文件都被引入如下图所示

    image-20200820215335583

    (4)到此,DMP移植的相关文件都完成了,下面就是引用头文件开始使用了。

    2.4.5 MPU6050硬件初始化

    (1)首先,主函数引入头文件

    image-20200820215916175

    (2)初始化

    image-20200820220022712

    2.4.6 IIC初始化

    IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。

    设备上的串行数据线SDA接口电路应该是双向的,输出电路用于向总线上发送数据,输入电路用于接收总线上的数据。而串行时钟线也应是双向的,作为控制总线数据传送的主机,一方面要通过SCL输出电路发送时钟信号,另一方面还要检测总线上的SCL电平,以决定什么时候发送下一个时钟脉冲电平;作为接受主机命令的从机,要按总线上的SCL信号发出或接收SDA上的信号,也可以向SCL线发出低电平信号以延长总线时钟信号周期。

    总线空闲时,因各设备都是开漏输出,上拉电阻Rp使SDA和SCL线都保持高电平。任一设备输出的低电平都将使相应的总线信号线变低,也就是说:各设备的SDA是“与”关系,SCL也是“与”关系。

    IIC.c中部分
        
    /**************************实现函数********************************************
    *函数原型:		void IIC_Init(void)
    *功  能:		初始化I2C对应的接口引脚。如果不能使用这两个引脚可以自行更改
    *******************************************************************************/
    void IIC_Init(void)
    {			
    	GPIO_InitTypeDef GPIO_InitStructure;
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);   //使能PB端口时钟
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;	//端口配置
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        //推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       //50M
    	GPIO_Init(GPIOB, &GPIO_InitStructure);					//根据设定参数初始化GPIOB 
    }
    
    IIC.h中部分
    
    //IO方向设置
    #define SDA_IN()  {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;}
    #define SDA_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<4;}
    
    //IO操作函数	 
    #define IIC_SCL    PBout(8) //SCL
    #define IIC_SDA    PBout(9) //SDA	 
    #define READ_SDA   PBin(9)  //输入SDA 
    

    其他的IIC读取和写入函数在例程中已完成,我们主要的工作是将任意两端口配置为IIC的SDA和SCL.

    2.4.7 MPU6050初始化

    /**************************实现函数********************************************
    *函数原型:		void MPU6050_initialize(void)
    *功  能:	    初始化 	MPU6050 以进入可用状态。
    *******************************************************************************/
    void MPU6050_initialize(void) {
    	MPU6050_setClockSource(MPU6050_CLOCK_PLL_YGYRO);    //设置时钟
    	MPU6050_setFullScaleGyroRange(MPU6050_GYRO_FS_2000);//陀螺仪最大量程 +-2000度每秒
    	MPU6050_setFullScaleAccelRange(MPU6050_ACCEL_FS_2);	//加速度度最大量程 +-2G
    	MPU6050_setSleepEnabled(0);                         //进入工作状态
    	MPU6050_setI2CMasterModeEnabled(0);	                //不让MPU6050 控制AUXI2C
    	MPU6050_setI2CBypassEnabled(0);	                    //主控制器的I2C与MPU6050的AUXI2C	直通。控制器可以直接访问HMC5883L
    }
    

    2.4.8 DMP初始化

    /**************************************************************************
    函数功能:MPU6050内置DMP的初始化
    入口参数:无
    返回  值:无
    **************************************************************************/
    void DMP_Init(void)
    { 
    	u8 temp[1]={0};
    	i2cRead(0x68,0x75,1,temp);
    	
    	printf("mpu_set_sensor complete ......\r\n");
    	if(temp[0]!=0x68)NVIC_SystemReset();
    	if(!mpu_init())
    	{
    		if(!mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL))
    			printf("mpu_set_sensor complete ......\r\n");
    		if(!mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL))
    			printf("mpu_configure_fifo complete ......\r\n");
    		if(!mpu_set_sample_rate(DEFAULT_MPU_HZ))
    			printf("mpu_set_sample_rate complete ......\r\n");
    		if(!dmp_load_motion_driver_firmware())
    			printf("dmp_load_motion_driver_firmware complete ......\r\n");
    		if(!dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation)))
    			printf("dmp_set_orientation complete ......\r\n");
    		if(!dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
    		DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL | DMP_FEATURE_SEND_CAL_GYRO |
    		DMP_FEATURE_GYRO_CAL))
    			printf("dmp_enable_feature complete ......\r\n");
    		if(!dmp_set_fifo_rate(DEFAULT_MPU_HZ))
    			printf("dmp_set_fifo_rate complete ......\r\n");
    		run_self_test();                                          //该函数决定了是否设置起始零偏
    		if(!mpu_set_dmp_state(1))
    			printf("mpu_set_dmp_state complete ......\r\n");
    	}
    }
    

    此函数主要是对DMP进行初始化,如果想要了解每个函数具体是初始化的什么内容,请看printf函数输出的内容即可。

    我们主要讲陀螺仪是如何进行开机校准,或者设置开机不校准。将目光放到run_self_test();这个函数,这是用来设置校准的函数.

    static void run_self_test(void)
    {
        int result;
        long gyro[3], accel[3];
    
        result = mpu_run_self_test(gyro, accel);
        if (result == 0x03) {                   //返回0x03为MPU6050六轴,只要通过该if语句,就可以实现零偏自动校准
            /* 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);			//根据读取的状态进行校准
    		printf("setting bias succesfully ......\r\n");
        }
    }
    

    这样的话,mpu6050校准的过程就明确了.

    如果不需要开机校准,可以是if(result==0x03)这个条件不满足,不进入处理,或者将陀螺仪 加速度计的基准设置函数set_bias不执行即可.

    如果需要开机校准,保证整个校准流程正常执行就行.

    2.4.9 MPU6050数据获取以及零漂抑制

    零漂随时间近似线性,不做处理的时候,大概3-4分钟yaw就飘1度。根据采集一段时间数据,计算单位时间内yaw漂移的量,根据时间变化,将对应的漂移量减掉,这样我们就可以得到,半小时只偏差1度的效果,这样已经满足大部分场景了。

    (1) main.c

    
    /*===================================================================
    程序功能:MPU6050 DMP数据读取
    程序编写:公众号:小白学移动机器人
    其他    :如果对代码有任何疑问,可以私信小编,一定会回复的。
    =====================================================================
    ------------------关注公众号,获得更多有趣的分享---------------------
    ===================================================================*/
    
    int main(void)
    { 
    
    	GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
    	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD
    	
    	MY_NVIC_PriorityGroupConfig(2);	//=====设置中断分组
    	
    	delay_init();	    	        //=====延时函数初始化
    	LED_Init();                     //=====LED初始化    程序灯
    	usart3_init(9600);              //=====串口3初始化  蓝牙
    
        IIC_Init();                     //=====IIC初始化    读取MPU6050数据
    	MPU6050_initialize();           //=====MPU6050初始化	
    	DMP_Init();                     //=====初始化DMP 
    	
    	MBOT_EXTI_Init();               //=====MPU6050 5ms定时中断初始化
    	
    	
    	while(1)
    	{
    		getAngle(&yaw,&yaw_acc_error); //数据获取,包含去除温漂 
    		
    		printf("%d\r\n",(int)yaw);
    	} 
    }
    

    (2) 外部中断,根据时间累积零漂

    #include "control.h"	
    #include "led.h"
    
    float yaw              =0;           //转向陀螺仪
    float yaw_acc_error    =0;           //yaw累积误差
    #define FIVE_MS_ERROR   0.00002115   //yaw每5ms的向上漂移的度数,这里近似线性,可以做到半小时偏1度,每个人的这个值可能有所不同,可以自行计算
    
    /**************************************************************************
    函数功能:所有的控制代码都在这里面
              5ms定时中断由MPU6050的INT引脚触发		
    **************************************************************************/
    void EXTI15_10_IRQHandler(void) 
    {                                                         
    	EXTI_ClearITPendingBit(EXTI_Line12);                            //===清除LINE12线路挂起位
    	
    	yaw_acc_error += FIVE_MS_ERROR;								    //===yaw漂移误差累加
    	
    	Led_Flash(200);                                                 //===LED闪烁,证明程序正常运行	
    		
    } 
    

    (3)getAngle函数

    /**************************************************************************
    函数功能:获取角度 0-359
    入口参数:无
    返回  值:无
    **************************************************************************/
    void getAngle(float *yaw,float *yaw_acc_error)
    {
    	Read_DMP();                   //===读取Yaw(-180 - 179)
    	
    	if(Yaw < 0)
    		Yaw = Yaw + 360;
    	*yaw = Yaw;                   //===转换yaw(   0 - 359)
    	
    	*yaw = *yaw - *yaw_acc_error; //===减去yaw随时间的向上漂移
    	
    	if(*yaw < 0)
    		*yaw = *yaw+360;
    }
    

    2.4.10 总结

    本篇文章基本上实现了(IIC+DMP+零偏校准+零漂大幅度抑制),对于ROS小车yaw的精度我们已经实现了。到这里,我们就已经完成了,ROS小车底层的大部分代码,下面剩下STM32与ROS通信。

    参考:

    MPU6050 6轴陀螺仪的使用与校准:点击查看

    MEMS陀螺仪如何进行信号温漂补偿:点击查看

    系列文章

    以往链接,点击访问。

    上一篇STM32电机PID速度控制
    下一篇stm32和ros的串口通信
    系列文章从零搭建ROS机器人

    如果你感觉,我的文章比较适合你,关注我,点个赞,给你不一样的惊喜。
    在这里插入图片描述

    展开全文
  • 包含了正点原子全系STM32串口发送MPU6050数据的程序。
  • ZigBee采集MPU6050数据

    2019-12-05 16:32:43
    网上看了不少MPU6050的教程,就是没有人具体讲讲CC2530在ZigBee下传MPU6050数据的方法,好不容易找到了有人写CC2530在ZigBee下传MPU6050的博客,求博主发代码借鉴参考,人家又不愿意给。 诶,多大点事,用得着这么...

    网上看了不少MPU6050的教程,就是没有人具体讲讲CC2530在ZigBee下传MPU6050数据的方法,好不容易找到了有人写CC2530在ZigBee下传MPU6050的博客,求博主发代码借鉴参考,人家又不愿意给。
    诶,多大点事,用得着这么珍惜吗?
    于是自己摸索着也弄出怎么用MPU6050采集原始数据,通过ZigBee的终端设备(EndDevice)无线传输给协调器(Coordinator)。在这里就分享一下,给曾经与我同样迷惑的伙伴们提供一个参考。

    我用的是Z-Stack2.5.1a的协议栈,建议大家学ZigBee用这个协议栈的时候,能先用GenericApp这个模板把Coordinator和EndDevice的程序分开写,这样辨识度高好理解。然后用熟了GenericApp模板写代码之后,再用SampleApp的模板写代码。
    GenericApp和SampleApp的区别可以自己上官网看介绍,对于我而言,SampleApp更好一些。因为之前试过Coordinator断电重启之后,基于GenericApp模板写的代码不能再跟EndDevice重连上,只能把EndDevice也断电重启或复位才能彼此重连。而用SampleApp模板写的代码,无论是EndDevice断电重启还是Coordinator断电重启,都能顺利重连上另外一方。
    但这篇我只讲用GenericApp模板写的代码。

    若编译过程中出现以下…has no prototype的报错
    在这里插入图片描述
    并不是代码的问题,只需要选中工程,右键点击Option…,然后去掉以下勾选项即可
    在这里插入图片描述
    编译下载,打开串口调试助手,看到的数据如下:
    在这里插入图片描述
    这就是MPU6050采集的原始数据ax,ay,az,gx,gy,gz。

    附上一张代码图,代码量不大。主要是对IIC的理解运用。在EndDevice.c里调用时需要先对IIC和MPU6050进行初始化。
    在这里插入图片描述

    ZigBee+MPU6050代码下载

    展开全文
  • dmp读取mpu6050数据

    2014-08-14 20:16:04
    dmp读取mpu6050数据,经过四元数输出稳定的角度,本人亲测可用
  • 本程序是C#程序,通过串口和单片机连接,并且读入单片机传来的MPU6050数据,主要有加速度,角速度三个维度的值和三个方向角。然后将得到的数据绘制成图形。还可以进行室内定位里的步态分析,然后进行快速傅里叶变换...
  • MPU6050数据分析与滤波

    2015-03-27 23:39:42
    MPU6050数据分析与滤波,九轴加速度传感器,平衡控制。
  • MPU6050数据采集

    2018-06-01 17:29:31
    基于51单片机平台,主要采集陀螺仪MPU6050相关数据,适合初学者学习。
  • 基于KEA128单片机的MPU6050数据读取,并进行数据滤波,解算姿态
  • MPU6050数据上传至匿名上位机,可观察数据量,并可以通过数据波形来分析数据的变化。亲测可用!
  • MPU6050数据采集程序

    2018-04-12 21:46:13
    本程序是STM32单片机通过I2C和MPU6050通信,获取MPU6050数据,包括加速度和角速度三个维度的值。然后利用MPU6050自带的dmp处理得到的加速度和角速度,计算出航偏角,滚动角,俯仰角,可以作为自动控制的参数。本...
  • MPU6050数据处理分析

    2019-05-01 11:31:17
    MPU6050数据处理分析,转载方便查看 https://zhuanlan.zhihu.com/p/20082486

    MPU6050数据处理分析,转载方便查看

    https://zhuanlan.zhihu.com/p/20082486

    关注专栏写文章
    Arduino教程:MPU6050的数据获取、分析与处理

    Arduino教程:MPU6050的数据获取、分析与处理

    428 人赞同了该文章

    摘要

    MPU6050是一种非常流行的空间运动传感器芯片,可以获取器件当前的三个加速度分量和三个旋转角速度。由于其体积小巧,功能强大,精度较高,不仅被广泛应用于工业,同时也是航模爱好者的神器,被安装在各类飞行器上驰骋蓝天。

    随着Arduino开发板的普及,许多朋友希望能够自己制作基于MPU6050的控制系统,但由于缺乏专业知识而难以上手。此外,MPU6050的数据是有较大噪音的,若不进行滤波会对整个控制系统的精准确带来严重影响。

    MPU6050芯片内自带了一个数据处理子模块DMP,已经内置了滤波算法,在许多应用中使用DMP输出的数据已经能够很好的满足要求。关于如何获取DMP的输出数据,我将在以后的文章中介绍。本文将直接面对原始测量数据,从连线、芯片通信开始一步一步教你如何利用Arduino获取MPU6050的数据并进行卡尔曼滤波,最终获得稳定的系统运动状态。

    一、Arduino与MPU-6050的通信

    为避免纠缠于电路细节,我们直接使用集成的MPU6050模块。MPU6050的数据接口用的是I2C总线协议,因此我们需要Wire程序库的帮助来实现Arduino与MPU6050之间的通信。请先确认你的Arduino编程环境中已安装Wire库。

    Wire库的官方文档(arduino.cc/en/Reference)中指出:在UNO板子上,SDA接口对应的是A4引脚,SCL对应的是A5引脚。MPU6050需要5V的电源,可由UNO板直接供电。按照下图连线。

    (紫色线是中断线,这里用不到,可以不接)


    MPU6050的数据写入和读出均通过其芯片内部的寄存器实现,这些寄存器的地址都是1个字节,也就是8位的寻址空间,其寄存器的详细列表说明书请点击下载:olimex.com/Products/Mod

    1.1 将数据写入MPU-6050

    在每次向器件写入数据前要先打开Wire的传输模式,并指定器件的总线地址,MPU6050的总线地址是0x68(AD0引脚为高电平时地址为0x69)。然后写入一个字节的寄存器起始地址,再写入任意长度的数据。这些数据将被连续地写入到指定的起始地址中,超过当前寄存器长度的将写入到后面地址的寄存器中。写入完成后关闭Wire的传输模式。下面的示例代码是向MPU6050的0x6B寄存器写入一个字节0。

    Wire.beginTransmission(0x68); //开启MPU6050的传输
    Wire.write(0x6B); //指定寄存器地址
    Wire.write(0); //写入一个字节的数据
    Wire.endTransmission(true); //结束传输,true表示释放总线
    1.2 从MPU-6050读出数据

    读出和写入一样,要先打开Wire的传输模式,然后写一个字节的寄存器起始地址。接下来将指定地址的数据读到Wire库的缓存中,并关闭传输模式。最后从缓存中读取数据。下面的示例代码是从MPU6050的0x3B寄存器开始读取2个字节的数据:

    Wire.beginTransmission(0x68); //开启MPU6050的传输
    Wire.write(0x3B); //指定寄存器地址
    Wire.requestFrom(0x68, 2, true); //将输据读出到缓存
    Wire.endTransmission(true); //关闭传输模式
    int val = Wire.read() << 8 | Wire.read(); //两个字节组成一个16位整数
    

    1.3 具体实现

    通常应当在setup函数中对Wire库进行初始化:

    Wire.begin(); 

    在对MPU6050进行各项操作前,必须启动该器件,向它的0x6B写入一个字节0即可启动。通常也是在setup函数完成,代码见1.1节。

    二、 MPU6050的数据格式

    我们感兴趣的数据位于0x3B到0x48这14个字节的寄存器中。这些数据会被动态更新,更新频率最高可达1000HZ。下面列出相关寄存器的地址,数据的名称。注意,每个数据都是2个字节。

    • 0x3B,加速度计的X轴分量ACC_X
    • 0x3D,加速度计的Y轴分量ACC_Y
    • 0x3F,加速度计的Z轴分量ACC_Z
    • 0x41,当前温度TEMP
    • 0x43,绕X轴旋转的角速度GYR_X
    • 0x45,绕Y轴旋转的角速度GYR_Y
    • 0x47,绕Z轴旋转的角速度GYR_Z

    MPU6050芯片的座标系是这样定义的:令芯片表面朝向自己,将其表面文字转至正确角度,此时,以芯片内部中心为原点,水平向右的为X轴,竖直向上的为Y轴,指向自己的为Z轴。见下图:

    我们只关心加速度计和角速度计数据的含义,下面分别介绍。

    2.1 加速度计

    加速度计的三轴分量ACC_X、ACC_Y和ACC_Z均为16位有符号整数,分别表示器件在三个轴向上的加速度,取负值时加速度沿座标轴负向,取正值时沿正向。

    三个加速度分量均以重力加速度g的倍数为单位,能够表示的加速度范围,即倍率可以统一设定,有4个可选倍率:2g、4g、8g、16g。以ACC_X为例,若倍率设定为2g(默认),则意味着ACC_X取最小值-32768时,当前加速度为沿X轴正方向2倍的重力加速度;若设定为4g,取-32768时表示沿X轴正方向4倍的重力加速度,以此类推。显然,倍率越低精度越好,倍率越高表示的范围越大,这要根据具体的应用来设定。

    我们用f表示倍率,f=0为2g,f=3为16g,设定加速度倍率的代码如下:

    Wire.beginTransmission(0x68); //开启MPU-6050的传输
    Wire.write(0x1C); //加速度倍率寄存器的地址
    Wire.requestFrom(0x68, 1, true); //先读出原配置
    unsigned char acc_conf = Wire.read();
    acc_conf = ((acc_conf & 0xE7) | (f << 3));
    Wire.write(acc_conf);
    Wire.endTransmission(true); //结束传输,true表示释放总线
    再以ACC_X为例,若当前设定的加速度倍率为4g,那么将ACC_X读数换算为加速度的公式为:[公式],g可取当地重力加速度。

    2.2 角速度计

    绕X、Y和Z三个座标轴旋转的角速度分量GYR_X、GYR_Y和GYR_Z均为16位有符号整数。从原点向旋转轴方向看去,取正值时为顺时针旋转,取负值时为逆时针旋转。

    三个角速度分量均以“度/秒”为单位,能够表示的角速度范围,即倍率可统一设定,有4个可选倍率:250度/秒、500度/秒、1000度/秒、2000度/秒。以GYR_X为例,若倍率设定为250度/秒,则意味着GYR取正最大值32768时,当前角速度为顺时针250度/秒;若设定为500度/秒,取32768时表示当前角速度为顺时针500度/秒。显然,倍率越低精度越好,倍率越高表示的范围越大。

    我们用f表示倍率,f=0为250度/秒,f=3为2000度/秒,除角速度倍率寄存器的地址为0x1B之外,设定加速度倍率的代码与2.1节代码一致。

    以GYR_X为例,若当前设定的角速度倍率为1000度/秒,那么将GRY_X读数换算为角速度(顺时针)的公式为:[公式]

    三、运动数据

    在读取加速度计和角速度计的数据并换算为物理值后,根据不同的应用,数据有不同的解译方式。本章将以飞行器运动模型为例,根据加速度和角速度来算出当前的飞行姿态。

    3.1 加速度计模型

    我们可以把加速度计想象为一个正立方体盒子里放着一个球,这个球被弹簧固定在立方体的中心。当盒子运动时,根据假想球的位置即可算出当前加速度的值。想象如果在太空中,盒子没有任何受力时,假想球将处于正中心的位置,三个轴的加速度均为0。见下图:

    如果我们给盒子施加一个水平向左的力,那么显然盒子就会有一个向左的加速度,此时盒内的假想球会因为惯性作用贴向盒内的右侧面。如下图所示:

    为了保证数据的物理意义,MPU6050的加速度计是以假想球在三轴上座标值的相反数作为三个轴的加速度值。当假想球的位置偏向一个轴的正向时,该轴的加速度读数为负值,当假想球的位置偏向一个轴的负向时,该轴的加速度读数为正值。

    根据以上分析,当我们把MPU6050芯片水平放于地方,芯片表面朝向天空,此时由于受到地球重力的作用, 假想球的位置偏向Z轴的负向,因此Z轴的加速度读数应为正,且在理想情况下应为g。注意,此加速度的物理意义并不是重力加速度,而是自身运动的加速度,可以这样理解:正因为其自身运动的加速度与重力加速度大小相等方向相反,芯片才能保持静止。

    3.2 Roll-pitch-yaw模型与姿态计算

    表示飞行器当前飞行姿态的一个通用模型就是建立下图所示坐标系,并用Roll表示绕X轴的旋转,Pitch表示绕Y轴的旋转,Yaw表示绕Z轴的旋转。

    由于MPU6050可以获取三个轴向上的加速度,而地球重力则是长期存在且永远竖直向下,因此我们可以根据重力加速度相对于芯片的指向为参考算得当前姿态。

    为方便起见,我们让芯片正面朝下固定在上图飞机上,且座标系与飞机的坐标系完全重合,以三个轴向上的加速度为分量,可构成加速度向量[公式]。假设当前芯片处于匀速直线运动状态,那么[公式]应垂直于地面上向,即指向Z轴负方向,模长为[公式](与重力加速度大小相等,方向相反,见3.1节)。若芯片(座标系)发生旋转,由于加速度向量[公式]仍然竖直向上,所以Z轴负方向将不再与[公式]重合。见下图。

    为了方便表示,上图坐标系的Z轴正方向(机腹以及芯片正面)向下,X轴正方向(飞机前进方向)向右。此时芯片的Roll角[公式](黄色)为加速度向量与其在XZ平面上投影[公式]的夹角,Pitch角[公式](绿色)与其在YZ平面上投影[公式]的夹角。求两个向量的夹角可用点乘公式:[公式] ,简单推导可得:

    [公式],以及[公式]

    注意,因为arccos函数只能返回正值角度,因此还需要根据不同情况来取角度的正负值。当y值为正时,Roll角要取负值,当x轴为负时,Pitch角要取负值。

    3.4 Yaw角的问题

    因为没有参考量,所以无法求出当前的Yaw角的绝对角度,只能得到Yaw的变化量,也就是角速度GYR_Z。当然,我们可以通过对GYR_Z积分的方法来推算当前Yaw角(以初始值为准),但由于测量精度的问题,推算值会发生漂移,一段时间后就完全失去意义了。然而在大多数应用中,比如无人机,只需要获得GRY_Z就可以了。

    如果必须要获得绝对的Yaw角,那么应当选用MPU9250这款九轴运动跟踪芯片,它可以提供额外的三轴罗盘数据,这样我们就可以根据地球磁场方向来计算Yaw角了,具体方法此处不再赘述。

    四、数据处理与实现

    MPU6050芯片提供的数据夹杂有较严重的噪音,在芯片处理静止状态时数据摆动都可能超过2%。除了噪音,各项数据还会有偏移的现象,也就是说数据并不是围绕静止工作点摆动,因此要先对数据偏移进行校准 ,再通过滤波算法消除噪音。

    4.1 校准

    校准是比较简单的工作,我们只需要找出摆动的数据围绕的中心点即可。我们以GRY_X为例,在芯片处理静止状态时,这个读数理论上讲应当为0,但它往往会存在偏移量,比如我们以10ms的间隔读取了10个值如下:

    -158.4, -172.9, -134.2, -155.1, -131.2, -146.8, -173.1, -188.6, -142.7, -179.5

    这10个值的均值,也就是这个读数的偏移量为-158.25。在获取偏移量后,每次的读数都减去偏移量就可以得到校准后的读数了。当然这个偏移量只是估计值,比较准确的偏移量要对大量的数据进行统计才能获知,数据量越大越准,但统计的时间也就越慢。一般校准可以在每次启动系统时进行,那么你应当在准确度和启动时间之间做一个权衡。

    三个角速度读数GYR_X、GYR_Y和GYR_Z均可通过统计求平均的方法来获得,但三个加速度分量就不能这样简单的完成了,因为芯片静止时的加速度并不为0。

    加速度值的偏移来自两个方面,一是由于芯片的测量精度,导至它测得的加速度向量并不垂直于大地;二是芯片在整个系统(如无人机)上安装的精度是有限的,系统与芯片的座标系很难达到完美重合。前者我们称为读数偏移,后者我们称为角度偏移。因为读数和角度之间是非线性关系,所以要想以高精度进行校准必须先单独校准读数偏移,再把芯片固定在系统中后校准角度偏移。然而,由于校准角度偏移需要专业设备,且对于一般应用来说,两步校准带来的精度提升并不大,因此通常只进行读数校准即可。下面介绍读数校准的方法。我们还3.2节的飞机为例,分以下几个步骤:

    1. 首先要确定飞机的坐标系,对于多轴飞行器来说这非常重要。如果坐标系原点的位置或坐标轴的方向存在较大偏差,将会给后面的飞控造成不良影响。
    2. 在确定了飞机的坐标系后,为了尽量避免读数偏移带来的影响,首先将MPU6050牢牢地固定在飞机上,并使二者座标系尽可能的重合。当然把Z轴反过来装也是可以的,就是需要重新推算一套角度换算公式。
    3. 将飞机置于水平、坚固的平面上,并充分预热。对于多轴无人机而言,空中悬停时的XY平面应当平行于校准时的XY平面。此时,我们认为芯片的加速度方向应当与Z轴负方向重合,且加速度向量的模长为g,因此ACC_X和ACC_Y的理论值应为0,ACC_Z的理论值应为-16384(假设我们设定2g的倍率,1g的加速度的读数应为最大值-32768的一半)。
    4. 由于ACC_X和ACC_Y的理论值应为0,与角速度量的校准类似,这两个读数偏移量可用统计均值的方式校准。ACC_Z则需要多一步处理,即在统计偏移量的过程中,每次读数都要加上16384,再进行统计均值校准。

    4.2 卡尔曼滤波

    对于夹杂了大量噪音的数据,卡尔曼滤波器的效果无疑是最好的。如果不想考虑算法细节,可以直接使用Arduino的Klaman Filter库完成。在我们的模型中,一个卡尔曼滤波器接受一个轴上的角度值、角速度值以及时间增量,估计出一个消除噪音的角度值。跟据当前的角度值和上一轮估计的角度值,以及这两轮估计的间隔时间,我们还可以反推出消除噪音的角速度。

    实现代码见4.3节。下面介绍卡尔曼滤波算法细节,不感兴趣的可跳过。

    (想看的人多了再写)

    4.3 实现代码

    以下代码在Arduino软件1.65版本中编译、烧写以及测试通过。

    // 本代码版权归Devymex所有,以GNU GENERAL PUBLIC LICENSE V3.0发布
    // http://www.gnu.org/licenses/gpl-3.0.en.html
    // 相关文档参见作者于知乎专栏发表的原创文章:
    // http://zhuanlan.zhihu.com/devymex/20082486
    
    //连线方法
    //MPU-UNO
    //VCC-VCC
    //GND-GND
    //SCL-A5
    //SDA-A4
    //INT-2 (Optional)
    
    #include <Kalman.h>
    #include <Wire.h>
    #include <Math.h>
    
    float fRad2Deg = 57.295779513f; //将弧度转为角度的乘数
    const int MPU = 0x68; //MPU-6050的I2C地址
    const int nValCnt = 7; //一次读取寄存器的数量
    
    const int nCalibTimes = 1000; //校准时读数的次数
    int calibData[nValCnt]; //校准数据
    
    unsigned long nLastTime = 0; //上一次读数的时间
    float fLastRoll = 0.0f; //上一次滤波得到的Roll角
    float fLastPitch = 0.0f; //上一次滤波得到的Pitch角
    Kalman kalmanRoll; //Roll角滤波器
    Kalman kalmanPitch; //Pitch角滤波器
    
    void setup() {
      Serial.begin(9600); //初始化串口,指定波特率
      Wire.begin(); //初始化Wire库
      WriteMPUReg(0x6B, 0); //启动MPU6050设备
    
      Calibration(); //执行校准
      nLastTime = micros(); //记录当前时间
    }
    

    void loop() {
    int readouts[nValCnt];
    ReadAccGyr(readouts); //读出测量值

    float realVals[7];
    Rectify(readouts, realVals); //根据校准的偏移量进行纠正

    //计算加速度向量的模长,均以g为单位
    float fNorm = sqrt(realVals[0] realVals[0] + realVals[1] realVals[1] + realVals[2] * realVals[2]);
    float fRoll = GetRoll(realVals, fNorm); //计算Roll角
    if (realVals[1] > 0) {
    fRoll = -fRoll;
    }
    float fPitch = GetPitch(realVals, fNorm); //计算Pitch角
    if (realVals[0] < 0) {
    fPitch = -fPitch;
    }

    //计算两次测量的时间间隔dt,以秒为单位
    unsigned long nCurTime = micros();
    float dt = (double)(nCurTime - nLastTime) / 1000000.0;
    //对Roll角和Pitch角进行卡尔曼滤波
    float fNewRoll = kalmanRoll.getAngle(fRoll, realVals[4], dt);
    float fNewPitch = kalmanPitch.getAngle(fPitch, realVals[5], dt);
    //跟据滤波值计算角度速
    float fRollRate = (fNewRoll - fLastRoll) / dt;
    float fPitchRate = (fNewPitch - fLastPitch) / dt;

    //更新Roll角和Pitch角
    fLastRoll = fNewRoll;
    fLastPitch = fNewPitch;
    //更新本次测的时间
    nLastTime = nCurTime;

    //向串口打印输出Roll角和Pitch角,运行时在Arduino的串口监视器中查看
    Serial.print(“Roll:”);
    Serial.print(fNewRoll); Serial.print(’(’);
    Serial.print(fRollRate); Serial.print("),\tPitch:");
    Serial.print(fNewPitch); Serial.print(’(’);
    Serial.print(fPitchRate); Serial.print(")\n");
    delay(10);
    }

    //向MPU6050写入一个字节的数据
    //指定寄存器地址与一个字节的值
    void WriteMPUReg(int nReg, unsigned char nVal) {
    Wire.beginTransmission(MPU);
    Wire.write(nReg);
    Wire.write(nVal);
    Wire.endTransmission(true);
    }

    //从MPU6050读出一个字节的数据
    //指定寄存器地址,返回读出的值
    unsigned char ReadMPUReg(int nReg) {
    Wire.beginTransmission(MPU);
    Wire.write(nReg);
    Wire.requestFrom(MPU, 1, true);
    Wire.endTransmission(true);
    return Wire.read();
    }

    //从MPU6050读出加速度计三个分量、温度和三个角速度计
    //保存在指定的数组中
    void ReadAccGyr(int pVals) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.requestFrom(MPU, nValCnt 2, true);
    Wire.endTransmission(true);
    for (long i = 0; i < nValCnt; ++i) {
    pVals[i] = Wire.read() << 8 | Wire.read();
    }
    }

    //对大量读数进行统计,校准平均偏移量
    void Calibration()
    {
    float valSums[7] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0};
    //先求和
    for (int i = 0; i < nCalibTimes; ++i) {
    int mpuVals[nValCnt];
    ReadAccGyr(mpuVals);
    for (int j = 0; j < nValCnt; ++j) {
    valSums[j] += mpuVals[j];
    }
    }
    //再求平均
    for (int i = 0; i < nValCnt; ++i) {
    calibData[i] = int(valSums[i] / nCalibTimes);
    }
    calibData[2] += 16384; //设芯片Z轴竖直向下,设定静态工作点。
    }

    //算得Roll角。算法见文档。
    float GetRoll(float pRealVals, float fNorm) {
    float fNormXZ = sqrt(pRealVals[0] pRealVals[0] + pRealVals[2] pRealVals[2]);
    float fCos = fNormXZ / fNorm;
    return acos(fCos) fRad2Deg;
    }

    //算得Pitch角。算法见文档。
    float GetPitch(float pRealVals, float fNorm) {
    float fNormYZ = sqrt(pRealVals[1] pRealVals[1] + pRealVals[2] pRealVals[2]);
    float fCos = fNormYZ / fNorm;
    return acos(fCos) fRad2Deg;
    }

    //对读数进行纠正,消除偏移,并转换为物理量。公式见文档。
    void Rectify(int pReadout, float pRealVals) {
    for (int i = 0; i < 3; ++i) {
    pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 16384.0f;
    }
    pRealVals[3] = pReadout[3] / 340.0f + 36.53;
    for (int i = 4; i < 7; ++i) {
    pRealVals[i] = (float)(pReadout[i] - calibData[i]) / 131.0f;
    }
    }

    展开全文
  • MPU6050数据发送到匿名上位机2.4版本的示例代码,MPU初始化是正点原子的
  • MPU6050数据手册.zip

    2019-06-14 17:17:29
    MPU6050开发数据手册,有英文数据手册及中文的寄存器手册
  • MPU6050数据分析

    2016-01-13 22:48:28
    MPU6050外围电路外加程序实现,简单明了
  • stm32读取MPU6050数据(硬件IIC) 已测试通过
  • mpu6050数据手册

    2015-07-29 11:11:03
    完整版mpu6050陀螺仪和加速度传感器数据手册及各寄存器配置
  • [体感游戏] 1、MPU6050数据采集传输与可视化-附件资源
  • verilog编程fpga通过IIC读取mpu6050数据

    热门讨论 2014-02-20 20:50:25
    使用verilog HDL语言编写IIC协议,用FPGA读取mpu6050数据,其他可用IIC读数器件操作类似
  • 瑞萨单片机姿态结算读取陀螺仪mpu6050数据程序
  • IIC驱动实验:读取mpu6050数据 MPU6050 空间运动传感器芯片 3轴加速度 3轴角速度 硬件原理图 设备树节点 iomuxc子节点 pinctrl_i2c1: i2c1grp { fsl,pins = < MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x...

    IIC驱动实验:读取mpu6050数据

    MPU6050

    空间运动传感器芯片

    • 3轴加速度
    • 3轴角速度

    硬件原理图

    在这里插入图片描述

    设备树节点

    iomuxc子节点
    pinctrl_i2c1: i2c1grp {
            		fsl,pins = <
            			MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
            			MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
            		>;
            	}; 
    
    i2c1子节点
    &i2c1{
    	clock-frequency = <100000>;
    	pinctrl-names = "default";
    	pinctrl-0 = <&pinctrl_i2c1>;
    	status = "okay";
    	
    	i2c_mpu6050@68 {
    	        	compatible = "fire,i2c_mpu6050";
    	        	reg = <0x68>;
    	        	status = "okay";
    	 };
    };
    

    执行程序: i2c_mpu6050.c

    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/uaccess.h>
    #include <linux/i2c.h>
    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/delay.h>
    #include <linux/ide.h>
    #include <linux/errno.h>
    #include <linux/gpio.h>
    #include <asm/mach/map.h>
    #include <linux/of.h>
    #include <linux/of_address.h>
    #include <linux/of_gpio.h>
    #include <asm/io.h>
    #include <linux/device.h>
    
    #include <linux/platform_device.h>
    
    #define SMPLRT_DIV                                     0x19
    #define CONFIG                                      		 0x1A
    #define GYRO_CONFIG                                 0x1B
    #define ACCEL_CONFIG                                0x1C
    #define ACCEL_XOUT_H                               0x3B
    #define ACCEL_XOUT_L                                0x3C
    #define ACCEL_YOUT_H                               0x3D
    #define ACCEL_YOUT_L                                0x3E
    #define ACCEL_ZOUT_H                               0x3F
    #define ACCEL_ZOUT_L                                0x40
    #define TEMP_OUT_H                                    0x41
    #define TEMP_OUT_L                                    0x42
    #define GYRO_XOUT_H                                 0x43
    #define GYRO_XOUT_L                                  0x44
    #define GYRO_YOUT_H                                 0x45
    #define GYRO_YOUT_L                                  0x46
    #define GYRO_ZOUT_H                                 0x47
    #define GYRO_ZOUT_L                                  0x48
    #define PWR_MGMT_1                                   0x6B
    #define WHO_AM_I                                          0x75
    
    /*------------------字符设备内容----------------------*/
    #define DEV_NAME "I2C1_mpu6050"
    #define DEV_CNT (1)
    
    /*定义 led 资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址*/
    static dev_t mpu6050_devno;				 //定义字符设备的设备号
    static struct cdev mpu6050_chr_dev;		 //定义字符设备结构体chr_dev
    struct class *class_mpu6050;			 //保存创建的类
    struct device *device_mpu6050;			 // 保存创建的设备
    struct device_node *mpu6050_device_node; //rgb_led的设备树节点结构体
    
    /*------------------IIC设备内容----------------------*/
    struct i2c_client *mpu6050_client = NULL; //保存mpu6050设备对应的i2c_client结构体,匹配成功后由.prob函数带回。
    
    /*通过i2c 向mpu6050写入数据
    *mpu6050_client:mpu6050的i2c_client结构体。
    *address, 数据要写入的地址,
    *data, 要写入的数据
    *返回值,错误,-1。成功,0  
    */
    static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
    {
    	int error = 0;
    	u8 write_data[2];
    	struct i2c_msg send_msg; //要发送的数据结构体
    
    	/*设置要发送的数据*/
    	write_data[0] = address;
    	write_data[1] = data;
    
    	/*发送 iic要写入的地址 reg*/
    	send_msg.addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
    	send_msg.flags = 0;					  //标记为发送数据
    	send_msg.buf = write_data;			  //写入的首地址
    	send_msg.len = 2;					  //reg长度
    
    	/*执行发送*/
    	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
    	if (error != 1)
    	{
    		printk(KERN_DEBUG "\n i2c_transfer error \n");
    		return -1;
    	}
    	return 0;
    }
    
    /*通过i2c 向mpu6050写入数据
    *mpu6050_client:mpu6050的i2c_client结构体。
    *address, 要读取的地址,
    *data,保存读取得到的数据
    *length,读长度
    *返回值,错误,-1。成功,0
    */
    static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
    {
    	int error = 0;
    	u8 address_data = address;
    	struct i2c_msg mpu6050_msg[2];
    	/*设置读取位置msg*/
    	mpu6050_msg[0].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
    	mpu6050_msg[0].flags = 0;					//标记为发送数据
    	mpu6050_msg[0].buf = &address_data;			//写入的首地址
    	mpu6050_msg[0].len = 1;						//写入长度
    
    	/*设置读取位置msg*/
    	mpu6050_msg[1].addr = mpu6050_client->addr; //mpu6050在 iic 总线上的地址
    	mpu6050_msg[1].flags = I2C_M_RD;			//标记为读取数据
    	mpu6050_msg[1].buf = data;					//读取得到的数据保存位置
    	mpu6050_msg[1].len = length;				//读取长度
    
    	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
    
    	if (error != 2)
    	{
    		printk(KERN_DEBUG "\n i2c_read_mpu6050 error \n");
    		return -1;
    	}
    	return 0;
    }
    
    /*初始化i2c
    *返回值,成功,返回0。失败,返回 -1
    */
    static int mpu6050_init(void)
    {
    	int error = 0;
    	/*配置mpu6050电源管理,0x00,正常启动*/
    	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
    	/*设置MPU6050的采样频率*/
    	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
    	/*设置数字低通滤波器和帧同步引脚采样*/
    	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
    	/*设置量程和 X、Y、Z 轴加速度自检*/
    	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
    
    	if (error < 0)
    	{
    		/*初始化错误*/
    		printk(KERN_DEBUG "\n mpu6050_init error \n");
    		return -1;
    	}
    	return 0;
    }
    
    /*字符设备操作函数集,open函数实现*/
    static int mpu6050_open(struct inode *inode, struct file *filp)
    {
    	// printk("\n mpu6050_open \n");
    
    	/*向 mpu6050 发送配置数据,让mpu6050处于正常工作状态*/
    	mpu6050_init();
    	return 0;
    }
    
    /*字符设备操作函数集,.read函数实现*/
    static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
    {
    	char data_H;
    	char data_L;
    	int error;
    	//保存mpu6050转换得到的原始数据
    	short mpu6050_result[6]; 
    
    	/*读取3轴加速度原始值*/
    	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
    	mpu6050_result[0] = data_H << 8;
    	mpu6050_result[0] += data_L;
    
    	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
    	mpu6050_result[1] = data_H << 8;
        mpu6050_result[1] += data_L;
    
    	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
    	mpu6050_result[2] = data_H << 8;
    	mpu6050_result[2] += data_L;
    
    	/*读取3轴角速度原始值*/
    	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
    	mpu6050_result[3] = data_H << 8;
    	mpu6050_result[3] += data_L;
    
    	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
    	mpu6050_result[4] = data_H << 8;
    	mpu6050_result[4] += data_L;
    
    	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
    	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
    	mpu6050_result[5] = data_H << 8;
    	mpu6050_result[5] += data_L;
    
    
    	// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
    	// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);
    
    	/*将读取得到的数据拷贝到用户空间*/
    	error = copy_to_user(buf, mpu6050_result, cnt);
    
    	if(error != 0)
    	{
    		printk("copy_to_user error!");
    		return -1;
    	}
    	return 0;
    }
    
    /*字符设备操作函数集,.release函数实现*/
    static int mpu6050_release(struct inode *inode, struct file *filp)
    {
    	// printk("\n mpu6050_release \n");
    	return 0;
    }
    
    /*字符设备操作函数集*/
    static struct file_operations mpu6050_chr_dev_fops =
    	{
    		.owner = THIS_MODULE,
    		.open = mpu6050_open,
    		.read = mpu6050_read,
    		.release = mpu6050_release,
    };
    
    /*----------------平台驱动函数集-----------------*/
    static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
    
    	int ret = -1; //保存错误状态码
    
    	printk(KERN_EMERG "\t  match successed  \n");
    	/*---------------------注册 字符设备部分-----------------*/
    
    	//采用动态分配的方式,获取设备编号,次设备号为0,
    	//设备名称为rgb-leds,可通过命令cat  /proc/devices查看
    	//DEV_CNT为1,当前只申请一个设备编号
    	ret = alloc_chrdev_region(&mpu6050_devno, 0, DEV_CNT, DEV_NAME);
    	if (ret < 0)
    	{
    		printk("fail to alloc mpu6050_devno\n");
    		goto alloc_err;
    	}
    
    	//关联字符设备结构体cdev与文件操作结构体file_operations
    	mpu6050_chr_dev.owner = THIS_MODULE;
    	cdev_init(&mpu6050_chr_dev, &mpu6050_chr_dev_fops);
    
    	// 添加设备至cdev_map散列表中
    	ret = cdev_add(&mpu6050_chr_dev, mpu6050_devno, DEV_CNT);
    	if (ret < 0)
    	{
    		printk("fail to add cdev\n");
    		goto add_err;
    	}
    
    	/*创建类 */
    	class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);
    
    	/*创建设备 DEV_NAME 指定设备名,*/
    	device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);
    	mpu6050_client = client;
    	return 0;
    
    add_err:
    	// 添加设备失败时,需要注销设备号
    	unregister_chrdev_region(mpu6050_devno, DEV_CNT);
    	printk("\n error! \n");
    alloc_err:
    
    	return -1;
    }
    
    
    static int mpu6050_remove(struct i2c_client *client)
    {
    	/*删除设备*/
    	device_destroy(class_mpu6050, mpu6050_devno);	  //清除设备
    	class_destroy(class_mpu6050);					  //清除类
    	cdev_del(&mpu6050_chr_dev);						  //清除设备号
    	unregister_chrdev_region(mpu6050_devno, DEV_CNT); //取消注册字符设备
    	return 0;
    }
    
    
    
    /*定义ID 匹配表*/
    static const struct i2c_device_id gtp_device_id[] = {
    	{"fire,i2c_mpu6050", 0},
    	{}};
    
    /*定义设备树匹配表*/
    static const struct of_device_id mpu6050_of_match_table[] = {
    	{.compatible = "fire,i2c_mpu6050"},
    	{/* sentinel */}};
    
    /*定义i2c总线设备结构体*/
    struct i2c_driver mpu6050_driver = {
    	.probe = mpu6050_probe,
    	.remove = mpu6050_remove,
    	.id_table = gtp_device_id,
    	.driver = {
    		.name = "fire,i2c_mpu6050",
    		.owner = THIS_MODULE,
    		.of_match_table = mpu6050_of_match_table,
    	},
    };
    
    /*
    *驱动初始化函数
    */
    static int __init mpu6050_driver_init(void)
    {
    	int ret;
    	pr_info("mpu6050_driver_init\n");
    	ret = i2c_add_driver(&mpu6050_driver);
    	return ret;
    }
    
    /*
    *驱动注销函数
    */
    static void __exit mpu6050_driver_exit(void)
    {
    	pr_info("mpu6050_driver_exit\n");
    	i2c_del_driver(&mpu6050_driver);
    }
    
    module_init(mpu6050_driver_init);
    module_exit(mpu6050_driver_exit);
    
    MODULE_LICENSE("GPL");
    
    

    app.c

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    int main(int argc, char *argv[])
    {
    
        short resive_data[6];  //保存收到的 mpu6050转换结果数据,依次为 AX(x轴角度), AY, AZ 。GX(x轴加速度), GY ,GZ
    
        /*打开文件*/
        int fd = open("/dev/I2C1_mpu6050", O_RDWR);
        if(fd < 0)
        {
    		printf("open file : %s failed !\n", argv[0]);
    		return -1;
    	}
    
        /*读取数据*/
        int error = read(fd,resive_data,12);
        if(error < 0)
        {
            printf("write file error! \n");
            close(fd);
            /*判断是否关闭成功*/
        }
    
        /*打印数据*/
        printf("AX=%d, AY=%d, AZ=%d ",(int)resive_data[0],(int)resive_data[1],(int)resive_data[2]);
    	printf("    GX=%d, GY=%d, GZ=%d \n \n",(int)resive_data[3],(int)resive_data[4],(int)resive_data[5]);
    
    
        /*关闭文件*/
        error = close(fd);
        if(error < 0)
        {
            printf("close file error! \n");
        }
        
        return 0;
    }
    
    展开全文
  • stm32f103c8t6 的MPU6050数据读取(经过卡尔曼滤波)使用方法:使用C8T6的串口一,和IIC接口一,直接会打印出数据
  • STM32F407VE实现软件IIC读取mpu6050数据,并依靠串口返回原始数据以及欧拉角
  • 该文件包含一个简单的单轴示例和一个使用 MPU6050 的 DLFP(数字低通滤波器)的完整 6 轴示例。 使用的 I2C 库是 WirnigPiI2C,它随 RPi 的 targetinstaller 发行版一起提供。 这些示例显示了 I2C 库命令的用法。 ...
  • mpu6050数据处理

    热门讨论 2013-08-23 01:12:37
    利用AVR单片机读取mpu6050的加速度计的加速度值和陀螺仪的角度值。用加速度值求出X和Y轴上的倾斜角;用陀螺仪的角度值积分出旋转角度。然后融合两者的数据,最后滤波以获得更好的效果。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 992
精华内容 396
关键字:

mpu6050数据