手机计步器原理

姓名:周君会        学号:17011210526

转载自:

https://www.zhihu.com/question/19808224

【嵌牛导读】:计步器可以记录我们的步数,督促我们的健康,对我们的生活带来了极大地便利。

【嵌牛鼻子】:运动手环也是极其常见,手机中自带的也有计步器。

【嵌牛提问】:但是手机中自带的计步器是如何实现精准计步的呢,它的相关原理是什么?

【嵌牛正文】:

Android_基于G-Sensor的计步算法

一、写在分享之前

学习Android也有将近一年的时间了,一直在看大牛们分享的知识,今天也想分享自己之前的一点研究,关于计步器算法的。目前在计步领域比较领先的有乐动力以及春雨计步器,在做算法的参数调试的时候也是一直拿这两个应用做对比。乐动力当之无愧行业第一,不管是应用的体验还是准确度都是非常棒,春雨计步器的亮点是轻量级,使用以及界面操作都很简单。之前因为一些需求,需要做一个计步器,所以就开始自己研究算法了,各种场景(走路拿在手上,放在口袋,跑步),算法的准确度大概可以达到95.7%,综合起来觉得是比春雨略好,但是赢不了乐动力(可以达到97.7%)在体验和大局观为王的互联网时代,我觉得技术上的差距会越来越小,重要的是体验还有对于产品的定位,所以决定将算法与大家分享,第一是希望可以帮到到家,第二也是希望大家提一些意见,让这个算法可以得到改进。

计步器apk下载

apk反编译下载

二、计步器算法的总体思路以及辅助调试的工具

人在走路时大致分为下面几种场景:

1、正常走路,手机拿在手上(边走边看、甩手、不甩手)

2、慢步走,手机拿在手上(边走边看、甩手、不甩手)

3、快步走,手机拿在手上(甩手、不甩手、走的很快一般不会看手机吧)

4、手机放在裤袋里(慢走、快走、正常走)

5、手机放在上衣口袋里(慢走、快走、正常走)

6、上下楼梯(上面五中场景可以在这个场景中再次适用一遍)

以上,不管出于哪一种场景(其实对应手机不同的运动规律),g-sensor的三轴数据都是有规律可以寻找的。

每一步都有特征点,找到这个特征点,就是识别出来一步。

下面推荐一个工具,叫gsensor-debug,可以观察三轴的曲线,下面是手机上下摆动的曲线

这是很规律曲线只要检测波峰就行了,实际的走路曲线会有很多杂波,算法的作用就是滤除这些杂波(走路的波形可以用工具自己看,可以保存为文件,用excel打开有数据,将数据转换为波形就可以自己看)

三、算法的介绍(贴出核心代码)1、变量的定义//存放三轴数据  float[] oriValues = new float[3];  final int valueNum = 4;  //用于存放计算阈值的波峰波谷差值  float[] tempValue = new float[valueNum];  int tempCount = 0;  //是否上升的标志位  boolean isDirectionUp = false;  //持续上升次数  int continueUpCount = 0;  //上一点的持续上升的次数,为了记录波峰的上升次数  int continueUpFormerCount = 0;  //上一点的状态,上升还是下降  boolean lastStatus = false;  //波峰值  float peakOfWave = 0;  //波谷值  float valleyOfWave = 0;  //此次波峰的时间  long timeOfThisPeak = 0;  //上次波峰的时间  long timeOfLastPeak = 0;  //当前的时间  long timeOfNow = 0;  //当前传感器的值  float gravityNew = 0;  //上次传感器的值  float gravityOld = 0;  //动态阈值需要动态的数据,这个值用于这些动态数据的阈值  final float initialValue = (float) 1.3;  //初始阈值  float ThreadValue = (float) 2.0;  private StepListener mStepListeners;

2. 代码,结合注释看

检测步子就是检测波峰,但是要滤除无效的波峰,主要采用了如下三种措施

a、规定曲线连续上升的次数

b、波峰波谷的差值需要大于阈值

c、阈值是动态改变的

另一个是一些参数的初始值,比如initialValue 以及ThreadValue 的初始值,以及averageValue函数的梯度化范围值

