关于微信运动和其他APP的计步器究竟是如何实现的?

3,737 阅读7分钟
原文链接: rdc.hundsun.com
自从微信推出“微信运动”功能之后很多小伙伴就在惊叹,为什么微信能够如此准确的统计我们日常行走记录?其工作原理是什么?
对于不太了解这个问题的朋友可能会非常好奇,今天我们就来向大家详细介绍一下利用Android手机自带的计步传感器与加速度传感器结合算法的方式进行计步功能的设计和实现原理以及实战。
【计步器&检测器介绍】 Android4.4 Kitkat 新增了STEP COUNTER 和STEP DETECTOR传感器,使用这2种传感器可以实现计步功能。
▲ TYPE_STEP_COUNTER:计步器(记录历史步数累加值)
这种类型的传感器返回用户自上次重新开机以来所记录的步数。该值作为浮点数返回(小数部分设置为零),仅在系统重新启动时才将其重置为零。事件的时间戳设置为采取该事件的最后一步的时间。该传感器以硬件实现,功耗低。如果要持续跟踪长时间的步数,勿取消注册该传感器以便即使APP处于挂起模式也会在后台继续计步。该传感器适用于健身跟踪应用。
  TYPE_STEP_DETECTOR:检测器(检测每次步伐数据)
这种类型的传感器每次触发一个事件,唯一允许的返回值为1.0。与任何其他事件一样,时间戳表示事件(这里是一步)何时发生,这对应于当脚撞到地面时,产生加速度的高变化。该传感器仅用于检测每个单独的一步,如果需要在一段时间内累积的步数,可以使用此传感器。
  TYPE_STEP_ COUNTER与TYPE_STEP_DETECTOR计步实现(核心部分)
使用手机内置的计步传感器注册监听后直接重写onSensorChanged方法,每次检测到人走一步就会调用这个方法。在此方法中可以得到一个值(event.value[0]),不同类型的内置计步传感器对此值的处理并不相同。
在TYPE_STEP_ COUNTER类型中由于此类型返回的值是自上次重新开机以来所记录的步数,所以需要在启动计步服务的时候判断是否可以获取之前已有的步数,如果没有则将第一次从event.value[0]中获得的值作为此次计步服务的“基准值”,后续从event.value[0]得到的值都需要与此“基准值”进行差值计算,具体实现可以参考图1.1与图1.2中的代码示列。
在TYPE_STEP_ DETECTOR类型中由于每一次的方法回调代表单独的一步,则只需要进行计数累积即可,具体实现可以参考图1.1与图1.2中代码示列。 图1 手机内置的计步传感器计数代码变量定义 图2 手机内置的计步传感器计数代码方法实现
 【加速度传感器结合算法的方式 】 人在走路时大致分为下面几种场景:
1.正常走路,手机拿在手上(边走边看、甩手、不甩手)
2.慢步走,手机拿在手上(边走边看、甩手、不甩手)
3.快步走,手机拿在手上(甩手、不甩手、走的很快一般不会看手机吧)
4.手机放在裤袋里(慢走、快走、正常走)
5.手机放在上衣口袋里(慢走、快走、正常走)
6.上下楼梯(上面五中场景可以在这个场景中再次适用一遍)
以上,不管出于哪一种场景(其实对应手机不同的运动规律),g-sensor的三轴数据都是有规律可以寻找的。每一步都有特征点,找到这个特征点,就是识别出来一步。下面推荐一个工具,叫gsensor-debug,可以观察三轴的曲线,下面是手机上下摆动的曲线,如图3和图4所示。 图3 手机上下摆动数值 图4 手机上下摆动曲线
这是很规律曲线只要检测波峰就行了,实际的走路曲线会有很多杂波,算法的作用就是滤除这些杂波。
【算法核心介绍】 使用加速度传感器进行计步统计时,若发生变化可以得到传感器三轴的值(X,Y,Z)然后计算他们的平均值,这样做的目的是为了平衡在某一个方向数值过大造成的数据误差,然后将该值与上一时间点的值进行比较,判断是否为波峰或波谷,如果是就相应的保存下来。如果检测到了波峰,并且符合时间差以及阈值的条件,则判定位1步,如果符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中。
检测步子就是检测波峰,但是要滤除无效的波峰,主要采用了如下三种措施: ▪ 规定曲线连续上升的次数; ▪ 波峰波谷的差值需要大于阈值; ▪ 阈值是动态改变的。
另一个是一些参数的初始值,比如initialValue 以及ThreadValue 的初始值,以及averageValue函数的梯度化范围值,算法中主要的变量定义如图5所示,变量的含义见注释。 图5 算法变量定义

算法中的主要内容包含:
1.加速度传感器回调方法onSensorChanged中三轴的值进行处理 在该onSensorChanged()方法中,我们先得到传感器事件,获得加速度传感器并且算出加速度传感器的x、y、z三轴的平均数值(这是为了平衡在某一个方向数值过大造成的数据误差),之后交给detectorNewStep方法处理,如图6所示。 图6  x、y、z三轴均值计算
2.步子检测并开始计步 在detectorNewStep方法中,首先判断上次传感器三轴的平均值gravityOld是否为0,如果为零说明这是第一次进行探测将当前的值赋给gravityOld,如果不为零我们通过当前的值values和上次传感器的值gravityOld值通过detectorPeak方法进行判断,检测是否为波峰(具体算法下面会给出),当检测到了波峰后记录这次的时间和上次的时间,如果两次的时间差大于等于250毫秒并且波峰与波谷的差大于阈值时就判定为一步,调用步数回调对象的相应方法传递步数+1的信息;而如果符合时间差条件,但波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中。最后将传入的值赋给grayityOld,如图7所示。 图7 步子检测
3.检测波峰 先记录下lastStatus即上一点的状态,上升还是下降,然后比较新值和旧值如果newValue >= oldValue就设置和更新 isDirectionUp、continueUpCount 否则就将contineUpCount赋给continueUpFormerCount(上一点的持续上升的次数,为了记录波峰的上升次数)。最后判断当满足波峰判断的4个条件(见代码注释)的话,这个值就是波峰值返回true。如果上一次状态为下降,本次状态为上升则这个值为波谷值并返回false,如图8所示。 图8检测波峰

4.阈值的计算 动态生成阈值,阈值是为了跟波峰与波谷的差值进行比较,进而判断是否为1步,如图9所示。
5.梯度化阈值 阈值进行梯度化,取4组数值,进行梯度化。如图10所示。 图10梯度化阈值
【总结】 基于以上的介绍,推荐当设备是Android4.4 Kitkat及以上的时候,如果存在内置的计步传感器时则优先使用内置的计步的传感器进行计步统计,否则就使用加速度传感器结合算法的方式进行计步统计,流程如图11所示。

图11整体流程


恒生技术之眼原创文章,未经授权禁止转载。详情见(点击)转载须知