1. 程式人生 > >android筆記,山寨版搖動判斷演算法的設計過程。

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>