采用MPU6050的手势遥控器

随着社会的发展与进步,电子产品日益更新,个人家庭迎来了越来越多的智能家电。然而,现在的智能电子设备产品的遥控器多数都是按键形的。操作繁琐不直观,学习成本大。并且对于有老人小孩的家庭,给他们的操作带来了巨大的障碍。手势遥控器是指利用简单的手势来指挥电子设备,获得便捷的操作体验。让家庭成员都可以享受科技发展带来的福利。MPU6050是一个运动传感器,内置陀螺仪,再由电子设备利用判断结果做出相应的响应即完成了通过手势控制电子设备的目标。
智能家电为我们的生活提供了巨大的方便,但与此同时我们也注意到了一些现象,智能家电功能丰富,伴随而来的就是复杂的惭怍性。家电控制面板或者遥控器上许多按键分别负责不同的功能,为了实现特定功能,可能还需要一些组合按键。这样的学习成本对于老人和婴幼儿来说显然是不合适的,很容易产生误操作。为了让前者像年轻人一样方便的的操作智能家电设备享受科技进步带来的便利,我们想到了如果能通过一些简单的手势运动给家电发送指令,进行操作,将会给这一现实局面带来巨大的缓解。
我们设想这样一个场景,当你回到家中,通过手势的上扬调节空调温度的上升,电视音量的增加,通过手势的左挥调节电视的节目台,空调的工作模式等。我们了解到,目前这方面的研究比较少,也没有公开的实现方案,是一个很有前景的方向,我们选择了一个有代表性的一个方面来进行设计实现:用手势来实现不同的灯光花样。系统包括主机发送端,从机响应端。发送端分析计算手势行为,接收端做出预期响应,来完成通过手势控制电子设备的目的。
硬件部分由于只使用了单片机最小系统,和MPU6050模块,蓝牙主从机模块,由于时间缘故所以没有进行焊接,直接采用了开发板进行实现。软件部分核心有两方面,一为MPU6050姿态确定,二为串口发送与接收。即单片机通过MPU读取到MPU6050寄存器值后通过串口发送给从机,从机通过串口接受发送值并且做出相应响应,完成手势遥控功能。

作为一个简易的手势遥控器,首先因具有手势识别,即采用MPU6050读取手势状态。作为遥控器,即应有发送端和接收端,即通过蓝牙串口实现发送与接收。

设计原理
MPU6050
Mpu6050为全球首例整合3轴陀螺仪、3轴加速器、含9轴融合演
MPU-6000为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了大量的包装空间。MPU-6000整合了3轴陀螺仪、3轴加速器,并含可藉由第二个I2C端口连接其他厂牌之加速器、磁力传感器、或其他传感器的数位运动处理(DMP: Digital Motion Processor)硬件加速引擎,由主要I2C端口以单一数据流的形式,向应用端输出完整的9轴融合演算技术
InvenSense的运动处理资料库,可处理运动感测的复杂数据,降低了运动处理运算对操作系统的负荷,并为应用开发提供架构化的API。
MPU-6000的角速度全格感测范围为±250、±500、±1000与±2000°/sec (dps),可准确追緃快速与慢速动作,并且,用户可程式控制的加速器全格感测范围为±2g、±4g±8g与±16g。产品传输可透过最高至400kHz的I2C或最高达20MHz的SPI。
MPU-6000可在不同电压下工作,VDD供电电压介为2.5V±5%、3.0V±5%或3.3V±5%,逻辑接口VVDIO供电为1.8V± 5%。MPU-6000的包装尺寸4x4x0.9mm(QFN),在业界是革命性的尺寸。其他的特征包含内建的温度感测器、包含在运作环境中仅有±1%变动的振荡器。

应用
运动感测游戏
现实增强
电子稳像 (EIS: Electronic Image Stabilization)
光学稳像(OIS: Optical Image Stabilization)
行人导航器
“零触控”手势用户接口
姿势快捷方式

认证市场
智能型手机
平板装置设备
手持型游戏产品
游戏机
3D遥控器
可携式导航设备

