計步器演算法是如何實現的?
近來出於自己對運動的興趣,突然想做一個app,但是在核心演算法實現上出現了一點難題。在數據採集之後,通過excel可以明顯的觀測出數據存在的周期心跳,但是對於離散的數據,統計心跳個數著實犯難。此數據基於加速度感測器採集。
想到兩個方案:1.取一個固定間斷的點進行平均,換取曲線的光滑,然後再統計波峰2.對於和加速度的偏移角度達到某個閾值。但上述兩種方案的精度都很低,而且不通用。所以向知友們討教一下是否有好的實現方案,能夠最大限度地滿足通用性及精確性。還有可以順路告訴我對於現有的一些計步app的演算法又是如何實現的。本人目前也尚屬於大二學生,曾經找到一篇關於計步器的學術論文,不過對於其內容中的演算法思想還沒有理解的能力。故麻煩知友能夠點化一下。附加論文信息:《步數檢測方法及在手腕式計步器中的應用研究》交通大學的謝如花女士的論文。望各位大牛賜教,小弟不勝感激
step detection其實很簡單,這篇paper中有講 FootPath: Accurate Map-based Indoor Navigation Using Smartphones(1)加速度取模,然後加一個 Low pass filter,窗口大小一般為300ms - 500ms,因為人的步頻大概就是這個量級(2) 檢測峰值,並判斷是否超過閾值,超過就是一步我用自己的手機採集了一些數據,親測可行。在Matlab下畫出計算結果,跟實際完全一致。
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;
}
搜索大法好~發明輪子哇哈哈。。。接觸到了一款APP,就算關閉狀態也能檢測計步器,隨便搜了一下,關鍵詞:TYPE_STEP_COUNTERhttp://developer.android.com/reference/android/hardware/Sensor.html#TYPE_STEP_COUNTER註冊了這個感測器之後,可以拿到用戶自重啟以來走過的步數
這個用手機還是比較困難的,網上已經有很多計步器應用,你可以體驗一下他們的準確度。
我認為手機感測器的精度不是問題,問題在於手機的位置,如果是綁在腿上那自然就簡單多了。很多與定位相關的論文會涉及到步伐檢測(用來實現慣性定位),我之前粗略閱讀過現在記不太清了,隱約記得有一篇講到一個雙峰+加速度檢測,即每一步都會出現一個雙峰再加上一定量的加速度。偶爾搜索到這個東西,怒答一下,下面鏈接是我之前寫的一篇文章,基本上中國互聯網上有教育意義的記步連接我都放裡面了。演算法在大多數情況下都比較准,唯一問題是,跑步的時候如果放在褲兜裡面手機來回晃動,不準確,如果用腰帶緊貼腰上則比較准。連接在下面。另外一種基於手機感測器簡單的記步演算法
根據自己開發遇到的問題,所謂的某些手機自帶計步器的說法保持懷疑!
如果使用繼承SensorEventListener 方式獲取步數,在某些手機上會出現極大地偏差。
包括魅族全系、華為部分機器。個人感覺是api中返回的數據錯誤導致不可用
手機上的計步軟體或多或少有些不準,這是加速度感測器實現演算法的通病。動動、咕咚這些計步軟體都不錯,偏向社交一點。如果要一款純粹的專註的計步軟體,有一款簡潔,小巧的——力成計步器,可以試試:應用詳情 - 應用寶官網
推薦閱讀:
※音樂隨機播放的演算法是怎樣的?可能做到產生一個和原來順序完全一樣的歌單嗎?如果有幾率是多少?
※C++ 如何實現平衡的名次樹?
※為什麼常見的O(n)時間的找凸多邊形內最大內接三角形的那個演算法是錯誤的?
※通過脈搏波PPG波形推導血壓(主要是SBP)有沒有可能性,結合其它例如年齡、體重等參數呢?
※演算法 第四版(algorithms 4th edition ) 這本書有配套的習題答案嗎?
TAG:演算法 | 感測器 | 應用程序Application | Android | 計步器 |