手機的計步器軟體原理是什麼?

android 和 ios 平台上都有這類軟體


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;

}


其實方法不止一種:

樓上「黃桂超」所介紹的只是其中的一種方式。也有基於統計的,也有基於角度變化的。

不過確實二次波峰是基礎,並且也被用到其他的演算法中。

事實上,基於模板匹配的是精度最高的。

而且根據使用的場景區分也不一樣:

如果是隨身的設備,通常要簡單些,如果是手持設備,要複雜些,因為環境更複雜。

呃。。。這個問題可說的點挺多的,回頭細節問題可以給我發消息,或者以後有時間再來補充好了。。。

追加:

好吧,其實現在apk中主流的演算法是這樣的:

@Override

public void onSensorChanged(SensorEvent event) {

long currentUpdateTime = System.currentTimeMillis();

long timeInterval = currentUpdateTime - lastUpdateTime;

if (timeInterval &< UPTATE_INTERVAL_TIME)

return;

lastUpdateTime = currentUpdateTime;

float x = event.values[0];

float y = event.values[1];

float z = event.values[2];

float deltaX = x - lastX;

float deltaY = y - lastY;

float deltaZ = z - lastZ;

lastX = x;

lastY = y;

lastZ = z;

double speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ

* deltaZ)

/ timeInterval * 10000;

Log.v("thelog", "===========log===================");

if (speed &>= SPEED_SHRESHOLD) {

onShakeListener.onShake();

}

}

代碼來源:ShakeListener1_wg96160078_新浪博客

如果你在百度搜索"ShakeListener",你會找到更多相似代碼。

不過,這只是最簡單的一種實現方式,當然,其實效果還不錯啦,再當然,你需要好好調整其中的兩個參數。


通過重力加速計感應,重力變化的方向,大小。與正常走路或跑步時的重力變化比對,達到一定相似度時認為是在走路或跑步。實現起來很簡單,只要手機有重力感應器就能實現。


跟重力感應器有關係,曾規划過一款類似軟體也有聽硬體廠商講解過重力感應器的原理。

你可以先嘗試寫一個能夠獲取重力感應器XYZ值讀數的軟體,然後去模擬走步,跑步等各種情況取得樣值,之後的你應該知道該怎麼做。


主要思路是:3軸加速度感測器採集數據-&>濾波-&>二次波峰監測,波峰數就是步數。另外得有一些防抖動方法:連續監測到3(或其它)個波峰才開始算。

可以參考我們的一個產品「 計步器」 :

完整版:http://itunes.apple.com/us/app/id396344676?mt=8

免費版:http://itunes.apple.com/us/app/id398117940?mt=8


新版的安卓(android4.4)和蘋果(ios7)都自帶計步器API,不知道準確率比其他app如何

老版的需要做計步器就是使用樓上各位所說的方法


使用安卓手機自帶的sensorManager來計步時,需要注意的幾個點就是開關機/零點更新數據/禁止app讀取運動數據吧


不知道有沒有大神用c語言寫出來計步演算法


推薦閱讀:

如何獲取學校網站公告的RSS?
如何評價夸克瀏覽器在2017年6月25日關於六色彩虹同志平權運動的策劃?
Android 有哪些精緻的 App?
為什麼很多軟體出新版本,安卓要比 iOS 慢?

TAG:iPhone應用 | iOS開發 | Android應用 | Android開發 |