特征
1、以数字输出6轴或9轴的旋转矩阵、四元数(quaternion)、欧拉角格式(Euler Angle forma)的融合演算数据。
2、具有131 LSBs/°/sec 敏感度与全格感测范围为±250、±500、±1000与±2000°/sec 的3轴角速度感测器(陀螺仪)。
3、可程式控制,且程式控制范围为±2g、±4g、±8g和±16g的3轴加速器。
4、移除加速器与陀螺仪轴间敏感度,降低设定给予的影响与感测器的飘移。
5、数字运动处理(DMP: Digital Motion Processing)引擎可减少复杂的融合演算数据、感测器同步化、姿势感应等的负荷。
6、运动处理数据库支持Android、Linux与Windows
7、内建之运作时间偏差与磁力感测器校正演算技术,免除了客户须另外进行校正的需求。
8、以数位输出的温度传感器
9、以数位输入的同步引脚(Sync pin)支援视频电子影相稳定技术与GPS
10、可程式控制的中断(interrupt)支援姿势识别、摇摄、画面放大缩小、滚动、快速下降中断、high-G中断、零动作感应、触击感应、摇动感应功能。
11、VDD供电电压为2.5V±5%、3.0V±5%、3.3V±5%;VDDIO为1.8V± 5%
12、陀螺仪运作电流:5mA,陀螺仪待命电流:8A;加速器运作电流:8A,加速器省电模式电流: 8A@10Hz
13、高达400kHz快速模式的I2C,或最高至20MHz的SPI串行主机接口(serial host interface)
14、内建频率产生器在所有温度范围(full temperature range)仅有±1%频率变化。
15、使用者亲自测试
16、10,000 g 碰撞容忍度
17、为可携式产品量身订作的最小最薄包装 (4x4x0.9mm QFN)
18、符合RoHS及环境标准MPU-6000为全球首例整合性6轴运动处理组件,相较于多组件方案,免除了组合陀螺仪与加速器时之轴间差的问题,减少了大量的包装空间。MPU-6000整合了3轴陀螺仪、3轴加速器,并含可藉由第二个I2C端口连接其他厂牌之加速器、磁力传感器、或其他传感器的数位运动处理(DMP: Digital Motion Processor)硬件加速引擎,由主要I2C端口以单一数据流的形式,向应用端输出完整的9轴融合演算技术
InvenSense的运动处理资料库,可处理运动感测的复杂数据,降低了运动处理运算对操作系统的负荷,并为应用开发提供架构化的API。

MPU-6000的角速度全格感测范围为±250、±500、±1000与±2000°/sec (dps),可准确追緃快速与慢速动作,并且,用户可程式控制的加速器全格感测范围为±2g、±4g±8g与±16g。产品传输可透过最高至400kHz的I2C或最高达20MHz的SPI。
MPU-6000可在不同电压下工作,VDD供电电压介为2.5V±5%、3.0V±5%或3.3V±5%,逻辑接口VVDIO供电为1.8V± 5%。MPU-6000的包装尺寸4x4x0.9mm(QFN),在业界是革命性的尺寸。其他的特征包含内建的温度感测器、包含在运作环境中仅有±1%变动的振荡器。

HC05
蓝牙HC05是主从一体的蓝牙串口模块,简单的说,当蓝牙设备与蓝牙设备配对连接成功后,我们可以忽视蓝牙内部的通信协议,直接将将蓝牙当做串口用。当建立连接,两设备共同使用一通道也就是同一个串口,一个设备发送数据到通道中,另外一个设备便可以接收通道中的数据。当然,对于建立这种通道连接是有一定条件,那就是对蓝牙设置好能进行配对连接的AT模式。

一般的蓝牙串口模块引脚:
RXD:接收端
TXD:发送端
AT:设置工作模式(1、工作模式 2、AT指令设置模式)
VCC:模块供电正极(5V)
GND:模块供电负极

蓝牙HC05模块探究-设置AT指令

一般的蓝牙模块使用有三种。
第一:蓝牙从设备与电脑配对连接(1、电脑自带蓝牙 2、电脑不带蓝牙,这事需要蓝牙适配器)
第二:蓝牙从设备与手机配对连接
第三:蓝牙从设备与蓝牙主设备配对连接
HC05蓝牙模块的AT模式设置的方法大致有三种:
一、默认设置
二、用USB转UART模块设置
三、用带有蓝牙设置的主控器串口程序进行设置
第一:主要默认设置:
模块工作角色:从模式
串口参数:38400bits/s 停止位1位无校验位
配对码:1234
设备名称:HC-05
连接模式:任意蓝牙设备连接模式
第二:用USB to UART模块设置蓝牙
蓝牙HC05模块探究-设置AT指令
蓝牙与USB转串口模块连接方式,RXD-TX TXD-RX VCC-VCC GND-GND
要设置蓝牙AT指令,必须让AT引脚置高,然后接上蓝牙模块,当蓝牙模块state灯变为慢闪,则表明已经进入AT模式。(j将蓝牙模块与转串口模块对插,用跳线将AT对应的引脚接VCC)这时候将转串口模块接入电脑,打开超级终端或者串口调试助手便可以开始设置AT模式。
打开串口调试助手,测试AT指令,找到相应串口号后,注意两点,1、设置AT模式的波特率为38400 2、输入指令后加上回车后换行,发送后返回OK。
蓝牙HC05模块探究-设置AT指令

