android筆記,山寨版搖動判斷演算法的設計過程。
我參與的學校的創新工程專案有一個計步器的需求,由於未知原因,前兩天模仿著寫的程式始終搞不定。。於是乾脆自己寫一個Demo來看看效果,也是多學學,加深印象。
計步器的核心就是根據重力感測器的xyz三軸測得的數值來判定使用者是不是走了一步。
當手機向某個方向加速時,三軸對應的讀數也會增加(或減少,如果是反方向加速的話)。
粗略點的話,可以分別把三軸數值的絕對值相加,得到粗略的加速度,超過一定的闕值之後就可以判定為跑了一步(震了一下)。對於加速方向,則可以無視,因為計步器沒這個需求。
還有個精確些的想法:由於三軸中任意兩軸呈直角,可以用算斜邊的公(x平方加y平方再開方)先將xy軸的合力算出來,再把合力與z軸如法炮製一遍,就可以得到三軸的合力。這個合力的好處就是手機無論是怎樣放置的,它的讀數都一致,可以得到更真實的加速度,這正是我要的。
然後我實機測試了一下,發現只搖一下,讀數確快速增加了很多。恩。。。即使是效能較差的手機,計算速度也是很快的,一次判斷晃動後要等一下子才能再次進行判斷才行。
再試一次,可以了,效果不錯。但是還有個問題。。:如果有條件可以持續提供超出闕值的加速度(比如超級跑車?呵呵),這種演算法的讀數不也會持續增加麼。。
當然正常情況下這種bug不會出現。。因為如果能持續維持住瞬間搖動時的加速度的話,我們的手機抽根菸工夫就能快過子彈了。。(粗略估計。勿較真。。)不過這倒提供了另一個思路:對搖動的判斷條件應該是在一定時間內完成往復加速才完整。
過過吐槽癮:
對我這個專案肯定是用不上了。。不過這倒是個不錯的練習題,也許還可以加上加速方向的判定,不過這東西貌似只有在3d遊戲中才有用啊。。而3d遊戲。。人家有物理引擎。。。。。。殘念。。索愛那樣用在音樂播放器上?向右摔是下一首,左是上一首,然後就一群人天天甩來甩去。。。線控明明很方便的說。。甩動換歌真心是個腦殘主意。。。還要先把手機拿在手上。那樣直接按鍵不是更好。。。
忘上程式碼了。。
PedometerDemoActivity.java:
package com.gaga.PedometerDemo; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.util.Log; import android.widget.RelativeLayout; import android.widget.TextView; public class PedometerDemoActivity extends Activity { private String TAG = "My Pedometer"; private TextView mStepCounter; private TextView mx; private TextView my; private TextView mz; private int steps; private SensorManager mSensorManager; private Sensor mSensor; private StepDetector mStepDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mStepCounter = (TextView)findViewById(R.id.txt_StepsCount); mx = (TextView)findViewById(R.id.x); my = (TextView)findViewById(R.id.y); mz = (TextView)findViewById(R.id.z); steps = 0; mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); registerStepListener(); } private void registerStepListener() { if (mStepDetector == null) { mStepDetector = new StepDetector(); } mSensorManager.registerListener(mStepDetector, mSensor, SensorManager.SENSOR_DELAY_FASTEST); } @Override protected void onPause() { Log.d(TAG, "onPause"); super.onPause(); mSensorManager.unregisterListener(mStepDetector); } @Override protected void onResume() { registerStepListener(); super.onResume(); } private class StepDetector implements SensorEventListener { @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { updateImage(event.values[0], event.values[1], event.values[2]); isStep(event.values); } } private void updateImage(float x,float y, float z) { RelativeLayout.LayoutParams mxLayoutParams = (RelativeLayout.LayoutParams)mx.getLayoutParams(); RelativeLayout.LayoutParams myLayoutParams = (RelativeLayout.LayoutParams)my.getLayoutParams(); RelativeLayout.LayoutParams mzLayoutParams = (RelativeLayout.LayoutParams)mz.getLayoutParams(); mxLayoutParams.height = (int)x*10+50; myLayoutParams.height = (int)y*10+50; mzLayoutParams.height = (int)z*10+50; mx.setLayoutParams(mxLayoutParams); my.setLayoutParams(myLayoutParams); mz.setLayoutParams(mzLayoutParams); } private float offset = 50; //隨便設的闕值 private long lastStepTime = 0; private void isStep(float[] values) { float value = Math.abs(values[0])+Math.abs(values[1])+Math.abs(values[2]); if (value > offset) { //判定為一步後要等一下,不然讀數會猛跳。 long curTime = System.currentTimeMillis(); if ((curTime - lastStepTime) > 200) { increaseAndSetSteps(); lastStepTime = curTime; } } } private void increaseAndSetSteps() { steps ++; mStepCounter.setText(String.valueOf(steps)); } } }
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:background="#fff">
<TextView
android:id="@+id/StepsHere"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Steps Here!"
android:textColor="#232725"
android:shadowColor="#666"
android:shadowRadius="0.5"
android:textSize="40dp"
android:shadowDx="1.0"
android:shadowDy="1.0"/>
<TextView
android:id="@+id/x"
android:layout_width="50px"
android:layout_height="20px"
android:layout_above="@+id/StepsHere"
android:layout_alignLeft="@+id/StepsHere"
android:text=""
android:background="#18270a" />
<TextView
android:id="@+id/y"
android:layout_width="50px"
android:layout_height="20px"
android:layout_above="@+id/StepsHere"
android:layout_toRightOf="@+id/x"
android:background="#3b7306"
android:text=""/>
<TextView
android:id="@+id/z"
android:layout_width="50px"
android:layout_height="20px"
android:layout_above="@+id/StepsHere"
android:layout_toRightOf="@+id/y"
android:background="#488c03"
android:text=""/>
<TextView
android:id="@+id/txt_StepsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/StepsHere"
android:layout_below="@+id/StepsHere"
android:shadowColor="#666"
android:shadowDx="1.0"
android:shadowDy="1.0"
android:shadowRadius="0.5"
android:text="0"
android:textColor="#cbd9b8"
android:textSize="190dp" />
</RelativeLayout>