需要结合各种场景的波形图来统计,还有几十实际的测试来调试参数,这些参数大概前后调了两个星期,其实总体思路不复杂。

下面贴出核心代码以及一些注释:

(因为一些原因,整个工程我就不传了,后面有时间我可以将app传上来)

/*

* 注册了G-Sensor后一只会调用这个函数

* 对三轴数据进行平方和开根号的处理

* 调用DetectorNewStep检测步子

* */

@Override

public void onSensorChanged(SensorEvent event) {

for (int i = 0; i < 3; i++) {

oriValues[i] = event.values[i];

}

gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0]

+ oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]);

DetectorNewStep(gravityNew);

}

/*

* 检测步子,并开始计步

* 1.传入sersor中的数据

* 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步

* 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中

* */

public void DetectorNewStep(float values) {

if (gravityOld == 0) {

gravityOld = values;

} else {

if (DetectorPeak(values, gravityOld)) {

timeOfLastPeak = timeOfThisPeak;

timeOfNow = System.currentTimeMillis();

if (timeOfNow - timeOfLastPeak >= 250

&& (peakOfWave - valleyOfWave >= ThreadValue)) {

timeOfThisPeak = timeOfNow;

/*

* 更新界面的处理,不涉及到算法

* 一般在通知更新界面之前,增加下面处理,为了处理无效运动:

* 1.连续记录10才开始计步

* 2.例如记录的9步用户停住超过3秒,则前面的记录失效,下次从头开始

* 3.连续记录了9步用户还在运动,之前的数据才有效

* */

mStepListeners.onStep();

}

if (timeOfNow - timeOfLastPeak >= 250

&& (peakOfWave - valleyOfWave >= initialValue)) {

timeOfThisPeak = timeOfNow;

ThreadValue = Peak_Valley_Thread(peakOfWave - valleyOfWave);

}

}

}

gravityOld = values;

}

/*

* 检测波峰

* 以下四个条件判断为波峰:

* 1.目前点为下降的趋势:isDirectionUp为false

* 2.之前的点为上升的趋势:lastStatus为true

* 3.到波峰为止,持续上升大于等于2次

* 4.波峰值大于20

* 记录波谷值      * 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值      * 2.所以要记录每次的波谷值,为了和下次的波峰做对比

* */      public boolean DetectorPeak(float newValue, float oldValue) {

lastStatus = isDirectionUp;

if (newValue >= oldValue) {              isDirectionUp = true;

continueUpCount++;

} else {

continueUpFormerCount = continueUpCount;

continueUpCount = 0;

isDirectionUp = false;

}

if (!isDirectionUp && lastStatus

&& (continueUpFormerCount >= 2 || oldValue >= 20)) {

peakOfWave = oldValue;

return true;

} else if (!lastStatus && isDirectionUp) {

valleyOfWave = oldValue;

return false;

} else {              return false;

}

}

/*

* 阈值的计算

* 1.通过波峰波谷的差值计算阈值

* 2.记录4个值,存入tempValue[]数组中

* 3.在将数组传入函数averageValue中计算阈值

* */

public float Peak_Valley_Thread(float value) {

float tempThread = ThreadValue;

if (tempCount < valueNum) {

tempValue[tempCount] = value;

tempCount++;

} else {

tempThread = averageValue(tempValue, valueNum);

for (int i = 1; i < valueNum; i++) {

tempValue[i - 1] = tempValue[i];

}

tempValue[valueNum - 1] = value;

}

return tempThread;

}

/*

* 梯度化阈值

* 1.计算数组的均值

* 2.通过均值将阈值梯度化在一个范围里

* */

public float averageValue(float value[], int n) {

float ave = 0;

for (int i = 0; i < n; i++) {

ave += value[i];

}

ave = ave / valueNum;

if (ave >= 8)

ave = (float) 4.3;

else if (ave >= 7 && ave < 8)

ave = (float) 3.3;

else if (ave >= 4 && ave < 7)

ave = (float) 2.3;

else if (ave >= 3 && ave < 4)

ave = (float) 2.0;

else {

ave = (float) 1.3;

}

return ave;

}

你可能感兴趣的:(手机计步器原理)