2024年2月29日发(作者:迮昕昕)
基于STM32的电子计步器的设计与实现
随着生活节奏的不断加快,留给人们的锻炼时间越来越少,走路和跑步成为人们日常生活中为数不多的运动之一。计步器携带方便,能很好地完成量化运动量的目标。因此,最近几年各种计步器以及计步软件大量出现。鉴于人们对于步数检测准确度的要求以及使用便利的需求,十分有必要设计一套计步算法并应用于相关的计步器。
本设计的研究目的是设计出一款高精度、便携的计步器。本设计的主要难点在于数据滤波算法以及计步检测算法的研究。首先,本设计分析了几种数据滤波的方法,选择了比较适合的卡尔曼滤波算法。接着,分析了现有的几种计步检测算法,包括动态阈值算法和峰值检测算法。发现这些算法都不是很准确,所以本文设计了一种新的计步检测算法,提高了计步检测的精度,为其他研究者在步数检测方面提供了一种较好的解决方案。最后,本设计还采用了TFT彩屏的人机交互界面,可以实时显示卡路里、时间以及步数。
通过实际调试过程中的不断改进,实现了计步器的准确检测。
关键词: 计步器 MEMS传感器 滤波 步数检测
I
目录
1 绪论 ......................................................................................................................................... 1
1.1 研究背景和意义 .................................................................................................................. 1
1.2 国内外研究现状 .................................................................................................................. 1
1.3 章节安排 .............................................................................................................................. 2
2 系统总体设计方案 ......................................................................................................... 3
2.1 设计目标 .............................................................................................................................. 3
2.2 系统架构分析 ...................................................................................................................... 3
2.3 系统方案分析 ...................................................................................................................... 3
2.3.1 佩戴位置选择 ............................................................................................................... 3
2.3.2 MEMS惯性传感器的数据读取 ................................................................................... 4
2.3.3 数据融合与滤波 ........................................................................................................... 5
2.3.4 计步算法 ....................................................................................................................... 8
3 系统硬件设计 ................................................................................................................... 9
3.1 系统硬件电路总体设计 ...................................................................................................... 9
3.2 单片机最小系统设计 .......................................................................................................... 9
3.3 MEMS传感器 .................................................................................................................... 10
3.4其他外围电路 ..................................................................................................................... 11
3.4.1 电源转换 ..................................................................................................................... 11
3.4.2 TFT彩屏电路 .............................................................................................................. 11
3.4.3 无线串口通信 ............................................................................................................. 12
4 系统软件设计 ................................................................................................................. 13
4.1 系统软件总体设计 ............................................................................................................ 13
4.2中断设计 ............................................................................................................................. 14
4.2.1 定时器中断 ................................................................................................................. 14
4.2.2 串口中断 ..................................................................................................................... 15
4.2.3 中断优先级判断 ......................................................................................................... 16
II
4.3 MPU6050原始数据采集 ................................................................................................... 16
4.3.1 陀螺仪和加速度计的配置工作 ................................................................................. 16
4.3.2 串行口的配置工作 ..................................................................................................... 17
4.3.3 IIC读取姿态传感器数据 ............................................................................................ 17
4.4 数据处理 ............................................................................................................................ 18
4.4.1 数据类型统一 ............................................................................................................. 18
4.4.2 卡尔曼滤波 ................................................................................................................. 19
4.5 计步算法 ............................................................................................................................ 21
4.6 无线串口通信 .................................................................................................................... 22
5 系统调试 ............................................................................................................................ 23
5.1 系统调试上位机 ................................................................................................................ 23
5.2 标定MPU6050零点 ......................................................................................................... 23
5.3 卡尔曼滤波参数调试 ........................................................................................................ 23
5.4 计步测试 ............................................................................................................................ 24
6 总结与展望 ....................................................................................................................... 25
6.1 总结 .................................................................................................................................... 25
6.2 展望 .................................................................................................................................... 25
6.3 课题研究对环境以及社会的影响 .................................................................................... 26
附录 ............................................................................................................................................ 27
附录一 系统硬件原理图和PCB ............................................................................................ 27
附录二 系统实物图 ................................................................................................................. 28
附录三 系统核心 ..................................................................................................................... 28
III
1 绪论
1.1 研究背景和意义
随着社会不断进步以及生活水平不断提高,人们逐渐开始重视自身的健康。据统计,2015年中国的60岁及以上人口已经占总人口的16.25%,达到2.32亿。预计2020年老年人口将达到2.38亿,老龄化程度将达到17.27%,其中80岁以上老年人口将超过三千万人;除了人口老龄化之外,肥胖问题也日益成为人们所关心的热点问题。根据世界卫计委和国家统计局的调查数据,中国人的肥胖人口正在逐年上升。从1990年到2016年,肥胖率从14%上升到31%,超重率从4%上升到15%。
这些老龄人口与肥胖人口十分需要一款便携式的运动检测装备,通过监控日常的运动量来确保合理的运动量。实验表明,步行和跑步作为一种方便又十分有效的运动,既不需要占用场地,也不容易受到伤害,受到许多人的欢迎。这使得应用于计步的智能穿戴设备迅速发展。
意大利的达芬奇制造了世界上最早的计步器,但现存的最早的计步器是1775年日本制造的。这种机械式计步器主要是利用摆钟原理进行工作,摆动计步器时,内部的金属球会来回晃动,机械开关开始检测步伐,计步器内部的计数器数值增加。但这种机械式的计步器由于精确度比较差,早已不被人所使用,取代它的是电子式计步器。
1.2 国内外研究现状
国内市场上流行的大多是3D电子式计步器,通过加速度计来测量加速度变化,根据软件算法判断出步数。比起机械式计步器,电子式计步器的精确度有很大提升。现在市场上比较知名的国产品牌主要有乐心、小米、时刻美、爱希亚等。国内的小米手环1代采用40mAh的锂电池进行供电,实现了降低功耗的目的,续航时间大大延长;国产的clingband手环采用光学心率传感器,能够对人体进行心率耗时分析。另外,clingband手环还可以通过高精度的温度传感器来实现对体表温度变化的监测;
国外计步器发展比较早,计步器除了计步的基础功能之外,还具备许多其他的功能。东京大学设计出一款鞋内嵌入式计步器,通过压电材料将运动所产生的机械能转化为电能,直接给计步器供电;韩国的SAMSUNG开发出一款拥有操作系统和预装软件的电子计步器,可以播放音乐、视频聊天和接听电话。
1
从国内外现状可以看出,计步器设计不仅需要考虑续航能力、舒适度、外观等方面,还需要考虑如何获取更多的健康数据以及如何保证健康数据的准确性。
1.3 章节安排
第一章:绪论,主要介绍计步器发展的研究背景与意义,并详细叙述下国内外技术上的创新。
第二章:系统总体方案设计,简单介绍下计步器的设计目标,根据设计目标对系统的整体框架进行设计,对具体的系统方案进行选择。
第三章:系统硬件电路设计,主要介绍了基于STM32的单片机最小系统、电源转换、MEMS惯性传感器、TFT彩屏显示、无线通信等硬件电路的设计。
第四章:系统软件设计,首先介绍整个计步器的软件设计思路,接着介绍MEMS惯性传感器的数据获取、卡尔曼滤波、步数检测等具体部分的软件设计思路。
第五章:系统调试,首先调试惯性传感器的参数,最后测试计步器的准确度性能。
第六章:总结与展望,总结了计步器设计所完成的设计目标,列举了计步器设计过程中所遇到的特殊问题。在计步器实现的功能上进行了展望,举出未来可以添加的功能。
2
2 系统总体设计方案
2.1 设计目标
本设计是一款电子计步器,用来量化用户的运动量。考虑到携带的方便,产品设计一定要紧凑以节省空间,看起来美观大方。同时,要很好地考虑到用户的体验。用户可以随时看到自己行走的步数,以及其他的一些健康数据,比如卡路里、运动时间。另外,计步器的最主要功能是计步,所以设计时一定要保证电子计步器的计步准确性。
2.2 系统架构分析
电子计步器需要对大量的数据进行处理,所以需要采用一款运算性能较强的单片机作为主处理器。电子计步器主要是对人体位置的移动进行评估,因此需要选择一款合适的姿态传感器。主处理器通过姿态传感器得到加速度信号和角速度信号,对这两种信号进行处理,得到相对准确的角度变化。主处理器再对角度信号进行步数检测,得到准确的步伐。此外,可以添加显示屏、按键以及无线通信,方便数据的显示以及调试。系统的整体框架如图2.1所示。
图2.1 系统的整体框架图
2.3 系统方案分析
2.3.1 佩戴位置选择
3
要实现步数检测首先需要对人行走和跑步时的姿态有一定的了解。人体在行走时,身体各部分都在运动,都会产生相应的周期性正弦加速度信号。一般来讲,用脚部的加速度信号进行对步数的检测,是比较准确的。但考虑到美观性以及携带方便性,一般不将计步器佩戴在脚部。目前大部分计步器是利用手腕或腰部的运动来检测步数。本设计是一款比较常见的手腕式计步器。
图2.2 手腕式计步器
图2.3 腰部式计步器
2.3.2 MEMS惯性传感器的数据读取
在分析行走的特征参数时,通常选择加速度和角速度作为相关变量。MEMS惯性传感器中包括加速度三个轴和陀螺仪三个轴,分别与人体运动的三个方向相对应。在表征加速度和角速度时,通常要选择变化最明显的轴。在实际场合时,因为传感器的佩戴位置不同,传感器的的测量值并不直接对应人体运动的三个方向,而对应传感器三个轴上的投影值。因此不应该简单地认为行走时某轴的变化一定最大,而应该通过观察MEMS惯性传感器的原始数据来提取合适的加速度和角速度信号。
因为本设计的佩戴位置已经确定,所以在行走过程中,会有固定的加速度轴和陀螺仪轴数据波动比较明显。为了接下来数据处理的方便,本设计将加速度计和陀螺仪三个轴的数据通过无线通信发送到电脑上,确定好加速度计和陀螺仪变化比较明显的那个轴,只用那两轴数据来表示加速度和角速度。
图2.4 人体运动模型
4
图2.5 MPU6050的各个轴
2.3.3 数据融合与滤波
得到的MEME传感器原始数据包括角速度值和加速度计值。加速度计长时间测量的精度比较高,短时间测量会因为噪声而产生一定的偏差。陀螺仪则相反,短时间测量比较精确,长时间测量由于温漂会产生误差。因此,本设计将两者进行数据融合,以确保测量数据的准确性。
数据融合是对不同来源的数据进行综合分析后,最终得到状态估计。换而言之,就是将不同数据融合成一个新数据,便于接下来的数据处理。在实际的工程中,单个传感器的测量值未必能够真实反映物体的状态。所以实际运用中大多使用多个传感器,通过不同属性的传感器信息来得到准确的状态。这些年来,得益于微机电系统的迅猛发展,工业中开始广泛应用各种形式的传感器技术。对采集到的声音、图像、文字等进行数据融合,可以得到物体的真实状态。
有许多数据融合算法可以进行多传感器的数据融合。
(1)加权平均法处理的数据必须是同一类型的数据,根据数据来源的不同,对多个数据赋予不同权重,使得融合出来的新数据能够反映物体的真实状态。
(2)分组估计法是对采样数据进行分组处理,先计算出每一组数据的均方差,再赋予相应的权重,最后得到相应的数据融合值。
(3)卡尔曼滤波法是一种基于时域的滤波方法,对于具有高斯噪声的线性系统,可以得到关于系统状态的最优估计。卡尔曼滤波法是一种进行最优估计的数据处理算法,并且它采用递归计算,在控制、通信以及导航方面应用很广,可以用于传感器数量较少的数据融合。卡尔曼滤波法用在数据融合时,使用一个传感器值作为此刻的测量值,由多个传感器值得到下一个时刻的预估值,最后再由此刻的测量值和上一时刻的预估值计算出此刻的最优估计值。
(4)贝叶斯估计法会把数据看作具备某种分布特征的随机变量,允许我们通过观测值以及之前总结出来的规律对数据进行估计。用在数据融合时,通常适用于传感器数量较多、数据量庞大的场合。
本设计要对加速度信号和角速度信号进行数据融合,得到拟合角度值,方便接下来的计步算法。卡尔曼滤波法不仅能够达到实时处理的目标,还可以滤除噪声,十分适合本设计。
5
为了简化接下来的运算,本设计将采集得到的加速度信号和角速度信号统一成角度信号,再进行卡尔曼滤波。
① 加速度转化成角度
将芯片水平水平放置在平台上, Y轴与X轴方向的重力分量都是0,而Z轴方向的重力分量是g。垂直向下时,加速度轴各个方向的重力分量如图2.6所示。
图2.6 垂直向下示意图
将芯片稍微倾斜一定角度,地面坐标系如图2.7所示。Ax是X轴方向的加速度,Ay是Y轴方向的加速度,Az是Z轴方向的加速度。α1是X轴与水平线的夹角,α是X轴与竖直方向的夹角。β1是Y轴与水平方向的夹角,β是Y轴与竖直方向的夹角。γ1是Z轴与水平方向的夹角,γ是Z轴与竖直方向的夹角。
图2.7 旋转α°地面坐标系示意图
从图2.7中可以看出各夹角之间的关系
α = 90°− α1, β = 90°− β1 , γ = 90°− γ1 (式2.1)
各轴上的重力加速度分量
6
Ax = gcosα, Ay = gcosβ , Az = gcosγ (式2.2)
结合式2.1和式2.2可得到
Ax ==gsinα1 , Ay =gsinβ1 , Az = gsin γ1 (式2.3)
由g2= (gsinα1 )2+ (gcosα1)2,则
gcosα1 =g2-Ax2, gcosβ1 =g2-Ay2,gcosγ1 =g2-Az2 (式2.4)
倾斜一定角度,载体坐标系如图2.8所示。结合立体几何的知识,立方体的对角线就是重力加速度g,立方体的长宽高就是Ax、Ay、Az,图中的虚线大小等于 Ay2+Az2。根据勾股定理Ax2 + Ay2 + Az2 = g2,并结合式2.4,可得:
tanα1 =
AxAyAz22 ,tanβ1 =AyAxAz22, tanγ1 =AzAxAy22 (式2.5)
图2.8 旋转α°载体坐标系示意图
那么弧度值可以表示为
α1 = arctan(
AxAyAz22),β1= arctan(AyAxAz22),γ1= arctan(AzAxAy22) (式2.6)
最后得到的各轴的角度值分别为
θx=arctan(
180)× (式2.7)
22πAyAzAxθy =arctan(180)× (式2.8)
22πAxAzAy7
θz = arctan(
180)× (式2.9)
22πAxAyAz②角速度转换成角度:只要对角速度进行积分就可以得到角度。
2.3.4 计步算法
本设计需要对卡尔曼滤波处理后的角度变化数据进行计步分析。在步行和跑步两个运动状态中,臂部、手腕、脚和腿部的动作都可以视作钟摆运动,这些身体部位的角度信号呈现出一种正弦变化。根据人体运动的特征,可将计步算法大致分为两类。
一是峰值判断法,每走一步角度信号会有一个相对明显的峰谷值,所以可以通过对角度的峰值检测,得到有效的行走步数。正弦信号的峰值处一般就是拐点,此处的斜率为零。但是由于采样频率有限,采样数据呈现离散的状况,所以只能根据相邻两个采样点的斜率是否趋近于零,来判断峰值的出现。只有当峰值两侧的正斜率点和负斜率点数目相等时,才认定为走了有效的一步。
二是动态阈值判断法,当角度数值从大到小或从小到大穿越某个设定值时,可认定为走了一步,这个设定值称作阈值。因为周围环境会对人的步伐大小产生影响,阈值最好是根据实时采样数据计算得到的。一般取最小值和最大值的平均数、取一段时间内采样数据的中位数作为动态阈值。只有当相近的采样值连线呈现下降态势且穿过动态阈值,才可以记为有效的一步。
对于峰值判断法来讲,一些毛刺噪声会被误判为峰值,从而影响步伐检测的精度。对于动态阈值判断法来讲,当人体步伐大小发生改变时,会漏掉一些步伐。所以本设计将峰值检测和动态阈值结合起来,并且将时间窗加入其中,可以滤除一些无效振动并且可以更准确地判断出峰值。查阅资料可知,快跑的最快频率约为每秒5步,慢走的最慢频率约为每秒0.5步,所以将峰值附近0.2S~2S之间的数据作为有效数据,超过这个范围的数据均看作无效数据。之后将峰值相邻的有效数据再进行一次比较,确保找到真实的峰值以及比较合适的阈值,保证步伐检测的精准度。
8
3 系统硬件设计
3.1 系统硬件电路总体设计
本文第2章对整个系统进行了方案分析,本章也将对设计中的硬件部分进行分析。以基于STM32F103系列单片机的最小系统作为主控核心,其他的外围电路包括电源转换、TFT彩屏显示、MPU6050姿态传感器等。硬件方面的整体框架如图3.1所示。
图3.1 系统整体硬件框架图
3.2 单片机最小系统设计
本设计考虑到计步算法的运算过程相当复杂,选用了目前市场上性价比相对较高、运算性能较好的STM32F103系列单片机。
STM32F103系列使用高性能的ARM_M3内核,工作频率高达70MHz,内置高速缓存以及静态RAM。其I/O口资源比较丰富,可以同时与多个外设相连。该型号单片机同时具备ADC采集、定时器以及输入捕获功能,支持IIC协议、SPI协议和CAN协议,拥有USART接口和USB接口。
单片机最小系统包含了主控芯片、时钟电路、BOOT自举电路、复位电路等,它关系着整个硬件系统能否良好运转,所以单片机的设计必须要十分细致,尽可能考虑到各种突发状况对单片机运行造成的影响。
9
图3.2 STM32F103C8T6最小系统板
整个系统需要的所有时钟都是时钟电路提供的。而复位电路的作用在于,当整个系统由于某个原因出现程序跑飞现象时,可以通过复位按钮使单片机重启。STM32一般带有BOOT自举电路,用来选择单片机的工作模式,本设计将BOOT1和BOOT2两个引脚同时接地,使单片机处于正常工作的状态。
3.3 MEMS传感器
MEMS惯性传感器通过测量惯性敏感元件在载体坐标系中的线运动和角运动,计算得到载体的角速度和加速度。它一般集成了磁力计、陀螺仪以及加速度计,可以用在自主定位以及自主导航中。MPU6050姿态传感器就是一款低成本MEMS惯性传感模块。为了实现对步数的实时检测,本设计使用了MPU6050姿态传感器中的加速度计和陀螺仪。下面简单的对该姿态传感器进行讲解。
MPU6050姿态传感器工作电压为2.4V-5V,具备功耗低以及体积小的优势。该装置拥有II2C和SPI的通信接口,可以输出欧拉角、加速度计、温度、陀螺仪等信息。MPU6050加速度参数的测量范围分别为±2g、±4g、±8g、±16g,角速度的测量范围分别为±250°/s、±500°/s、±1000°/s 和±2000°/s。
本设计通过II2C进行读取MPU6050的数据,SCL接单片机的PB6,SDA接单片机的PB7。本设计中MPU6050姿态传感器的原理图如图3.6所示。
10
图3.6 MPU6050姿态传感器
3.4其他外围电路
3.4.1 电源转换
电源转换出来的电压是否达到要求,这关系到系统能不能正常运行,因此需要谨慎进行电源部分的电压转换。本设计所采用的芯片都是使用3.3V电源供电,因此只需要一个3.3V的稳压电路。在保证压降以及纹波较小的情况下,本设计采用AMS1117-3.3输出3.3V的电源。AMS1117-3.3是一种正向低压降稳压器,适用于稳压器电池、线性开关电源以及充电器等。它的成本较低,转换效率高,外部电路设计起来十分简单。AMS1117-3.3稳压电路原理图如图3.7所示。
图3.7 AMS1117-3.3稳压电路
3.4.2 TFT彩屏电路
本设计需要将计算所得的步数实时显示出来,本设计选用了性价比高的TFT彩屏进行显示,下面对该TFT彩屏进行简单描述。
该款显示屏的分辨率为125*125,尺寸为1.45寸,采用SPI协议进行通信,工作电压范围在2.4V-3.3V之间。要实现彩屏显示字符、汉字以及字符串的目的,首先要编写彩屏11
的驱动程序,然后再根据自己所要实现的功能编写应用程序。驱动程序相当于一个字符库,应用程序调用其中的字符,字符就会在彩屏上显示出来。
本设计中用3.3V对TFT彩屏进行供电,3.3V电源通过上拉电阻接TFT_LED,TFT_SCK接单片机的PA5,TFT_SDA接单片机的PA7,TFT_AO接单片机的PA4,TFT_RST接单片机的PA3,TFT_CS接单片机的PA2。TFT彩屏的接口图如图3.8所示。
图3.8 TFT彩屏接口图
3.4.3 无线串口通信
在进行调试时需要将姿态传感器MPU6050得到的原始数据发送到电脑上,这时可以选择无线串口模式、蓝牙模式或WIFI模式。本设计采用的是无线串口模式,选择透传模式就不需要进行配置,十分方便简单。无线串口进行主从机配对之后,就可以将数据通过无线发送到电脑上。
本设计采用HJ-WUS1无线模块,输入电压范围为2.40V-3.50V,最大工作电流不能超过50mA。该模块传输距离达到34米以上,并且它支持无线透传模式。在本设计的原理图中,TX1接单片机PB10/USART3_TX,RX1接单片机PB11/USART3_RX。
12
4 系统软件设计
4.1 系统软件总体设计
第二章和第三章重点讲述了计步器的总体系统方案以及硬件电路设计,本章主要进行述计步器的软件部分设计。软件也是系统整体设计的一个重点,软件部分关系到设计的功能能否正常实现。本设计的软件部分主要包括系统初始化、MPU6050原始数据采集、卡尔曼滤波、无线串口通信和TFT彩屏界面设计等。本设计整体软件框架如图4.1所示。
图4.1 软件框架图
整个系统软件执行后,首先进行的是系统初始化,其中包括了定时器的初始化、TFT彩屏初始化、IO口的初始化、MPU6050初始化、串口初始化等。然后再进行MPU6050原始数据(加速度信号和角速度信号)的读取,如果没有读取到数据则等待。当读取到加速度信号和角速度信号之后,将它们转化成角度信号,进行卡尔曼滤波。接着对经过滤波、13
融合的角度数据进行步数检测。检测得到的步数可以通过无线通信发送给电脑进行调试,也可以发送给TFT彩屏显示。
4.2中断设计
4.2.1 定时器中断
本设计在采集MPU6050原始数据时,需要用到定时器来确定采样频率。定时时间到,通过两个IIC口采集50个数据。定时器中断初始化流程图如图4.2所示。
图4.2 定时器初始化流程图
使能定时器TIM时钟。本设计使用的是定时器3,由图4. 3(STM32的时钟树)可以看出,定时器3的时钟是挂在总线APB1上面,所以打开定时器3需要使能总线APB1。这时定时器的时钟来源于总线APB1的一个倍频器,大小为2×36Mhz=72Mhz。
TIM3的计数模式分为向上向下双向计数、向下计数以及向上计数。本设计采用向上计数模式,计数器从0计数到TIM3_ARR中的自动重装值,再进入中断服务程序,最后还会返回重新从0开始计数。
14
图4.3 定时器的时钟树
定时器的定时时间计算公式:TIME=((TIM3_PSC +1)×(TIM3_ARR +1))/72Mhz;本设计的定时器寄存器配置为TIM3_ARR=71、TIM3_PSC=4999,所以定时时间为
((71+1)×(4999+1))/72Mhz=5ms (式4.1)
4.2.2 串口中断
本设计在进行调试时,需要将数据通过无线串口发送到电脑上。本设计使用的STM32F103系列单片机拥有4路串口,串口支持同步通信以及异步通信。串口中断初始化流程图如图4.4所示。
图4.4 串口中断初始化流程图
15
STM32F103系列单片机中串口是挂载在 APB2 下面的外设,所以串口使能也需要使能总线APB2。串口参数主要包括:字长,停止位,波特率,奇偶校验位等。本设计主要使用串口3,将波特率配置为115200,字长设置成8位数据,停止位为1位,没有奇偶校验。
4.2.3 中断优先级判断
本设计中同时使用了串口3中断和定时器3中断,所以需要合理配置好中断优先级。每个中断都对应着一个外设,每个外设都包含许多个中断源或中断事件,这些中断可以通过特定的中断通道向主处理器请求中断。软件设计中一般会通过配置中断控制器完成中断优先级的选择。ARM_M3内核芯片可以配置256个中断优先级,STM32的NVIC是ARM_M3内核芯片的NVIC的子集。表4.1就是STM32的NVIC中断优先级配置表。
表4.1 NVIC中断优先级配置表
NVIC优先级组
NVIC_优先组0
NVIC_优先组1
NVIC_优先组2
NVIC_优先组3
NVIC_优先组4
NVIC_IRQ抢占优先级
0
0-1
0-3
0-7
0-15
NVIC_IRQ次优先级
0-15
0-7
0-3
0-1
0
描述
1位抢占优先级3位次优先级
0位抢占优先级4位次优先级
2位抢占优先级2位次优先级
3位抢占优先级1位次优先级
0位抢占优先级4位次优先级
经过实际调试,本系统的中断优先组别选择NVIC_PriorityGroup_2,中断优先级为:USART3 优先级大于TIM3。
4.3 MPU6050原始数据采集
本设计只用到9轴MPU6050姿态传感器中的加速度计和陀螺仪,主要用来测量加速度和角速度。具体数据采集程序的整体设计思路是,首先做好陀螺仪和加速度计的配置工作,并且确定好串行控制口。接着,因为该惯性传感器内包含AD转换器,可以将采集到的模拟量转换成数字量,所以只要再将数字量通过I2C接口传输到主处理器中,就可以完成对MPU6050姿态传感器原始数据的采集。MPU6050原始数据采集的流程图如图4.5所示。
4.3.1 陀螺仪和加速度计的配置工作
16
因为人体行走频率不是很高,所以加速度计和陀螺仪的采样频率也不需要配置得过高,本设计采用500Hz的频率,配置角速度量程为±2000°/S, 配置加速度量程为±8g。
4.3.2 串行口的配置工作
配置完加速度计和陀螺仪之后,需要通过I2C接口对6个轴的原始数据进行实时读取。由于STM32的硬件IIC设计得十分复杂,所以本设计采用软件模拟IIC的办法。定义时钟总线接口为PB6,定义数据总线接口为PB7。
#define IIC0_SCL PBout(6) //SCL
#define IIC0_SDA PBout(7) //SDA
图4.5 MPU6050原始数据采集软件设计
4.3.3 IIC读取姿态传感器数据
MPU6050姿态传感器通过IIC发送的一个数据帧中依次为加速度3轴数据、陀螺仪3轴数据、欧拉角数据以及温度数据,由于本设计中只需要陀螺仪数据和加速度数据,所以只读取前面的数据即可。但是因为加速度计信息和陀螺仪数据都是16位的,而这些数据分别存放在两个8位寄存器中,所以要将两个8位数据转换成一个16位数据。本设计先将高8位数据左移8位,再加上低8位寄存器中的数据,形成需要的16位加速度计信息和陀螺仪数据。void Data_get_6050(int* AX, int* AY, int*
AZ,int* GX, int*GY, int* GZ)
{iicDevRead(MPU6050_ADDR,MPU6050_
Accelerometer, 12, Registers);
17
*AX=(((s16) Registers[0]) << 8)| Registers [1];
*AY= (((s16) Registers[2]) << 8)|Registers [3];
*AZ=(((s16)Registers[4])<< 8) | Registers[5];
*GX=(((s16) Registers[6])<<8) | Registers[7];
*GY=(((s16)Registers [8])<<8) | Registers[9];
*GZ=(((s16)Registers[10])<<8)|Registers[11;}4.4 数据处理
由2.3.3、4.2.3可知,得到的加速度信号和角速度信号都是16位的,本设计要采用卡尔曼滤波进行数据融合处理。为了方便接下来的计算,先将加速度信号和角速度信号都统一为角度信号。再采用卡尔曼滤波,从而滤除噪声,得到较为理想的角度变化波形。数据处理的具体流程如图4.6所示。
图4.6 数据处理流程图
4.4.1 数据类型统一
因为采集值都是16位2进制的加速度计值和角速度值,十分不利于运算,所以要先将两者统一为为角度信号。由2.3.3可知,加速度信号和角速度信号是如何转化成角度信号。数据归一化的具体代码实现如下,
Accelerometer _X=Get Accelerometer _X( ); //IIC读取X轴加速度计
Accelerometer _Ax = (Accelerometer _Z - 1100) /16384; //去除加速度计的零点偏移 Accelerometer _Ax = Angle_Ax*1.2*180/3.14; //将加速度的弧度转换为度
Gyroscope _y = GetAngle_Z(); //IIC读取Z轴陀螺仪
Gyroscope _y = (Gyroscope _y + 30)/16.4; //去除陀螺仪的零点偏移,计算角速度值
18
Gyroscope _y= Gyroscope _y+ Gyroscope _y*0.01; //角速度积分得到角度
4.4.2 卡尔曼滤波
卡尔曼滤波以最小均方根作为估计的最佳准则,采用噪声、输入信号的状态空间模型,利用前一刻的状态估计值和现在的测量值来更新对状态变量的估计,求得此刻的状态估计值。因为卡尔曼滤波采用不断递归的思想,只会保留上一刻的协方差,因此十分适用于数据实时处理。本设计采用简单的卡尔曼滤波算法,首先通过陀螺仪计算得到此刻的角度测量值。接着由加速度计算得到的角度与陀螺仪计算得到的角度,得到下一时刻的角度预估值。最后通过上一时刻的角度预估值和此刻的角度测量值,得到此刻的最优角度估计。卡尔曼滤波的具体流程图如图4.7所示。
图4.7 卡尔曼滤波流程图
保持计步器水平放置,快速改变计步器角度,之后再快速恢复到水平位置,观察滤波前后角度变化的波形。图4.8即为滤波前的原始数据,可以看到数据数值较大,不太利于计算,而且曲线上升缓慢,快速跟随性比较差,输出中也存在较大的噪声。图4.9为经过卡尔曼滤波后的融合角度值。从图中可以看出,经过卡尔曼滤波后,角度曲线的跟随性和快速性都得到显著的提升,同时噪声干扰也少了好多,曲线变得更加平滑。
19
图4.8 滤波前原始数据
图4.9 滤波后的数据
图4.8、图4.9中,X轴为时间,Y轴为角度值。从图中可以看出,卡尔曼滤波后的图像出现了过冲现象,由于过冲不是很明显且不影响接下来的计步,所以可以不改动相关参数。卡尔曼滤波具体代码实现如下
DoubleFilter_Kalman(doubleAccelerometer_num,double Gyroscope_num)
angle = angle + Gyroscope_num *0.001f;
//最优角度估计值
{ static double angle=0; //最优角度的初值 p=p+q;
static double p=0.000001;
//预测噪声方差矩阵
static double q=0.000001;
//协方差最优估计矩阵
static double k=0; //卡尔曼增益
k=p/(p+ Gyroscope_num);
// 计算卡尔曼增益
angle = angle +k*(Accelerometer_num- angle);
//卡尔曼增益修正角度
p=(1-k)*p; //更新协方差最优估计矩阵
20
return angle; }
4.5 计步算法
本设计中,核心部分就是计步算法。根据之前得到的角度变化信息,通过合适的计步算法以确定运动步数。由2.3.4可知,本设计打算将峰值检测和动态阈值结合起来,形成一种融合的计步算法。
因为人快跑的速度最多达到每秒5步,这种频率不是很快,所以本设计先采用了一个低通滤波算法。计步算法中设定两个移位寄存器Data_new和Data_old,当新得到一个角度信号时,用Data_new寄存器中的数值与新的角度信号进行比较,若两者相减的绝对值比预定精度小,则Data_old寄存器的新值就是Data_new寄存器值,而Data_new寄存器的新值是新的角度信号;当两者相减的绝对值比预定精度大时,新得到的角度信号被抛弃,Data_new寄存器数值保持不变,Data_old寄存器的新值依旧是Data_new寄存器的数值。
图4.10 计步算法示意图
峰值出现的条件是,有曲线穿越动态阈值的下方,并且角度曲线的斜率为负值(Data_new 21 4.6 无线串口通信 调试过程中,本设计将数据通过无线串口传输到电脑上。为了观察方便,将2进制的原始数据转换成10进制数据。之后上位机软件会将采样得到的离散信号拟合成连续信号,形成波形,在电脑上显示出来。 为了明确数据通信是否正常,本设计在数据通信中采取了最常用的CRC16校验方式。CRC即循环冗余校验码,具备数据传输校验的功能。校验的主要过程,首先对数据进行多项式计算,结果被称为冗余码,将结果放在发送数据的末尾。随后接收装置也使用CRC对接收的数据进行检验,这样就清楚数据通信是否正常无误。 在数据检错中最关键的就是冗余码的产生,而冗余码产生的步骤如下:首先,用2n乘以M,相当于把n个0添加到M的后面。接着,用得到的结果除以事先双方约定好的 (n+1)位除数P,余数R就是冗余码。如果传输过程中没有出现任何差错,那么通过CRC检验得到的余数R必为0,否则就会产生误码。 无线串口通信的代码具体如下 void Data_transmission (void) { int Temporary0 [4] = {0}; unsigned int Temporary 1 [4] = {0}; u8 Data_ cache [10] = {0}; u8 i; u16 CRC_16 = 0; for(i=0;i<4;i++) { Temporary 0[i] = (int) Data_ cache [i]; Temporary1[i]=(unsigned int) Temporary 0[i];} for(i=0;i<4;i++) {Data_cache[i*2]=(u8)(Temporary1[i]%256); Data_cache[i*2+1]=(u8)(Temporary1[i]/256);} CRC_16 = CRC_CHECK(Data_ cache,8); Data_ cache [8] = CRC_16%256; Data_ cache [9] = CRC_16/256; for(i=0;i<10;i++) {printf("%c", Data_ cache [i]); }}22 5 系统调试 经过一段时间的软硬件设计之后,完成了简易电子计步器的初步搭建。本设计是一款电子计步器,对于自身的计步精确度提出了很高的要求。为了能够做到计步足够准确,如何进行系统调试就显得尤为重要。 5.1 系统调试上位机 在本设计的整个调试过程中需要对许多参数进行观测,所以为了调试的方便,本设计采用了上位机调试软件。这款上位机可以自动搜索串口,无需查看端口号。并且这款上位机总共可以同时查看4个通道的数据,十分方便。可以暂停和保存波形,给数据处理带来便捷。 5.2 标定MPU6050零点 MPU6050的陀螺仪和加速度计在测量过程中会出现数据漂移的问题,为了保证步数计算的精确,需要标定MPU6050的零点。使计步器保持在手臂垂直向下的角度,采集此刻陀螺仪和加速度计的值,这就是MPU6050零点值。但是在实际运用过程中由于数据漂移的影响,测量零点值一定会产生误差。本设计连续采集十次零点值,取它们的平均值作为零点值,以减小数据漂移给计步精度所带来的影响。陀螺仪Z轴和加速度计X轴的十次采集值如表5.1所示。 表5.1 陀螺仪和加速度计的采集值 次数 1 2 3 4 5 陀螺仪Z轴 -42 -39 --41 -38 -40 加速度计X轴 3738 3737 3745 3742 3741 次数 6 7 8 9 10 陀螺仪Z轴 -42 --41 -40 -41 -37 加速度计X轴 3742 3740 3741 3738 3739 5.3 卡尔曼滤波参数调试 由于加速度计和陀螺仪在采集数据方面都具有一定的局限性,所以本设计采用了卡尔曼滤波将两者结合起来,以保证计步的精确性。卡尔曼滤波参数调试主要是调节角速度积分时间dt和卡尔曼增益比例Gyro_K,角速度积分时间dt主要是用于估算陀螺仪所得到的23 角度,Gyro_K主要是用来计算出卡尔曼增益k。k越大,卡尔曼滤波的拟合角度就会越靠近测量值;k越小,卡尔曼滤波的拟合角度就会越靠近预估值。 卡尔曼滤波调节参数时,首先要调节角速度积分时间dt。因为角度值=角速度×时间,所以当增大积分时间时,角度输出变化就会越快,滤波后曲线的跟随性更强。dt初始给定值为0.0001,此时调节MPU6050姿态传感器的倾斜角度,结果输出角度值跟随较慢。接着每次以0.0002速率缓慢增大dt值,发现输出角度值的变化速度慢慢增大,此时继续加大dt值直到输出角度值发生过冲、震荡现象。 接着保持dt值不变,改变卡尔曼增益比例Gyro_K的值。因为Gyro_K在拟合角度过程中起到了微分控制的功能,所以可以通过改变Gyro_K使输出角度的过冲、震荡现象减弱。通过不断的调试角速度积分时间dt和卡尔曼增益比例Gyro_K,最终确定了dt=0.0011,Gyro_K=0.007。这时卡尔曼滤波拟合角度曲线跟随性好且没有出现过冲。 5.4 计步测试 计步器软硬件设计完成后,要通过实验来验证计步器的计步效果,检查其是否达到了预期的设计目标。对于运动来说,步数是一个可以量化的指标,因此步数检测的精确度也成为衡量计步器的一个标准。计步器的精确度越高,越能反映出人体的运动量大小。计步精度的定义如下:精确度=|1-计步数-实际步数|×100%。 实际步数表5.1 计步器精确度测试 实验者 1 2 3 4 5 实际步数 100 100 100 100 100 计步数 98 97 103 104 102 误差 -2 -3 +3 +4 +2 精确度 98% 97% 103% 104% 102% 表5.1是5个实验者的行走测试结果,结果发现误差基本上都在±5步内,基本上达到了设计目标,相对误差也不大。由于起步不稳定以及周围环境的影响,几步的误差完全可以接受。 24 6 总结与展望 6.1 总结 本设计以姿态传感器MPU6050为基础设计了一款电子计步器,利用姿态传感器MPU6050检测采集数据,通过陀螺仪和加速度计归一化得到角度变化。再利用变化的角度值进行步数检测,最终实现电子计步器的计步过程。 本设计经过不断的调试与改进,获得的成果如下: 1、完成了计步器的硬件架构设计,并进行硬件选型。对采集的加速度信号和角速度信号进行归一化处理,再使用卡尔曼滤波得到相应的角度信号。实验发现,经过卡尔曼滤波的角度信号变得更加平滑,噪声少,便于计步算法的实现。 2、根据运动时手腕处周期性角度变化,以及观察到的波形特征,设计了一套应用于手腕上的步数检测方法,并把该计步方法应用到电子计步器上。 3、设计了一套采集加速度信号的系统,可以对各种运动状态下的加速度数据进行观察。采集系统通过无线串口和电脑连接,加速度数据在电脑上位机上以波形方式显示。 4、对设计出来的电子计步器进行步数检测实验评估,实验结果表明能够较好地完成设计目标,步数误差在5%左右,很好的满足了设计要求。 6.2 展望 市场上的电子计步器有很多类型,但关于计步器的研究仍在不断继续,计步器的功能变得越来越多且越来越复杂。对于本设计有以下几点展望: 1、选用功耗低的锂电池或者纽扣电池进行供电,降低功耗,延长续航时间。 2、能够减小电路板的体积,并且给它安装上外壳,使它更加方便携带。 3、试验中采集加速度数据时,运动速度和步伐相对恒定。但是在日常生活中由于周围环境的影响,人的运动速度和步伐是不断变化的,在未来的研究中希望对不同速度下的人体运动进行识别。 4、手腕式计步器由于不规则的手腕摆动,步数检测不是很精确。在未来进一步的研究中希望对运动状态进行特征分析,深入探索各种特征分类器,选择合适的方法来提高手腕式计步器的精确度。 25 6.3 课题研究对环境以及社会的影响 电子式计步器是由于人们进行跑步时需要计算步数才产生的,后来还拥有了对卡路里、心率、体表温度、睡眠质量的监测功能。科学研究表明,使用计步器可以促使人们每天多走3000多步,这不仅锻炼了人的身体,还节省日常出行所需要消耗的燃料,既节能又环保。 如今,中国人口老龄化逐年提高,同时因为生活水平的改善也产生了大量的肥胖人口,这不利于整体社会的发展。当计步器能够深入到人们生活中去,人们就会逐步养成一种勤于锻炼的好习惯。老年人锻炼可以预防一些心血管疾病,肥胖人口锻炼可以展现出积极向上的精神外貌,这些都会促进社会主义精神文明建设,加快富强、民主的社会主义强国的建设步伐。 26 附录 附录一 系统硬件原理图和PCB 27 附录二 系统实物图 附录三 系统核心 /∗∗∗∗∗∗∗清零步数∗∗∗/ void Liquidation_S(u32 ∗Value) {u32 Value_B=∗Value; while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0); Value_B=∗Value; ∗Value=0; Step_Count_B=∗Value; TFT_PutString( 40,0," 00000²½",RED,BLACK);} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); 28 ∗Value=Value_B;} Dis_Buff[0]=∗Value/10000+′0′; TFT_PutChar(40,8,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,16,Dis_Buff[1], RED,BLACK); TFT_PutChar(40,24,Dis_Buff[2], RED,BLACK); TFT_PutChar(40,32,Dis_Buff[3], RED,BLACK);TFT_PutChar( 40,40,Dis_Buff[4],RED,BLACK); {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} }} /∗∗∗∗∗清零时间∗∗∗∗∗∗/ void Liquidation_T(u32 ∗Value) {u32 Value_B=∗Value; while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0); Value_B=∗Value; ∗Value=0; TFT_PutString( 40,0," 00:00:00",RED,BLACK);}elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); ∗Value=Value_B;} T_H=∗Value/3600; T_M=∗Value%3600/60; T_S=∗Value%3600%60; Dis_Buff[0]=T_H/10+′0′; Dis_Buff[1]=T_H%10+′0′; TFT_PutChar(40,8,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,16,Dis_Buff[1], RED,BLACK); Dis_Buff[0]=T_M/10+′0′; Dis_Buff[1]=T_M%10+′0′; TFT_PutChar( 40,32,Dis_Buff[0],RED,BLACK); TFT_PutChar(40,40, Dis_Buff[1],RED,BLACK); Dis_Buff[0]=T_S/10+′0′; Dis_Buff[1]=T_S%10+′0′; TFT_PutChar(40,56,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,64,Dis_Buff[1], RED,BLACK); if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} } } /∗∗∗∗∗卡路里消耗设置函数∗∗∗∗∗∗∗/void Enter_Calories(float∗Calories) {u8 buff[3]={0,0,0}; while(1) if((∗Calories_V)>=9) {(∗Calories_V)=9;}} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); (∗Calories_V)−=0.01; if((∗Calories_V)<=0) {(∗Calories_V)=0;} buff[0]=(u32)((∗Calories_V) ∗100)/100+′0′; buff[1]=(u32)((∗Calories_V) 29 ∗100)%100/10+′0′; buff[2]=(u32)((∗Calories_V) ∗100)%100%10+′0′; TFT_PutChar( 40,8,buff[0],RED,BLACK); TFT_PutChar( 40,24,buff[1],RED,BLACK); TFT_PutChar( 40,32,buff[2],RED,BLACK); if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} } } /∗∗∗∗∗∗彩屏设置函数∗∗∗∗∗∗∗∗/voidSet_Function() {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); TFT_Clear(BLACK); switch(Fun_Sp) {case 1:TFT_PutString(10,0,"1.清零步数",RED,YELLOW); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 2:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,YELLOW); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 3:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,YELLOW); break;} while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0||GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0||GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) delay_ms(50); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0); Fun_Sp−−; 30 if(Fun_Sp<=1) {Fun_Sp=1;}} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); Fun_Sp++; if(Fun_Sp>=FUN_COUNT) {Fun_Sp=FUN_COUNT;}} switch(Fun_Sp) { case 1:TFT_PutString(10,0,"1.清零步数",RED,YELLOW); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 2:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,YELLOW); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 3:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,YELLOW); break;} if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) delay_ms(50); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0); switch(Fun_Sp) { case 1:TFT_Clear(BLACK); TFT_Clear(BLACK); case 2:TFT_Clear(BLACK); TFT_PutString(10,0,"清零时间:",RED,BLACK); TFT_PutString( 40,0," ",RED,BLACK); LiquidationS(&TimeCount); TFT_Clear(BLACK); TFT_PutString(10,"步数: ",RED,BLACK); TFT_PutString(40,0,"时间: : ",RED,BLACK); ∶ TFT_PutString(100,0,"卡路里: ",RED,BLACK); break; 31 case 3:TFT_Clear(BLACK); TFT_PutString(10,0,"清零卡路里:",RED,BLACK); TFT_PutString( 40,0," . ",RED,BLACK); Enter_Calories(&Calories_V); TFT_Clear(BLACK); TFT_Clear( BLACK);TFT_PutString(10,0,"步数: ",RED,BLACK); TFT_PutString(40,0,"时间: ∶ : ",RED,BLACK); TFT_PutString(100,0,"卡路里: ",RED,BLACK); /∗MPU650初始化∗/ bool mpu6500Init(void) {u8temp=iicDevReadByte( bool state = false; delay_ms(50); iicDevWriteByte( state = true; return state;} /∗获取6轴原始数据∗/ /∗void mpu6500GetRawData(s16 ∗ ax,s16∗ ay,s16∗ az,s16∗ gx,s16∗ gy,s16∗ gz) {iicDevRead( ∗ax = (((s16) buffer[0]) break;} break;} } } } Dis_Buff[0]=Step_Count/10000; DisBuff[1]=StepCount%10000 /1000+′0′; TFT_PutChar( 10,40,Dis_Buff[0],RED,BLACK); TFT_PutChar( 10,48,Dis_Buff[1],RED,BLACK); TFT_PutChar( 10,56,Dis_Buff[2],RED,BLACK); TFT_PutChar( 10,64,Dis_Buff[3],RED,BLACK); TFT_PutChar( 10,72,Dis_Buff[4],RED,BLACK);<< 8) | buffer[1]; ∗ay = (((s16) buffer[2]) << 8) | buffer[3]; ∗az = (((s16) buffer[4]) << 8) | buffer[5]; ∗gx = (((s16) buffer[8]) << 8) | buffer[9] ∗gy = (((s16) buffer[10]) << 8) | buffer[9]; ∗gz = (((s16) buffer[12]) << 8) | buffer[13];} s16 GetAngleX() {s16 roll;float ax,ay,az,gx,gy,gz; roll = gx; 32 return roll;} s16 GetAngleY() {s16 pitch; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); pitch = gy; return pitch;} s16 GetAngleZ(){ s16 pitch; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); pitch = gz; return pitch;} s16 GetAccelX() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = ax; return roll;} s16 GetAccelY() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = ay; return roll;} s16 GetAccelZ() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = az; return roll;} /∗卡尔曼滤波∗/ double Kalman_Filter( double angle_m,double gyro_m) {static double x=0; static double p=0.000001; static double Q=0.000001; static double k=0; x=x+ gyro_m∗0.001f; p=p+Q; k=p/(p+GyroR); x=x+k∗(angle_m−x); p=(1−k)∗p; return x; } /∗数据归一化∗/ void Angle_get() {GyroYo=GyroY; GyroY=GetAngleZ(); if(GyroY<32768) GyroY+=65535;GyroY=GyroY−Gyropitchfloat; if((GyroY−GyroYo) >32768) GyroY−=65535; else if((GyroYo−GyroY) >32768) GyroY+=65535; 33 Gyropitch=GyroY∗Gyrorate; Acc_Zo=Acc_Z; AccZ= GetAccelX( ); if(Acc_Z<32768) Acc_Z+=65535; if((AccZ−AccZo)>32768) Acc_Z−=65535; else if ((AccZo−AccZ)>32768) Acc_Z+=65535; Acc_Z=Acc_Z−Anglefloat; MMA_Angle=Acc_Z∗Anglerate; Angle=Kalman_Filter( MMA_Angle,Gyropitch);} unsigned long Step_Count( float axis0,float axis1,float axis2){ unsigned int nowPos = 0; int ppDiff = 0; int timeDiff = 0; pSta = FALLING_EDGE; newMax = lastPos; walkSta = TRUE;} walkSta = FALSE;} lastPos = nowPos; if(walkSta==TRUE){ walkSta = FALSE; ppDiff = newMax − newMin; if(ppDiff > P_P_DIFF){ timeDiff = GetTime() − lastTime; if(timeDiff < FAST_WALK_TIME_LIMIT_MS){ return stepCount;} else if(timeDiff > SLOW_WALK_TIME_LIMIT_MS){ walkOkSta = FALSE; stepOK = 0; return stepCount;} stepOK++; if(stepOK>=STEP_OK){ walkOkSta = TRUE;} lastTime = GetTime(); }} if(walkOkSta==TRUE){ stepCount += stepOK; stepOK = 0;} return stepCount;} RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); TIM_TimeBaseInit( TIM3,&TIM_TimeBaseStruct); TIM_ClearFlag( TIM3,TIM_IT_Update);TIM_ITConfig( TIM3,TIM_IT_Update,ENABLE); NVIC_Tim3_Configuration(); TIM_Cmd(TIM3,ENABLE);} void NVIC_Tim3_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); NVIC_Init(&NVIC_InitStructure);} void TimerInit(u16 per,u16 psc){ NVIC_InitTypeDef NVIC_InitStructure; 34 RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4,ENABLE); TIM4,&TIM_TimeBaseInitStructure); TIM_ITConfig( TIM4,TIM_IT_Update,ENABLE); TIM_ClearITPendingBit( TIM4,TIM_IT_Update); NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM4,ENABLE); } void USART3_Config(void) {GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE); RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART3,ENABLE); GPIO__Pin = GPIO_Pin_10; GPIO__Mode = GPIO_Mode_AF_PP; GPIO__Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO__Pin = GPIO_Pin_11; GPIO__Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB,&GPIO_InitStructure); USART__BaudRate = 115200; USART__StopBits = USART_StopBits_1; USART__Parity = USART_Parity_No ; USART__Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init( USART3,&USART_InitStructure); USART_ITConfig( USART3,USART_IT_RXNE,ENABLE); USART_ITConfig( USART3,USART_IT_IDLE,ENABLE); USART_Cmd(USART3,ENABLE); } void OutPut_Data(void) { int temp[4] = {0}; unsigned int temp1[4] = {0}; u8 databuf[10] = {0} u16 CRC16 = 0; for(i=0;i<4;i++) {temp[i] = (int)OutData[i]; temp1[i] = (unsigned int)temp[i];} for(i=0;i<4;i++) {databuf[i∗2] = (u8)(temp1[i]%256); databuf[i∗2+1] =(u8)(temp1[i]/256); } CRC=CRC(databuf,8); databuf[8] = CRC16%256; databuf[9] = CRC16/256; for(i=0;i<10;i++){ printf("%c",databuf[i]); }}35 36
2024年2月29日发(作者:迮昕昕)
基于STM32的电子计步器的设计与实现
随着生活节奏的不断加快,留给人们的锻炼时间越来越少,走路和跑步成为人们日常生活中为数不多的运动之一。计步器携带方便,能很好地完成量化运动量的目标。因此,最近几年各种计步器以及计步软件大量出现。鉴于人们对于步数检测准确度的要求以及使用便利的需求,十分有必要设计一套计步算法并应用于相关的计步器。
本设计的研究目的是设计出一款高精度、便携的计步器。本设计的主要难点在于数据滤波算法以及计步检测算法的研究。首先,本设计分析了几种数据滤波的方法,选择了比较适合的卡尔曼滤波算法。接着,分析了现有的几种计步检测算法,包括动态阈值算法和峰值检测算法。发现这些算法都不是很准确,所以本文设计了一种新的计步检测算法,提高了计步检测的精度,为其他研究者在步数检测方面提供了一种较好的解决方案。最后,本设计还采用了TFT彩屏的人机交互界面,可以实时显示卡路里、时间以及步数。
通过实际调试过程中的不断改进,实现了计步器的准确检测。
关键词: 计步器 MEMS传感器 滤波 步数检测
I
目录
1 绪论 ......................................................................................................................................... 1
1.1 研究背景和意义 .................................................................................................................. 1
1.2 国内外研究现状 .................................................................................................................. 1
1.3 章节安排 .............................................................................................................................. 2
2 系统总体设计方案 ......................................................................................................... 3
2.1 设计目标 .............................................................................................................................. 3
2.2 系统架构分析 ...................................................................................................................... 3
2.3 系统方案分析 ...................................................................................................................... 3
2.3.1 佩戴位置选择 ............................................................................................................... 3
2.3.2 MEMS惯性传感器的数据读取 ................................................................................... 4
2.3.3 数据融合与滤波 ........................................................................................................... 5
2.3.4 计步算法 ....................................................................................................................... 8
3 系统硬件设计 ................................................................................................................... 9
3.1 系统硬件电路总体设计 ...................................................................................................... 9
3.2 单片机最小系统设计 .......................................................................................................... 9
3.3 MEMS传感器 .................................................................................................................... 10
3.4其他外围电路 ..................................................................................................................... 11
3.4.1 电源转换 ..................................................................................................................... 11
3.4.2 TFT彩屏电路 .............................................................................................................. 11
3.4.3 无线串口通信 ............................................................................................................. 12
4 系统软件设计 ................................................................................................................. 13
4.1 系统软件总体设计 ............................................................................................................ 13
4.2中断设计 ............................................................................................................................. 14
4.2.1 定时器中断 ................................................................................................................. 14
4.2.2 串口中断 ..................................................................................................................... 15
4.2.3 中断优先级判断 ......................................................................................................... 16
II
4.3 MPU6050原始数据采集 ................................................................................................... 16
4.3.1 陀螺仪和加速度计的配置工作 ................................................................................. 16
4.3.2 串行口的配置工作 ..................................................................................................... 17
4.3.3 IIC读取姿态传感器数据 ............................................................................................ 17
4.4 数据处理 ............................................................................................................................ 18
4.4.1 数据类型统一 ............................................................................................................. 18
4.4.2 卡尔曼滤波 ................................................................................................................. 19
4.5 计步算法 ............................................................................................................................ 21
4.6 无线串口通信 .................................................................................................................... 22
5 系统调试 ............................................................................................................................ 23
5.1 系统调试上位机 ................................................................................................................ 23
5.2 标定MPU6050零点 ......................................................................................................... 23
5.3 卡尔曼滤波参数调试 ........................................................................................................ 23
5.4 计步测试 ............................................................................................................................ 24
6 总结与展望 ....................................................................................................................... 25
6.1 总结 .................................................................................................................................... 25
6.2 展望 .................................................................................................................................... 25
6.3 课题研究对环境以及社会的影响 .................................................................................... 26
附录 ............................................................................................................................................ 27
附录一 系统硬件原理图和PCB ............................................................................................ 27
附录二 系统实物图 ................................................................................................................. 28
附录三 系统核心 ..................................................................................................................... 28
III
1 绪论
1.1 研究背景和意义
随着社会不断进步以及生活水平不断提高,人们逐渐开始重视自身的健康。据统计,2015年中国的60岁及以上人口已经占总人口的16.25%,达到2.32亿。预计2020年老年人口将达到2.38亿,老龄化程度将达到17.27%,其中80岁以上老年人口将超过三千万人;除了人口老龄化之外,肥胖问题也日益成为人们所关心的热点问题。根据世界卫计委和国家统计局的调查数据,中国人的肥胖人口正在逐年上升。从1990年到2016年,肥胖率从14%上升到31%,超重率从4%上升到15%。
这些老龄人口与肥胖人口十分需要一款便携式的运动检测装备,通过监控日常的运动量来确保合理的运动量。实验表明,步行和跑步作为一种方便又十分有效的运动,既不需要占用场地,也不容易受到伤害,受到许多人的欢迎。这使得应用于计步的智能穿戴设备迅速发展。
意大利的达芬奇制造了世界上最早的计步器,但现存的最早的计步器是1775年日本制造的。这种机械式计步器主要是利用摆钟原理进行工作,摆动计步器时,内部的金属球会来回晃动,机械开关开始检测步伐,计步器内部的计数器数值增加。但这种机械式的计步器由于精确度比较差,早已不被人所使用,取代它的是电子式计步器。
1.2 国内外研究现状
国内市场上流行的大多是3D电子式计步器,通过加速度计来测量加速度变化,根据软件算法判断出步数。比起机械式计步器,电子式计步器的精确度有很大提升。现在市场上比较知名的国产品牌主要有乐心、小米、时刻美、爱希亚等。国内的小米手环1代采用40mAh的锂电池进行供电,实现了降低功耗的目的,续航时间大大延长;国产的clingband手环采用光学心率传感器,能够对人体进行心率耗时分析。另外,clingband手环还可以通过高精度的温度传感器来实现对体表温度变化的监测;
国外计步器发展比较早,计步器除了计步的基础功能之外,还具备许多其他的功能。东京大学设计出一款鞋内嵌入式计步器,通过压电材料将运动所产生的机械能转化为电能,直接给计步器供电;韩国的SAMSUNG开发出一款拥有操作系统和预装软件的电子计步器,可以播放音乐、视频聊天和接听电话。
1
从国内外现状可以看出,计步器设计不仅需要考虑续航能力、舒适度、外观等方面,还需要考虑如何获取更多的健康数据以及如何保证健康数据的准确性。
1.3 章节安排
第一章:绪论,主要介绍计步器发展的研究背景与意义,并详细叙述下国内外技术上的创新。
第二章:系统总体方案设计,简单介绍下计步器的设计目标,根据设计目标对系统的整体框架进行设计,对具体的系统方案进行选择。
第三章:系统硬件电路设计,主要介绍了基于STM32的单片机最小系统、电源转换、MEMS惯性传感器、TFT彩屏显示、无线通信等硬件电路的设计。
第四章:系统软件设计,首先介绍整个计步器的软件设计思路,接着介绍MEMS惯性传感器的数据获取、卡尔曼滤波、步数检测等具体部分的软件设计思路。
第五章:系统调试,首先调试惯性传感器的参数,最后测试计步器的准确度性能。
第六章:总结与展望,总结了计步器设计所完成的设计目标,列举了计步器设计过程中所遇到的特殊问题。在计步器实现的功能上进行了展望,举出未来可以添加的功能。
2
2 系统总体设计方案
2.1 设计目标
本设计是一款电子计步器,用来量化用户的运动量。考虑到携带的方便,产品设计一定要紧凑以节省空间,看起来美观大方。同时,要很好地考虑到用户的体验。用户可以随时看到自己行走的步数,以及其他的一些健康数据,比如卡路里、运动时间。另外,计步器的最主要功能是计步,所以设计时一定要保证电子计步器的计步准确性。
2.2 系统架构分析
电子计步器需要对大量的数据进行处理,所以需要采用一款运算性能较强的单片机作为主处理器。电子计步器主要是对人体位置的移动进行评估,因此需要选择一款合适的姿态传感器。主处理器通过姿态传感器得到加速度信号和角速度信号,对这两种信号进行处理,得到相对准确的角度变化。主处理器再对角度信号进行步数检测,得到准确的步伐。此外,可以添加显示屏、按键以及无线通信,方便数据的显示以及调试。系统的整体框架如图2.1所示。
图2.1 系统的整体框架图
2.3 系统方案分析
2.3.1 佩戴位置选择
3
要实现步数检测首先需要对人行走和跑步时的姿态有一定的了解。人体在行走时,身体各部分都在运动,都会产生相应的周期性正弦加速度信号。一般来讲,用脚部的加速度信号进行对步数的检测,是比较准确的。但考虑到美观性以及携带方便性,一般不将计步器佩戴在脚部。目前大部分计步器是利用手腕或腰部的运动来检测步数。本设计是一款比较常见的手腕式计步器。
图2.2 手腕式计步器
图2.3 腰部式计步器
2.3.2 MEMS惯性传感器的数据读取
在分析行走的特征参数时,通常选择加速度和角速度作为相关变量。MEMS惯性传感器中包括加速度三个轴和陀螺仪三个轴,分别与人体运动的三个方向相对应。在表征加速度和角速度时,通常要选择变化最明显的轴。在实际场合时,因为传感器的佩戴位置不同,传感器的的测量值并不直接对应人体运动的三个方向,而对应传感器三个轴上的投影值。因此不应该简单地认为行走时某轴的变化一定最大,而应该通过观察MEMS惯性传感器的原始数据来提取合适的加速度和角速度信号。
因为本设计的佩戴位置已经确定,所以在行走过程中,会有固定的加速度轴和陀螺仪轴数据波动比较明显。为了接下来数据处理的方便,本设计将加速度计和陀螺仪三个轴的数据通过无线通信发送到电脑上,确定好加速度计和陀螺仪变化比较明显的那个轴,只用那两轴数据来表示加速度和角速度。
图2.4 人体运动模型
4
图2.5 MPU6050的各个轴
2.3.3 数据融合与滤波
得到的MEME传感器原始数据包括角速度值和加速度计值。加速度计长时间测量的精度比较高,短时间测量会因为噪声而产生一定的偏差。陀螺仪则相反,短时间测量比较精确,长时间测量由于温漂会产生误差。因此,本设计将两者进行数据融合,以确保测量数据的准确性。
数据融合是对不同来源的数据进行综合分析后,最终得到状态估计。换而言之,就是将不同数据融合成一个新数据,便于接下来的数据处理。在实际的工程中,单个传感器的测量值未必能够真实反映物体的状态。所以实际运用中大多使用多个传感器,通过不同属性的传感器信息来得到准确的状态。这些年来,得益于微机电系统的迅猛发展,工业中开始广泛应用各种形式的传感器技术。对采集到的声音、图像、文字等进行数据融合,可以得到物体的真实状态。
有许多数据融合算法可以进行多传感器的数据融合。
(1)加权平均法处理的数据必须是同一类型的数据,根据数据来源的不同,对多个数据赋予不同权重,使得融合出来的新数据能够反映物体的真实状态。
(2)分组估计法是对采样数据进行分组处理,先计算出每一组数据的均方差,再赋予相应的权重,最后得到相应的数据融合值。
(3)卡尔曼滤波法是一种基于时域的滤波方法,对于具有高斯噪声的线性系统,可以得到关于系统状态的最优估计。卡尔曼滤波法是一种进行最优估计的数据处理算法,并且它采用递归计算,在控制、通信以及导航方面应用很广,可以用于传感器数量较少的数据融合。卡尔曼滤波法用在数据融合时,使用一个传感器值作为此刻的测量值,由多个传感器值得到下一个时刻的预估值,最后再由此刻的测量值和上一时刻的预估值计算出此刻的最优估计值。
(4)贝叶斯估计法会把数据看作具备某种分布特征的随机变量,允许我们通过观测值以及之前总结出来的规律对数据进行估计。用在数据融合时,通常适用于传感器数量较多、数据量庞大的场合。
本设计要对加速度信号和角速度信号进行数据融合,得到拟合角度值,方便接下来的计步算法。卡尔曼滤波法不仅能够达到实时处理的目标,还可以滤除噪声,十分适合本设计。
5
为了简化接下来的运算,本设计将采集得到的加速度信号和角速度信号统一成角度信号,再进行卡尔曼滤波。
① 加速度转化成角度
将芯片水平水平放置在平台上, Y轴与X轴方向的重力分量都是0,而Z轴方向的重力分量是g。垂直向下时,加速度轴各个方向的重力分量如图2.6所示。
图2.6 垂直向下示意图
将芯片稍微倾斜一定角度,地面坐标系如图2.7所示。Ax是X轴方向的加速度,Ay是Y轴方向的加速度,Az是Z轴方向的加速度。α1是X轴与水平线的夹角,α是X轴与竖直方向的夹角。β1是Y轴与水平方向的夹角,β是Y轴与竖直方向的夹角。γ1是Z轴与水平方向的夹角,γ是Z轴与竖直方向的夹角。
图2.7 旋转α°地面坐标系示意图
从图2.7中可以看出各夹角之间的关系
α = 90°− α1, β = 90°− β1 , γ = 90°− γ1 (式2.1)
各轴上的重力加速度分量
6
Ax = gcosα, Ay = gcosβ , Az = gcosγ (式2.2)
结合式2.1和式2.2可得到
Ax ==gsinα1 , Ay =gsinβ1 , Az = gsin γ1 (式2.3)
由g2= (gsinα1 )2+ (gcosα1)2,则
gcosα1 =g2-Ax2, gcosβ1 =g2-Ay2,gcosγ1 =g2-Az2 (式2.4)
倾斜一定角度,载体坐标系如图2.8所示。结合立体几何的知识,立方体的对角线就是重力加速度g,立方体的长宽高就是Ax、Ay、Az,图中的虚线大小等于 Ay2+Az2。根据勾股定理Ax2 + Ay2 + Az2 = g2,并结合式2.4,可得:
tanα1 =
AxAyAz22 ,tanβ1 =AyAxAz22, tanγ1 =AzAxAy22 (式2.5)
图2.8 旋转α°载体坐标系示意图
那么弧度值可以表示为
α1 = arctan(
AxAyAz22),β1= arctan(AyAxAz22),γ1= arctan(AzAxAy22) (式2.6)
最后得到的各轴的角度值分别为
θx=arctan(
180)× (式2.7)
22πAyAzAxθy =arctan(180)× (式2.8)
22πAxAzAy7
θz = arctan(
180)× (式2.9)
22πAxAyAz②角速度转换成角度:只要对角速度进行积分就可以得到角度。
2.3.4 计步算法
本设计需要对卡尔曼滤波处理后的角度变化数据进行计步分析。在步行和跑步两个运动状态中,臂部、手腕、脚和腿部的动作都可以视作钟摆运动,这些身体部位的角度信号呈现出一种正弦变化。根据人体运动的特征,可将计步算法大致分为两类。
一是峰值判断法,每走一步角度信号会有一个相对明显的峰谷值,所以可以通过对角度的峰值检测,得到有效的行走步数。正弦信号的峰值处一般就是拐点,此处的斜率为零。但是由于采样频率有限,采样数据呈现离散的状况,所以只能根据相邻两个采样点的斜率是否趋近于零,来判断峰值的出现。只有当峰值两侧的正斜率点和负斜率点数目相等时,才认定为走了有效的一步。
二是动态阈值判断法,当角度数值从大到小或从小到大穿越某个设定值时,可认定为走了一步,这个设定值称作阈值。因为周围环境会对人的步伐大小产生影响,阈值最好是根据实时采样数据计算得到的。一般取最小值和最大值的平均数、取一段时间内采样数据的中位数作为动态阈值。只有当相近的采样值连线呈现下降态势且穿过动态阈值,才可以记为有效的一步。
对于峰值判断法来讲,一些毛刺噪声会被误判为峰值,从而影响步伐检测的精度。对于动态阈值判断法来讲,当人体步伐大小发生改变时,会漏掉一些步伐。所以本设计将峰值检测和动态阈值结合起来,并且将时间窗加入其中,可以滤除一些无效振动并且可以更准确地判断出峰值。查阅资料可知,快跑的最快频率约为每秒5步,慢走的最慢频率约为每秒0.5步,所以将峰值附近0.2S~2S之间的数据作为有效数据,超过这个范围的数据均看作无效数据。之后将峰值相邻的有效数据再进行一次比较,确保找到真实的峰值以及比较合适的阈值,保证步伐检测的精准度。
8
3 系统硬件设计
3.1 系统硬件电路总体设计
本文第2章对整个系统进行了方案分析,本章也将对设计中的硬件部分进行分析。以基于STM32F103系列单片机的最小系统作为主控核心,其他的外围电路包括电源转换、TFT彩屏显示、MPU6050姿态传感器等。硬件方面的整体框架如图3.1所示。
图3.1 系统整体硬件框架图
3.2 单片机最小系统设计
本设计考虑到计步算法的运算过程相当复杂,选用了目前市场上性价比相对较高、运算性能较好的STM32F103系列单片机。
STM32F103系列使用高性能的ARM_M3内核,工作频率高达70MHz,内置高速缓存以及静态RAM。其I/O口资源比较丰富,可以同时与多个外设相连。该型号单片机同时具备ADC采集、定时器以及输入捕获功能,支持IIC协议、SPI协议和CAN协议,拥有USART接口和USB接口。
单片机最小系统包含了主控芯片、时钟电路、BOOT自举电路、复位电路等,它关系着整个硬件系统能否良好运转,所以单片机的设计必须要十分细致,尽可能考虑到各种突发状况对单片机运行造成的影响。
9
图3.2 STM32F103C8T6最小系统板
整个系统需要的所有时钟都是时钟电路提供的。而复位电路的作用在于,当整个系统由于某个原因出现程序跑飞现象时,可以通过复位按钮使单片机重启。STM32一般带有BOOT自举电路,用来选择单片机的工作模式,本设计将BOOT1和BOOT2两个引脚同时接地,使单片机处于正常工作的状态。
3.3 MEMS传感器
MEMS惯性传感器通过测量惯性敏感元件在载体坐标系中的线运动和角运动,计算得到载体的角速度和加速度。它一般集成了磁力计、陀螺仪以及加速度计,可以用在自主定位以及自主导航中。MPU6050姿态传感器就是一款低成本MEMS惯性传感模块。为了实现对步数的实时检测,本设计使用了MPU6050姿态传感器中的加速度计和陀螺仪。下面简单的对该姿态传感器进行讲解。
MPU6050姿态传感器工作电压为2.4V-5V,具备功耗低以及体积小的优势。该装置拥有II2C和SPI的通信接口,可以输出欧拉角、加速度计、温度、陀螺仪等信息。MPU6050加速度参数的测量范围分别为±2g、±4g、±8g、±16g,角速度的测量范围分别为±250°/s、±500°/s、±1000°/s 和±2000°/s。
本设计通过II2C进行读取MPU6050的数据,SCL接单片机的PB6,SDA接单片机的PB7。本设计中MPU6050姿态传感器的原理图如图3.6所示。
10
图3.6 MPU6050姿态传感器
3.4其他外围电路
3.4.1 电源转换
电源转换出来的电压是否达到要求,这关系到系统能不能正常运行,因此需要谨慎进行电源部分的电压转换。本设计所采用的芯片都是使用3.3V电源供电,因此只需要一个3.3V的稳压电路。在保证压降以及纹波较小的情况下,本设计采用AMS1117-3.3输出3.3V的电源。AMS1117-3.3是一种正向低压降稳压器,适用于稳压器电池、线性开关电源以及充电器等。它的成本较低,转换效率高,外部电路设计起来十分简单。AMS1117-3.3稳压电路原理图如图3.7所示。
图3.7 AMS1117-3.3稳压电路
3.4.2 TFT彩屏电路
本设计需要将计算所得的步数实时显示出来,本设计选用了性价比高的TFT彩屏进行显示,下面对该TFT彩屏进行简单描述。
该款显示屏的分辨率为125*125,尺寸为1.45寸,采用SPI协议进行通信,工作电压范围在2.4V-3.3V之间。要实现彩屏显示字符、汉字以及字符串的目的,首先要编写彩屏11
的驱动程序,然后再根据自己所要实现的功能编写应用程序。驱动程序相当于一个字符库,应用程序调用其中的字符,字符就会在彩屏上显示出来。
本设计中用3.3V对TFT彩屏进行供电,3.3V电源通过上拉电阻接TFT_LED,TFT_SCK接单片机的PA5,TFT_SDA接单片机的PA7,TFT_AO接单片机的PA4,TFT_RST接单片机的PA3,TFT_CS接单片机的PA2。TFT彩屏的接口图如图3.8所示。
图3.8 TFT彩屏接口图
3.4.3 无线串口通信
在进行调试时需要将姿态传感器MPU6050得到的原始数据发送到电脑上,这时可以选择无线串口模式、蓝牙模式或WIFI模式。本设计采用的是无线串口模式,选择透传模式就不需要进行配置,十分方便简单。无线串口进行主从机配对之后,就可以将数据通过无线发送到电脑上。
本设计采用HJ-WUS1无线模块,输入电压范围为2.40V-3.50V,最大工作电流不能超过50mA。该模块传输距离达到34米以上,并且它支持无线透传模式。在本设计的原理图中,TX1接单片机PB10/USART3_TX,RX1接单片机PB11/USART3_RX。
12
4 系统软件设计
4.1 系统软件总体设计
第二章和第三章重点讲述了计步器的总体系统方案以及硬件电路设计,本章主要进行述计步器的软件部分设计。软件也是系统整体设计的一个重点,软件部分关系到设计的功能能否正常实现。本设计的软件部分主要包括系统初始化、MPU6050原始数据采集、卡尔曼滤波、无线串口通信和TFT彩屏界面设计等。本设计整体软件框架如图4.1所示。
图4.1 软件框架图
整个系统软件执行后,首先进行的是系统初始化,其中包括了定时器的初始化、TFT彩屏初始化、IO口的初始化、MPU6050初始化、串口初始化等。然后再进行MPU6050原始数据(加速度信号和角速度信号)的读取,如果没有读取到数据则等待。当读取到加速度信号和角速度信号之后,将它们转化成角度信号,进行卡尔曼滤波。接着对经过滤波、13
融合的角度数据进行步数检测。检测得到的步数可以通过无线通信发送给电脑进行调试,也可以发送给TFT彩屏显示。
4.2中断设计
4.2.1 定时器中断
本设计在采集MPU6050原始数据时,需要用到定时器来确定采样频率。定时时间到,通过两个IIC口采集50个数据。定时器中断初始化流程图如图4.2所示。
图4.2 定时器初始化流程图
使能定时器TIM时钟。本设计使用的是定时器3,由图4. 3(STM32的时钟树)可以看出,定时器3的时钟是挂在总线APB1上面,所以打开定时器3需要使能总线APB1。这时定时器的时钟来源于总线APB1的一个倍频器,大小为2×36Mhz=72Mhz。
TIM3的计数模式分为向上向下双向计数、向下计数以及向上计数。本设计采用向上计数模式,计数器从0计数到TIM3_ARR中的自动重装值,再进入中断服务程序,最后还会返回重新从0开始计数。
14
图4.3 定时器的时钟树
定时器的定时时间计算公式:TIME=((TIM3_PSC +1)×(TIM3_ARR +1))/72Mhz;本设计的定时器寄存器配置为TIM3_ARR=71、TIM3_PSC=4999,所以定时时间为
((71+1)×(4999+1))/72Mhz=5ms (式4.1)
4.2.2 串口中断
本设计在进行调试时,需要将数据通过无线串口发送到电脑上。本设计使用的STM32F103系列单片机拥有4路串口,串口支持同步通信以及异步通信。串口中断初始化流程图如图4.4所示。
图4.4 串口中断初始化流程图
15
STM32F103系列单片机中串口是挂载在 APB2 下面的外设,所以串口使能也需要使能总线APB2。串口参数主要包括:字长,停止位,波特率,奇偶校验位等。本设计主要使用串口3,将波特率配置为115200,字长设置成8位数据,停止位为1位,没有奇偶校验。
4.2.3 中断优先级判断
本设计中同时使用了串口3中断和定时器3中断,所以需要合理配置好中断优先级。每个中断都对应着一个外设,每个外设都包含许多个中断源或中断事件,这些中断可以通过特定的中断通道向主处理器请求中断。软件设计中一般会通过配置中断控制器完成中断优先级的选择。ARM_M3内核芯片可以配置256个中断优先级,STM32的NVIC是ARM_M3内核芯片的NVIC的子集。表4.1就是STM32的NVIC中断优先级配置表。
表4.1 NVIC中断优先级配置表
NVIC优先级组
NVIC_优先组0
NVIC_优先组1
NVIC_优先组2
NVIC_优先组3
NVIC_优先组4
NVIC_IRQ抢占优先级
0
0-1
0-3
0-7
0-15
NVIC_IRQ次优先级
0-15
0-7
0-3
0-1
0
描述
1位抢占优先级3位次优先级
0位抢占优先级4位次优先级
2位抢占优先级2位次优先级
3位抢占优先级1位次优先级
0位抢占优先级4位次优先级
经过实际调试,本系统的中断优先组别选择NVIC_PriorityGroup_2,中断优先级为:USART3 优先级大于TIM3。
4.3 MPU6050原始数据采集
本设计只用到9轴MPU6050姿态传感器中的加速度计和陀螺仪,主要用来测量加速度和角速度。具体数据采集程序的整体设计思路是,首先做好陀螺仪和加速度计的配置工作,并且确定好串行控制口。接着,因为该惯性传感器内包含AD转换器,可以将采集到的模拟量转换成数字量,所以只要再将数字量通过I2C接口传输到主处理器中,就可以完成对MPU6050姿态传感器原始数据的采集。MPU6050原始数据采集的流程图如图4.5所示。
4.3.1 陀螺仪和加速度计的配置工作
16
因为人体行走频率不是很高,所以加速度计和陀螺仪的采样频率也不需要配置得过高,本设计采用500Hz的频率,配置角速度量程为±2000°/S, 配置加速度量程为±8g。
4.3.2 串行口的配置工作
配置完加速度计和陀螺仪之后,需要通过I2C接口对6个轴的原始数据进行实时读取。由于STM32的硬件IIC设计得十分复杂,所以本设计采用软件模拟IIC的办法。定义时钟总线接口为PB6,定义数据总线接口为PB7。
#define IIC0_SCL PBout(6) //SCL
#define IIC0_SDA PBout(7) //SDA
图4.5 MPU6050原始数据采集软件设计
4.3.3 IIC读取姿态传感器数据
MPU6050姿态传感器通过IIC发送的一个数据帧中依次为加速度3轴数据、陀螺仪3轴数据、欧拉角数据以及温度数据,由于本设计中只需要陀螺仪数据和加速度数据,所以只读取前面的数据即可。但是因为加速度计信息和陀螺仪数据都是16位的,而这些数据分别存放在两个8位寄存器中,所以要将两个8位数据转换成一个16位数据。本设计先将高8位数据左移8位,再加上低8位寄存器中的数据,形成需要的16位加速度计信息和陀螺仪数据。void Data_get_6050(int* AX, int* AY, int*
AZ,int* GX, int*GY, int* GZ)
{iicDevRead(MPU6050_ADDR,MPU6050_
Accelerometer, 12, Registers);
17
*AX=(((s16) Registers[0]) << 8)| Registers [1];
*AY= (((s16) Registers[2]) << 8)|Registers [3];
*AZ=(((s16)Registers[4])<< 8) | Registers[5];
*GX=(((s16) Registers[6])<<8) | Registers[7];
*GY=(((s16)Registers [8])<<8) | Registers[9];
*GZ=(((s16)Registers[10])<<8)|Registers[11;}4.4 数据处理
由2.3.3、4.2.3可知,得到的加速度信号和角速度信号都是16位的,本设计要采用卡尔曼滤波进行数据融合处理。为了方便接下来的计算,先将加速度信号和角速度信号都统一为角度信号。再采用卡尔曼滤波,从而滤除噪声,得到较为理想的角度变化波形。数据处理的具体流程如图4.6所示。
图4.6 数据处理流程图
4.4.1 数据类型统一
因为采集值都是16位2进制的加速度计值和角速度值,十分不利于运算,所以要先将两者统一为为角度信号。由2.3.3可知,加速度信号和角速度信号是如何转化成角度信号。数据归一化的具体代码实现如下,
Accelerometer _X=Get Accelerometer _X( ); //IIC读取X轴加速度计
Accelerometer _Ax = (Accelerometer _Z - 1100) /16384; //去除加速度计的零点偏移 Accelerometer _Ax = Angle_Ax*1.2*180/3.14; //将加速度的弧度转换为度
Gyroscope _y = GetAngle_Z(); //IIC读取Z轴陀螺仪
Gyroscope _y = (Gyroscope _y + 30)/16.4; //去除陀螺仪的零点偏移,计算角速度值
18
Gyroscope _y= Gyroscope _y+ Gyroscope _y*0.01; //角速度积分得到角度
4.4.2 卡尔曼滤波
卡尔曼滤波以最小均方根作为估计的最佳准则,采用噪声、输入信号的状态空间模型,利用前一刻的状态估计值和现在的测量值来更新对状态变量的估计,求得此刻的状态估计值。因为卡尔曼滤波采用不断递归的思想,只会保留上一刻的协方差,因此十分适用于数据实时处理。本设计采用简单的卡尔曼滤波算法,首先通过陀螺仪计算得到此刻的角度测量值。接着由加速度计算得到的角度与陀螺仪计算得到的角度,得到下一时刻的角度预估值。最后通过上一时刻的角度预估值和此刻的角度测量值,得到此刻的最优角度估计。卡尔曼滤波的具体流程图如图4.7所示。
图4.7 卡尔曼滤波流程图
保持计步器水平放置,快速改变计步器角度,之后再快速恢复到水平位置,观察滤波前后角度变化的波形。图4.8即为滤波前的原始数据,可以看到数据数值较大,不太利于计算,而且曲线上升缓慢,快速跟随性比较差,输出中也存在较大的噪声。图4.9为经过卡尔曼滤波后的融合角度值。从图中可以看出,经过卡尔曼滤波后,角度曲线的跟随性和快速性都得到显著的提升,同时噪声干扰也少了好多,曲线变得更加平滑。
19
图4.8 滤波前原始数据
图4.9 滤波后的数据
图4.8、图4.9中,X轴为时间,Y轴为角度值。从图中可以看出,卡尔曼滤波后的图像出现了过冲现象,由于过冲不是很明显且不影响接下来的计步,所以可以不改动相关参数。卡尔曼滤波具体代码实现如下
DoubleFilter_Kalman(doubleAccelerometer_num,double Gyroscope_num)
angle = angle + Gyroscope_num *0.001f;
//最优角度估计值
{ static double angle=0; //最优角度的初值 p=p+q;
static double p=0.000001;
//预测噪声方差矩阵
static double q=0.000001;
//协方差最优估计矩阵
static double k=0; //卡尔曼增益
k=p/(p+ Gyroscope_num);
// 计算卡尔曼增益
angle = angle +k*(Accelerometer_num- angle);
//卡尔曼增益修正角度
p=(1-k)*p; //更新协方差最优估计矩阵
20
return angle; }
4.5 计步算法
本设计中,核心部分就是计步算法。根据之前得到的角度变化信息,通过合适的计步算法以确定运动步数。由2.3.4可知,本设计打算将峰值检测和动态阈值结合起来,形成一种融合的计步算法。
因为人快跑的速度最多达到每秒5步,这种频率不是很快,所以本设计先采用了一个低通滤波算法。计步算法中设定两个移位寄存器Data_new和Data_old,当新得到一个角度信号时,用Data_new寄存器中的数值与新的角度信号进行比较,若两者相减的绝对值比预定精度小,则Data_old寄存器的新值就是Data_new寄存器值,而Data_new寄存器的新值是新的角度信号;当两者相减的绝对值比预定精度大时,新得到的角度信号被抛弃,Data_new寄存器数值保持不变,Data_old寄存器的新值依旧是Data_new寄存器的数值。
图4.10 计步算法示意图
峰值出现的条件是,有曲线穿越动态阈值的下方,并且角度曲线的斜率为负值(Data_new 21 4.6 无线串口通信 调试过程中,本设计将数据通过无线串口传输到电脑上。为了观察方便,将2进制的原始数据转换成10进制数据。之后上位机软件会将采样得到的离散信号拟合成连续信号,形成波形,在电脑上显示出来。 为了明确数据通信是否正常,本设计在数据通信中采取了最常用的CRC16校验方式。CRC即循环冗余校验码,具备数据传输校验的功能。校验的主要过程,首先对数据进行多项式计算,结果被称为冗余码,将结果放在发送数据的末尾。随后接收装置也使用CRC对接收的数据进行检验,这样就清楚数据通信是否正常无误。 在数据检错中最关键的就是冗余码的产生,而冗余码产生的步骤如下:首先,用2n乘以M,相当于把n个0添加到M的后面。接着,用得到的结果除以事先双方约定好的 (n+1)位除数P,余数R就是冗余码。如果传输过程中没有出现任何差错,那么通过CRC检验得到的余数R必为0,否则就会产生误码。 无线串口通信的代码具体如下 void Data_transmission (void) { int Temporary0 [4] = {0}; unsigned int Temporary 1 [4] = {0}; u8 Data_ cache [10] = {0}; u8 i; u16 CRC_16 = 0; for(i=0;i<4;i++) { Temporary 0[i] = (int) Data_ cache [i]; Temporary1[i]=(unsigned int) Temporary 0[i];} for(i=0;i<4;i++) {Data_cache[i*2]=(u8)(Temporary1[i]%256); Data_cache[i*2+1]=(u8)(Temporary1[i]/256);} CRC_16 = CRC_CHECK(Data_ cache,8); Data_ cache [8] = CRC_16%256; Data_ cache [9] = CRC_16/256; for(i=0;i<10;i++) {printf("%c", Data_ cache [i]); }}22 5 系统调试 经过一段时间的软硬件设计之后,完成了简易电子计步器的初步搭建。本设计是一款电子计步器,对于自身的计步精确度提出了很高的要求。为了能够做到计步足够准确,如何进行系统调试就显得尤为重要。 5.1 系统调试上位机 在本设计的整个调试过程中需要对许多参数进行观测,所以为了调试的方便,本设计采用了上位机调试软件。这款上位机可以自动搜索串口,无需查看端口号。并且这款上位机总共可以同时查看4个通道的数据,十分方便。可以暂停和保存波形,给数据处理带来便捷。 5.2 标定MPU6050零点 MPU6050的陀螺仪和加速度计在测量过程中会出现数据漂移的问题,为了保证步数计算的精确,需要标定MPU6050的零点。使计步器保持在手臂垂直向下的角度,采集此刻陀螺仪和加速度计的值,这就是MPU6050零点值。但是在实际运用过程中由于数据漂移的影响,测量零点值一定会产生误差。本设计连续采集十次零点值,取它们的平均值作为零点值,以减小数据漂移给计步精度所带来的影响。陀螺仪Z轴和加速度计X轴的十次采集值如表5.1所示。 表5.1 陀螺仪和加速度计的采集值 次数 1 2 3 4 5 陀螺仪Z轴 -42 -39 --41 -38 -40 加速度计X轴 3738 3737 3745 3742 3741 次数 6 7 8 9 10 陀螺仪Z轴 -42 --41 -40 -41 -37 加速度计X轴 3742 3740 3741 3738 3739 5.3 卡尔曼滤波参数调试 由于加速度计和陀螺仪在采集数据方面都具有一定的局限性,所以本设计采用了卡尔曼滤波将两者结合起来,以保证计步的精确性。卡尔曼滤波参数调试主要是调节角速度积分时间dt和卡尔曼增益比例Gyro_K,角速度积分时间dt主要是用于估算陀螺仪所得到的23 角度,Gyro_K主要是用来计算出卡尔曼增益k。k越大,卡尔曼滤波的拟合角度就会越靠近测量值;k越小,卡尔曼滤波的拟合角度就会越靠近预估值。 卡尔曼滤波调节参数时,首先要调节角速度积分时间dt。因为角度值=角速度×时间,所以当增大积分时间时,角度输出变化就会越快,滤波后曲线的跟随性更强。dt初始给定值为0.0001,此时调节MPU6050姿态传感器的倾斜角度,结果输出角度值跟随较慢。接着每次以0.0002速率缓慢增大dt值,发现输出角度值的变化速度慢慢增大,此时继续加大dt值直到输出角度值发生过冲、震荡现象。 接着保持dt值不变,改变卡尔曼增益比例Gyro_K的值。因为Gyro_K在拟合角度过程中起到了微分控制的功能,所以可以通过改变Gyro_K使输出角度的过冲、震荡现象减弱。通过不断的调试角速度积分时间dt和卡尔曼增益比例Gyro_K,最终确定了dt=0.0011,Gyro_K=0.007。这时卡尔曼滤波拟合角度曲线跟随性好且没有出现过冲。 5.4 计步测试 计步器软硬件设计完成后,要通过实验来验证计步器的计步效果,检查其是否达到了预期的设计目标。对于运动来说,步数是一个可以量化的指标,因此步数检测的精确度也成为衡量计步器的一个标准。计步器的精确度越高,越能反映出人体的运动量大小。计步精度的定义如下:精确度=|1-计步数-实际步数|×100%。 实际步数表5.1 计步器精确度测试 实验者 1 2 3 4 5 实际步数 100 100 100 100 100 计步数 98 97 103 104 102 误差 -2 -3 +3 +4 +2 精确度 98% 97% 103% 104% 102% 表5.1是5个实验者的行走测试结果,结果发现误差基本上都在±5步内,基本上达到了设计目标,相对误差也不大。由于起步不稳定以及周围环境的影响,几步的误差完全可以接受。 24 6 总结与展望 6.1 总结 本设计以姿态传感器MPU6050为基础设计了一款电子计步器,利用姿态传感器MPU6050检测采集数据,通过陀螺仪和加速度计归一化得到角度变化。再利用变化的角度值进行步数检测,最终实现电子计步器的计步过程。 本设计经过不断的调试与改进,获得的成果如下: 1、完成了计步器的硬件架构设计,并进行硬件选型。对采集的加速度信号和角速度信号进行归一化处理,再使用卡尔曼滤波得到相应的角度信号。实验发现,经过卡尔曼滤波的角度信号变得更加平滑,噪声少,便于计步算法的实现。 2、根据运动时手腕处周期性角度变化,以及观察到的波形特征,设计了一套应用于手腕上的步数检测方法,并把该计步方法应用到电子计步器上。 3、设计了一套采集加速度信号的系统,可以对各种运动状态下的加速度数据进行观察。采集系统通过无线串口和电脑连接,加速度数据在电脑上位机上以波形方式显示。 4、对设计出来的电子计步器进行步数检测实验评估,实验结果表明能够较好地完成设计目标,步数误差在5%左右,很好的满足了设计要求。 6.2 展望 市场上的电子计步器有很多类型,但关于计步器的研究仍在不断继续,计步器的功能变得越来越多且越来越复杂。对于本设计有以下几点展望: 1、选用功耗低的锂电池或者纽扣电池进行供电,降低功耗,延长续航时间。 2、能够减小电路板的体积,并且给它安装上外壳,使它更加方便携带。 3、试验中采集加速度数据时,运动速度和步伐相对恒定。但是在日常生活中由于周围环境的影响,人的运动速度和步伐是不断变化的,在未来的研究中希望对不同速度下的人体运动进行识别。 4、手腕式计步器由于不规则的手腕摆动,步数检测不是很精确。在未来进一步的研究中希望对运动状态进行特征分析,深入探索各种特征分类器,选择合适的方法来提高手腕式计步器的精确度。 25 6.3 课题研究对环境以及社会的影响 电子式计步器是由于人们进行跑步时需要计算步数才产生的,后来还拥有了对卡路里、心率、体表温度、睡眠质量的监测功能。科学研究表明,使用计步器可以促使人们每天多走3000多步,这不仅锻炼了人的身体,还节省日常出行所需要消耗的燃料,既节能又环保。 如今,中国人口老龄化逐年提高,同时因为生活水平的改善也产生了大量的肥胖人口,这不利于整体社会的发展。当计步器能够深入到人们生活中去,人们就会逐步养成一种勤于锻炼的好习惯。老年人锻炼可以预防一些心血管疾病,肥胖人口锻炼可以展现出积极向上的精神外貌,这些都会促进社会主义精神文明建设,加快富强、民主的社会主义强国的建设步伐。 26 附录 附录一 系统硬件原理图和PCB 27 附录二 系统实物图 附录三 系统核心 /∗∗∗∗∗∗∗清零步数∗∗∗/ void Liquidation_S(u32 ∗Value) {u32 Value_B=∗Value; while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0); Value_B=∗Value; ∗Value=0; Step_Count_B=∗Value; TFT_PutString( 40,0," 00000²½",RED,BLACK);} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); 28 ∗Value=Value_B;} Dis_Buff[0]=∗Value/10000+′0′; TFT_PutChar(40,8,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,16,Dis_Buff[1], RED,BLACK); TFT_PutChar(40,24,Dis_Buff[2], RED,BLACK); TFT_PutChar(40,32,Dis_Buff[3], RED,BLACK);TFT_PutChar( 40,40,Dis_Buff[4],RED,BLACK); {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} }} /∗∗∗∗∗清零时间∗∗∗∗∗∗/ void Liquidation_T(u32 ∗Value) {u32 Value_B=∗Value; while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0); Value_B=∗Value; ∗Value=0; TFT_PutString( 40,0," 00:00:00",RED,BLACK);}elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); ∗Value=Value_B;} T_H=∗Value/3600; T_M=∗Value%3600/60; T_S=∗Value%3600%60; Dis_Buff[0]=T_H/10+′0′; Dis_Buff[1]=T_H%10+′0′; TFT_PutChar(40,8,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,16,Dis_Buff[1], RED,BLACK); Dis_Buff[0]=T_M/10+′0′; Dis_Buff[1]=T_M%10+′0′; TFT_PutChar( 40,32,Dis_Buff[0],RED,BLACK); TFT_PutChar(40,40, Dis_Buff[1],RED,BLACK); Dis_Buff[0]=T_S/10+′0′; Dis_Buff[1]=T_S%10+′0′; TFT_PutChar(40,56,Dis_Buff[0], RED,BLACK); TFT_PutChar(40,64,Dis_Buff[1], RED,BLACK); if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} } } /∗∗∗∗∗卡路里消耗设置函数∗∗∗∗∗∗∗/void Enter_Calories(float∗Calories) {u8 buff[3]={0,0,0}; while(1) if((∗Calories_V)>=9) {(∗Calories_V)=9;}} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); (∗Calories_V)−=0.01; if((∗Calories_V)<=0) {(∗Calories_V)=0;} buff[0]=(u32)((∗Calories_V) ∗100)/100+′0′; buff[1]=(u32)((∗Calories_V) 29 ∗100)%100/10+′0′; buff[2]=(u32)((∗Calories_V) ∗100)%100%10+′0′; TFT_PutChar( 40,8,buff[0],RED,BLACK); TFT_PutChar( 40,24,buff[1],RED,BLACK); TFT_PutChar( 40,32,buff[2],RED,BLACK); if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); break;} } } /∗∗∗∗∗∗彩屏设置函数∗∗∗∗∗∗∗∗/voidSet_Function() {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0); TFT_Clear(BLACK); switch(Fun_Sp) {case 1:TFT_PutString(10,0,"1.清零步数",RED,YELLOW); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 2:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,YELLOW); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 3:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,YELLOW); break;} while(1) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0||GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0||GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) {if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_14)==0) delay_ms(50); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0); Fun_Sp−−; 30 if(Fun_Sp<=1) {Fun_Sp=1;}} elseif(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0) {delay_ms(50); while(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_13)==0); Fun_Sp++; if(Fun_Sp>=FUN_COUNT) {Fun_Sp=FUN_COUNT;}} switch(Fun_Sp) { case 1:TFT_PutString(10,0,"1.清零步数",RED,YELLOW); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 2:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,YELLOW); TFT_PutString(70,0,"3.设置卡路里",RED,BLACK); break; case 3:TFT_PutString(10,0,"1.清零步数",RED,BLACK); TFT_PutString(40,0,"2.清零时间",RED,BLACK); TFT_PutString(70,0,"3.设置卡路里",RED,YELLOW); break;} if(GPIO_ReadInputDataBit( GPIOB,GPIO_Pin_11)==0) delay_ms(50); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0); switch(Fun_Sp) { case 1:TFT_Clear(BLACK); TFT_Clear(BLACK); case 2:TFT_Clear(BLACK); TFT_PutString(10,0,"清零时间:",RED,BLACK); TFT_PutString( 40,0," ",RED,BLACK); LiquidationS(&TimeCount); TFT_Clear(BLACK); TFT_PutString(10,"步数: ",RED,BLACK); TFT_PutString(40,0,"时间: : ",RED,BLACK); ∶ TFT_PutString(100,0,"卡路里: ",RED,BLACK); break; 31 case 3:TFT_Clear(BLACK); TFT_PutString(10,0,"清零卡路里:",RED,BLACK); TFT_PutString( 40,0," . ",RED,BLACK); Enter_Calories(&Calories_V); TFT_Clear(BLACK); TFT_Clear( BLACK);TFT_PutString(10,0,"步数: ",RED,BLACK); TFT_PutString(40,0,"时间: ∶ : ",RED,BLACK); TFT_PutString(100,0,"卡路里: ",RED,BLACK); /∗MPU650初始化∗/ bool mpu6500Init(void) {u8temp=iicDevReadByte( bool state = false; delay_ms(50); iicDevWriteByte( state = true; return state;} /∗获取6轴原始数据∗/ /∗void mpu6500GetRawData(s16 ∗ ax,s16∗ ay,s16∗ az,s16∗ gx,s16∗ gy,s16∗ gz) {iicDevRead( ∗ax = (((s16) buffer[0]) break;} break;} } } } Dis_Buff[0]=Step_Count/10000; DisBuff[1]=StepCount%10000 /1000+′0′; TFT_PutChar( 10,40,Dis_Buff[0],RED,BLACK); TFT_PutChar( 10,48,Dis_Buff[1],RED,BLACK); TFT_PutChar( 10,56,Dis_Buff[2],RED,BLACK); TFT_PutChar( 10,64,Dis_Buff[3],RED,BLACK); TFT_PutChar( 10,72,Dis_Buff[4],RED,BLACK);<< 8) | buffer[1]; ∗ay = (((s16) buffer[2]) << 8) | buffer[3]; ∗az = (((s16) buffer[4]) << 8) | buffer[5]; ∗gx = (((s16) buffer[8]) << 8) | buffer[9] ∗gy = (((s16) buffer[10]) << 8) | buffer[9]; ∗gz = (((s16) buffer[12]) << 8) | buffer[13];} s16 GetAngleX() {s16 roll;float ax,ay,az,gx,gy,gz; roll = gx; 32 return roll;} s16 GetAngleY() {s16 pitch; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); pitch = gy; return pitch;} s16 GetAngleZ(){ s16 pitch; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); pitch = gz; return pitch;} s16 GetAccelX() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = ax; return roll;} s16 GetAccelY() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = ay; return roll;} s16 GetAccelZ() {s16 roll; float ax,ay,az,gx,gy,gz; mpu6500GetRawData( &ax,&ay,&az,&gx,&gy,&gz); roll = az; return roll;} /∗卡尔曼滤波∗/ double Kalman_Filter( double angle_m,double gyro_m) {static double x=0; static double p=0.000001; static double Q=0.000001; static double k=0; x=x+ gyro_m∗0.001f; p=p+Q; k=p/(p+GyroR); x=x+k∗(angle_m−x); p=(1−k)∗p; return x; } /∗数据归一化∗/ void Angle_get() {GyroYo=GyroY; GyroY=GetAngleZ(); if(GyroY<32768) GyroY+=65535;GyroY=GyroY−Gyropitchfloat; if((GyroY−GyroYo) >32768) GyroY−=65535; else if((GyroYo−GyroY) >32768) GyroY+=65535; 33 Gyropitch=GyroY∗Gyrorate; Acc_Zo=Acc_Z; AccZ= GetAccelX( ); if(Acc_Z<32768) Acc_Z+=65535; if((AccZ−AccZo)>32768) Acc_Z−=65535; else if ((AccZo−AccZ)>32768) Acc_Z+=65535; Acc_Z=Acc_Z−Anglefloat; MMA_Angle=Acc_Z∗Anglerate; Angle=Kalman_Filter( MMA_Angle,Gyropitch);} unsigned long Step_Count( float axis0,float axis1,float axis2){ unsigned int nowPos = 0; int ppDiff = 0; int timeDiff = 0; pSta = FALLING_EDGE; newMax = lastPos; walkSta = TRUE;} walkSta = FALSE;} lastPos = nowPos; if(walkSta==TRUE){ walkSta = FALSE; ppDiff = newMax − newMin; if(ppDiff > P_P_DIFF){ timeDiff = GetTime() − lastTime; if(timeDiff < FAST_WALK_TIME_LIMIT_MS){ return stepCount;} else if(timeDiff > SLOW_WALK_TIME_LIMIT_MS){ walkOkSta = FALSE; stepOK = 0; return stepCount;} stepOK++; if(stepOK>=STEP_OK){ walkOkSta = TRUE;} lastTime = GetTime(); }} if(walkOkSta==TRUE){ stepCount += stepOK; stepOK = 0;} return stepCount;} RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); TIM_TimeBaseInit( TIM3,&TIM_TimeBaseStruct); TIM_ClearFlag( TIM3,TIM_IT_Update);TIM_ITConfig( TIM3,TIM_IT_Update,ENABLE); NVIC_Tim3_Configuration(); TIM_Cmd(TIM3,ENABLE);} void NVIC_Tim3_Configuration(void){ NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2); NVIC_Init(&NVIC_InitStructure);} void TimerInit(u16 per,u16 psc){ NVIC_InitTypeDef NVIC_InitStructure; 34 RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4,ENABLE); TIM4,&TIM_TimeBaseInitStructure); TIM_ITConfig( TIM4,TIM_IT_Update,ENABLE); TIM_ClearITPendingBit( TIM4,TIM_IT_Update); NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM4,ENABLE); } void USART3_Config(void) {GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE); RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART3,ENABLE); GPIO__Pin = GPIO_Pin_10; GPIO__Mode = GPIO_Mode_AF_PP; GPIO__Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO__Pin = GPIO_Pin_11; GPIO__Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB,&GPIO_InitStructure); USART__BaudRate = 115200; USART__StopBits = USART_StopBits_1; USART__Parity = USART_Parity_No ; USART__Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init( USART3,&USART_InitStructure); USART_ITConfig( USART3,USART_IT_RXNE,ENABLE); USART_ITConfig( USART3,USART_IT_IDLE,ENABLE); USART_Cmd(USART3,ENABLE); } void OutPut_Data(void) { int temp[4] = {0}; unsigned int temp1[4] = {0}; u8 databuf[10] = {0} u16 CRC16 = 0; for(i=0;i<4;i++) {temp[i] = (int)OutData[i]; temp1[i] = (unsigned int)temp[i];} for(i=0;i<4;i++) {databuf[i∗2] = (u8)(temp1[i]%256); databuf[i∗2+1] =(u8)(temp1[i]/256); } CRC=CRC(databuf,8); databuf[8] = CRC16%256; databuf[9] = CRC16/256; for(i=0;i<10;i++){ printf("%c",databuf[i]); }}35 36