iOS中计步器的实现方案及原理
前言
当前社会随着社会水平的越来越高,人民饮食也更加的丰富多样,随之而来就成就了越来越多可爱的胖子。胖子多了,一些“不良商贩”发现商机研发出了运动健身类的APP,如:Keep,咕咚,动动,趣走等等,就连我们的移动社交老大哥腾讯也相继推出了微信运动,qq运动等功能。那么今天我将通过此篇文章来给大家简单介绍一下在我们的iOS中想要实现一个计步器的功能有哪几种方案,以及实现它的一些原理。
实现方案
从目前技术角度来看,ios实现计步功能主要有三种方案:加速计,CMPedometer,HealthKit。下面我们分别来说说这三种实现方案。
一、加速计
iOS4以后苹果已经提供了CoreMotion(核心运动框架),但是并没有提供给我们关于步数计算和行走公里数的计算方法。iOS7出现以后苹果在CoreMotion中添加了CMStepCounter,此时我们就可以通过CMStepCounter来计算行走的步数和公里数据了。iOS8的时候又出现了CMPedometer,它是CMStepCounter的替代者,CMPedometer中的数据种类也变得更加多样,比如我们不只可以获取步数和公里数,我们还可以计算我们的上下楼层数,以及我们活动的平均速度等等。但是由于硬件的缘故,在iPhone5s以下的设备中没有协处理器,就设备本身来讲我们是无法通过手机去计算步数和公里数的。难道这样我们就放弃了我们要做计步器的初衷了吗?当然不是,下面我们就来介绍在iPhone5s以下设备中比较流行的一种计步器的实现方案——加速计
加速计是CoreMotion框架中用来计算设备在三维空间中的加速度的。那么我们如何通过加速计来计算步数呢?接下来我将详细介绍加速计计算步数的原理。
原理
我们通过加速计来进行特性分析,分别用以检测人步行中三个方向的加速度变化,如下图
当我们在水平步行运动中,垂直和前进两个加速度会呈现周期性变化,如图下图所示,在步行收脚的动作中,由于重心向上单只脚触地,垂直方向加速度是呈正向增加的趋势,之后继续向前,重心下移两脚触底,加速度相反。水平加速度在收脚时减小,在迈步时增加。
反映到图表中,我们可以看到在步行运动中,垂直和前进产生的加速度与时间大致为一个正弦曲线,而且在某点有一个峰值,其中垂直方向的加速度变化最大,通过对轨迹的峰值进行检测计算和加速度阈值决策,即可实时计算用户运动的步数。
因为用户在运动中可能手平持设备或者将设备置于口袋中,所以设备的放置方向不定,为此我们通过计算三个加速度的矢量长度,获得一条步行运动的正弦曲线轨迹。
第二步是峰值检测,我们记录了上次矢量长度和运动方向,通过矢量长度的变化,可以判断目前加速度的方向,并和上一次保存的加速度方向进行比较,如果是相反的,即是刚过峰值状态,则进入计步逻辑进行计步,否则舍弃。通过对峰值的次数累加可得到用户步行步伐。
最后是去多余的干扰,手持设备会有一些低幅度和快速的移动状态,或是我们说的手抖和快速甩动,或者某个恶作剧用户想通过短时快速反复摇动设备来模拟人走路(当然也包括哪些将手机捆绑在狗尾巴上的天才),这些干扰数据如果不剔除,会影响记步的准确值,对于这种干扰,我们可以通过给检测加上阈值和步频判断来过滤。比如我们可以设置一个一步所用的时间,如果短于这个时间我们就过滤掉。就像博尔特,100米最快也需要9.69s。如果有生物跑100米所用时间小于8秒了,按照现在的身体极限我们就可以断定这个生物99.9%不是人类。
二、CMPedometer
iOS8以后,CoreMotion框架中为我们提供了一个获取用户活动信息的对象CMPedometer,通过CMPedometer我们可以获取用户的活动信息,如:行走步数,行走的公里数,上下楼层数以及平均速度等。因此我们通过CMPedometer调用它的API接口就完全可以获取到我们想要的步数。下边我将介绍一下我们用到的API。
1. 当我们在使用CMPedometer查询我们需要的数据的时候,比如:步数,公里数,上下楼层数等,我们需要先通过判断接口判断设备中的这些功能是否可用,如下边方法:
//判断计步器是否可用
+ (BOOL)isStepCountingAvailable;
//是否支持距离估计
+ (BOOL)isDistanceAvailable;
//是否支持楼梯计数
+ (BOOL)isFloorCountingAvailable;
//等等,不一一进行介绍了。
2. 因为我们需要从设备中获取我们需要的活动数据,所以我们需要调用的是设备的数据查询接口,如下边方法:
//此方法是从某一时间段开始,连续的采集数据,当设备中的活动数据发生变更就会回调此方法,此方法是在串行队列中执行。
-(void)startPedometerUpdatesFromDate:(NSDate*)start withHandler:(CMPedometerHandler)handler;
//此方法是查询某一时间段的数据,时间可长达7天,此方法是在串行队列中执行。
- (void)queryPedometerDataFromDate:(NSDate*)start toDate:(NSDate*)endwithHandler:(CMPedometerHandler)handler;
3. 废话不多说,直接上代码
连续获取数据
获取某一时间段的数据
更新UI
三、HealthKit
iOS8以后苹果推出了一个新的手机自带的App——健康App,这个App就是用来显示我们的活动信息的。并且苹果还为我们提供了一个访问健康App的框架HealthKit。我们可以理解HealthKit是一个活动数据的中央存储库,而健康App是用来展示这个库中的数据的。那么HealthKit是如何获取用户的活动数据的呢?首先我们想一下,健康App中的运动数据是从哪儿来的?运动信息嘛当然是从核心运动框架里边来的了,那么访问核心运动框架中的信息都是通过CMPedometer来访问的,所以我们就知道了HealthKit底层也是通过上边我们提到CMPedometer来获取用户信息的,只不过它的获取方法在手机内部,通过协处理器来进行计算的。而协处理器在5s以后的设备中才出现的,所以在5s以前的设备中是无法获取用户的运动信息的。
我们在使用HealthKit的时候的注意事项:
A:需要把HealthKit配置到AppID中
B:Xocede中需要打开HealthKit的开关选项
C:需要调用isHealthDataAvailable方法检测HealthKit是否可用
D:在读取和写入数据之前需要先获取权限,并且设置读取和写入的数据类型
申请权限
F:在info.plist文件中,增加NSHealthShareUsageDescription用于读取数据的描述和NSHealthUpdateUsageDescription用于写入数据的描述。
设置好上面的配置信息后我们如何访问和读取数据呢,下边我们将以特征数据(性别)和样本数据(步数)为例,话不多说,直接上代码。
性别读取:
特征数据我们一般都是用HKHealthStore直接访问,返回一个性别类型对象,然后通过对象值判断性别。
注意:特征数据只能读取不能写入,如何修改的话只能通过健康App来修改
步数读取:
a:事例查询
此查询方式为事例查询,可以查询到每一条写入的数据,并且能可以获取数据的来源,通过来源可以分析数据是设备背身记录的数据还是三方app写入的数据。
b:统计查询
此查询方式为统计查询,查询结果为步数的总和,不区分数据来源。
步数写入:
写入数据一般我们采用saveObject的形式
以上为三种实现计步器的实现方案,下边我将解答一下经常有人问的问题:
1.如果我们用HealthKit写入数据,那么我在其他App中读取数据,那么这个数据是不是很大(比如微信的步数排行榜)?
首先我们介绍一下微信运动,当我们首次打开微信运动的时候会出现一个提示框,询问是否允许访问健康App,由此可以证明微信运动就是通过HealthKit来访问数据的。那么会不会出现我们上面提到的问题呢?理论上是会的,前提是微信没有进行数据源判断。上边我们提到了,每条数据我们都可以知道它的来源,通过来源我们就可以过滤数据,微信运动有可能也是通过这种方式来处理了数据(个人猜测),通过此方式来保证数据的准确。
2.如果我通过HealthKit写入数据后,其他APP通过CMPedometer来访问数据会不会很大?
首先CMPedometer访问的是核心运动框架的数据,而HealthKit写入的数据只会保存在健康App中,健康App会整合数据,将所有来源的数据进行合并,但是核心运动框架中的数据还是与它分离的,只是HealthKit中也会包含核心运动框架中的数据而已。所以结论就是CMPedometer访问的还是核心运动框架的数据,并不会读取写入的数据。