下面来设置模块为从机模式,依次输入指令
AT+NAME=Bluetooth-Slave 蓝牙名称为Bluetooth-Slave
AT+ROLE=0 蓝牙模式为从模式
AT+CMODE=0 蓝牙连接模式为任意地址连接模式
AT+PSWD=1234 蓝牙配对密码为1234
AT+UART=9600,0,0 蓝牙通信串口波特率为9600,停止位1位,无校验位
AT+RMAAD 清空配对列表
相应返回OK表示设置成功。这个时候的蓝牙就可以与电脑主机或者手机配对通信。需要注意的是设置指令里的符号不要在中文状态下输入,否则不会返回相应指令。

设计方案和原理了解过后开始进行原理图绘制与软件编写

原理图

主机

采用MPU6050的手势遥控器_第1张图片

从机
采用MPU6050的手势遥控器_第2张图片

软件编写

通过MPU6050读取姿态

void InitMPU6050()
{
        Single_WriteI2C(PWR_MGMT_1, 0x00);        //解除休眠状态
        Single_WriteI2C(SMPLRT_DIV, 0x07);
        Single_WriteI2C(CONFIG, 0x06);
        Single_WriteI2C(GYRO_CONFIG, 0x18);
        Single_WriteI2C(ACCEL_CONFIG, 0x01);
}

//**************************************
//合成数据
//**************************************

int GetData(uchar REG_Address)
{
        char H,L;
        int value;
        H=Single_ReadI2C(REG_Address);
        L=Single_ReadI2C(REG_Address+1);
        value=(H<<8)+L;
        value/=64;
        return value;
}

通过串口接收发送数据
void send_init()
{
     TMOD=0X20;
     TH1=0XFD;
     TL1=0XFD;
       PCON=0;
     TR1=1;  //启动T1
     SM0=0;
     SM1=1;  
     EA=1;
     ES=1;   //允许串行口中断
}

void send(uchar pd)
{
    SBUF=pd ;
    while(!TI);
    TI=0;

}

通过串口接收数据

TMOD=0X20;  //定时器1工作方式2
    TH1=0XFD;   
    TL1=0XFD;   //八位自动重装计数模式
    TR1=1;
    REN=1;
    SM0=0;
    SM1=1;
    EA=1;
    ES=1;

