我参与的学校的创新工程项目有一个计步器的需求,由于未知原因,前两天模仿着写的程序始终搞不定。。于是干脆自己写一个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>