void bluet() interrupt 4  /*中断接收1个字节*/
{
    if(RI==1)
    {
        RI=0;
          dat=SBUF;
          flag_REC=1;

    }

前期设计原理方案验证后结果得实际连线图如下
主机采用MPU6050的手势遥控器_第3张图片
从机
采用MPU6050的手势遥控器_第4张图片

主从配合
采用MPU6050的手势遥控器_第5张图片

1.姿态解算问题
对mpu6050的数据进行融合的理解,由于数学功底比较差,理解起来不叫吃力所以多花了一些时间去理解,但是离数据融合还有一段距离。在关于mpu6050的数据融合方面,网上有很多资料,由于查找能力有限,没能找到很详细很细致一次性说明问题的资料,都是零零散散的。
2.蓝牙配对部分
由于急于上手,未理清蓝牙配对原理,一直配对不成功。

1.对于姿态解算
放弃对四元数计算,只采用两轴实现二维度姿态判定。
2.对于蓝牙配对
参考网上资料后,实现AT指令控制猪从机实现配对。

通过这次课程设计,使我对于单片机的原理及其应用有了更深的认识,对于MODUBUS的不熟所以采用了IIC实现硬件时序。徒增了程序的复杂性。单片机更多只是一种工具用来实现想法。应多加对理论基础学习,应用只是建立在理论基础上的使用。

心得体会
在实际项目中总会遇到各种问题,特别在硬件调试中,在它运行起来前你永远不会知道会出现什么问题,碰见问题不要害怕,多去网上查解决方法,尽量自己解决,解决不了再去问老师,更能明白自己的不足,锻炼实际解决问题的能力。
主机

#include         
#include     //Keil library  
#include    //Keil library    用于融合加速度数据     
#include   

typedef unsigned char  uchar;
typedef unsigned short ushort;
typedef unsigned int   uint;

//****************************************
// 定义51单片机端口
//定义IIC时钟线 数据线 软件模拟IIC
//****************************************

sbit    SCL=P1^1;
sbit    SDA=P1^0;

// 定义MPU6050内部地址  参考技术手册
//****************************************
#define        SMPLRT_DIV          0x19        //陀螺仪采样率,典型值:0x07(125Hz)
#define        CONFIG              0x1A        //低通滤波频率,典型值:0x06(5Hz)
#define        GYRO_CONFIG         0x1B        //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define        ACCEL_CONFIG        0x1C        //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#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        //电源管理,典型值:0x00(正常启用)
#define        WHO_AM_I            0x75        //IIC地址寄存器(默认数值0x68,只读)
#define        SlaveAddress        0xD0        //IIC写入时的地址字节数据,+1为读取

#define Delay() { _nop_();_nop_();_nop_();_nop_();} //用于IIC软件延时

void  InitMPU6050();
void  I2C_Start();
void  I2C_Stop();
void  I2C_SendACK(bit ack);
bit   I2C_RecvACK();
void  I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void  I2C_ReadPage();
void  I2C_WritePage();
uchar Single_ReadI2C(uchar REG_Address);
void  Single_WriteI2C(uchar REG_Address,uchar REG_data);

void delay(unsigned int k) ;       

void InitMPU6050() ;
int GetData(uchar REG_Address) ;

void send_init() ;
void send(uchar pd) ;

void main()
{ 
        int y;
        delay(500);
        InitMPU6050();
        send_init();  //串口发送
        delay(150);
        while(1)
        {
                        x=0 ;
                y=0 ;                            
                if(GetData(ACCEL_YOUT_H)>200)
                {
                        send('f');
                        y=1 ;
                }
                else if(GetData(ACCEL_YOUT_H)<(-200))
                {
                        send('b');
                        y=1 ;
                }
                if(GetData(ACCEL_XOUT_H)>200)
                {
                        send('r');
                        x=1;
                }
                else if(GetData(ACCEL_XOUT_H)<(-200))
                {
                        send('l');
                        x=1;
                }
                if (x=0)&&(y==0))
                {
                        send('s');                        
                }

                delay(500);
        }
}



//延时
void delay(unsigned int k)        
{                                                
        unsigned int i,j;                                
        for(i=0;ifor(j=0;j<121;j++);
        }                                                
}

//IIC操作函数

//**************************************
//I2C起始信号
//**************************************
void I2C_Start()
{
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay();                 //延时
    SDA = 0;                    //产生下降沿
    Delay();                 //延时
    SCL = 0;                    //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop()
{
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay();                 //延时
    SDA = 1;                    //产生上升沿
    Delay();                 //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(bit ack)
{
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay();                 //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK()
{
    SCL = 1;                    //拉高时钟线
    Delay();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay();                 //延时
    return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(uchar dat)
{
    uchar i;
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay();             //延时
        SCL = 0;                //拉低时钟线
        Delay();             //延时
    }
    I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//**************************************
uchar I2C_RecvByte()
{
    uchar i;
    uchar dat = 0;
    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay();             //延时
    }
    return dat;
}
//**************************************
//向I2C设备写入一个字节数据
//**************************************
void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
    I2C_Start();                  //起始信号
    I2C_SendByte(SlaveAddress);   //发送设备地址+写信号
    I2C_SendByte(REG_Address);    //内部寄存器地址,
    I2C_SendByte(REG_data);       //内部寄存器数据,
    I2C_Stop();                   //发送停止信号
}
//**************************************
//从I2C设备读取一个字节数据
//**************************************
uchar Single_ReadI2C(uchar REG_Address)
{
        uchar REG_data;
        I2C_Start();                   //起始信号
        I2C_SendByte(SlaveAddress);    //发送设备地址+写信号
        I2C_SendByte(REG_Address);     //发送存储单元地址,从0开始        
        I2C_Start();                   //起始信号
        I2C_SendByte(SlaveAddress+1);  //发送设备地址+读信号
        REG_data=I2C_RecvByte();       //读出寄存器数据
        I2C_SendACK(1);                //接收应答信号
        I2C_Stop();                    //停止信号
        return REG_data;
}


//**************************************
//初始化MPU6050
//**************************************

void InitMPU6050()
{
        Single_WriteI2C(PWR_MGMT_1, 0x00);        //解除休眠状态
        Single_WriteI2C(SMPLRT_DIV, 0x07);
        Single_WriteI2C(CONFIG, 0x06);
        Single_WriteI2C(GYRO_CONFIG, 0x18);
        Single_WriteI2C(ACCEL_CONFIG, 0x01);
}

//**************************************
//合成数据
//**************************************

int GetData(uchar REG_Address)
{
        char H,L;
        int value;
        H=Single_ReadI2C(REG_Address);
        L=Single_ReadI2C(REG_Address+1);
        value=(H<<8)+L;
        value/=64;
        return value;
}


void send_init()
{
     TMOD=0X20;
     TH1=0XFD;
     TL1=0XFD;
       PCON=0;
     TR1=1;  //启动T1
     SM0=0;
     SM1=1;  
     EA=1;
     ES=1;   //允许串行口中断
}

void send(uchar pd)
{
    SBUF=pd ;
    while(!TI);
    TI=0;

}

从机
#include 

typedef unsigned int uint ;
typedef unsigned char uchar ;

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

sbit led0 = P0^0 ;
sbit led1 = P0^1;
sbit led2 = P0^2 ;
sbit led3 = P0^3 ;
sbit led4 = P0^4;
sbit led5 = P0^5 ;
sbit led6 = P0^6 ;
sbit led7 = P0^7 ;

bit flag=0;
bit flag_REC=0;
uchar i=0;
uchar dat=0;

void delayms(uint xms);

void back()           ;
void forward()        ;
void stop()           ;
void turn_left()      ;
void turn_right()     ;

void main()
{    

    unsigned int i = 0;  



    ENLED = 0;   //U3、U4两片74HC138总使能
    ADDR3 = 1;   //使能U3使之正常输出
    ADDR2 = 1;   //经U3的Y6输出开启三极管Q16
    ADDR1 = 1;
    ADDR0 = 0;

    TMOD=0X20;  //定时器1工作方式2
    TH1=0XFD;   
    TL1=0XFD;   //八位自动重装计数模式
    TR1=1;
    REN=1;
    SM0=0;
    SM1=1;
    EA=1;
    ES=1;
    while(1)
    {
        if(flag_REC==1)
          {
              flag_REC=0;

                switch(dat)
                {
                    case 'f':
                       forward();    break;
                    case 'b':
                       back();       break;
                    case 'l':
                       turn_left();  break;
                    case 'r':
                       turn_right(); break;
                    case 's':
                       stop();       break; 
                }

          }

    }
}

void back()
{


 led0 = 0 ;
 led1 = 1;
 led2 = 0;
 led3 = 1;
 led4 = 0;
 led5 = 1;
 led6 = 0;
 led7 = 1;

}

void forward()
{



 led0 = 1 ;
 led1 = 0;
 led2 = 1;
 led3 = 0;
 led4 = 1;
 led5 = 0;
 led6 = 1;
 led7 = 0;

}
void stop()
{



 led0 = 0 ;
 led1 = 0;
 led2 = 0;
 led3 = 0;
 led4 = 0;
 led5 = 0;
 led6 = 0;
 led7 = 0;

}

void turn_left()
{


 led0 = 1 ;
 led1 = 1;
 led2 = 1;
 led3 = 1;
 led4 = 0;
 led5 = 0;
 led6 = 0;
 led7 = 0;

}

void turn_right()
{


 led0 = 0 ;
 led1 = 0;
 led2 = 0;
 led3 = 0;
 led4 = 1;
 led5 = 1;
 led6 = 1;
 led7 = 1;

}

void delayms(uint xms)
{
    uint i,j;
    for(i=xms;i>0;i--)
      for(j=110;j>0;j--);
}

void bluet() interrupt 4  /*中断接收1个字节*/
{
    if(RI==1)
    {
        RI=0;
          dat=SBUF;
          flag_REC=1;

    }
}

与我联系 ,发送邮件到[email protected]

你可能感兴趣的:(单片机)