如何在Android中使用加速计计算精确的脚步数?
我正在使用algorithmhttps://code.google.com/p/pedometer/开发一些像Runtastic计步器这样的应用程序,但是我没有得到结果之间的任何相似性。
我的代码如下:
public void onSensorChanged(SensorEvent event) { Sensor sensor = event.sensor; synchronized (this) { if (sensor.getType() == Sensor.TYPE_ORIENTATION) {} else { int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0; if (j == 1) { float vSum = 0; for (int i=0 ; i<3 ; i++) { final float v = mYOffset + event.values[i] * mScale[j]; vSum += v; } int k = 0; float v = vSum / 3; //Log.e("data", "data"+v); float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0)); if (direction == - mLastDirections[k]) { // Direction changed int extType = (direction > 0 ? 0 : 1); // minumum or maximum? mLastExtremes[extType][k] = mLastValues[k]; float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]); if (diff > mLimit) { boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3); boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3); boolean isNotContra = (mLastMatch != 1 - extType); if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) { for (StepListener stepListener : mStepListeners) { stepListener.onStep(); } mLastMatch = extType; } else { Log.i(TAG, "no step"); mLastMatch = -1; } } mLastDiff[k] = diff; } mLastDirections[k] = direction; mLastValues[k] = v; } } } }
用于登记传感器:
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); mSensor = mSensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER); mSensorManager.registerListener(mStepDetector,mSensor,SensorManager.SENSOR_DELAY_NORMAL);
在algorithm中,我有不同级别的灵敏度作为公共空白
setSensitivity(float sensitivity) { mLimit = sensitivity; // 1.97 2.96 4.44 6.66 10.00 15.00 22.50 33.75 50.62 }
在各种灵敏度水平上我的结果是:
sensitivity rantastic pedometer my app 10.00 3870 5500 11.00 3000 4000 11.15 3765 4576 13.00 2000 890 11.30 754 986
我没有得到任何适当的模式来配合的要求。 根据我的分析,这个应用程序使用Sensor.TYPE_MAGNETIC_FIELD进行步骤计算,请让我知道一些algorithm,以便我可以满足要求
https://github.com/bagilevi/android-pedometer
我希望这可能是有帮助的
你需要做的第一件事是决定一个algorithm。 据我所知,大致上讲有三种使用文献中描述的加速度计检测步骤的方法:
-
使用毕达哥拉斯定理来计算来自加速度计的每个样本的加速度vector的大小。 对幅度信号进行低通滤波以消除高频噪声,然后在滤波后的信号中查找峰值和谷值。 您可能需要添加额外的要求,以消除误报。 这是检测步骤的最简单的方法,也是大多数(如果不是所有的)普通计步器,你可以从运动商店购买。
-
使用(1)中的Pythagoras,然后通过FFT运行信号,并将FFT的输出与已知的步行输出进行比较。 这要求您有权访问相当多的培训数据。
-
将加速度计数据join到使用一些合适的机器学习技术(例如neural network或数字小波变换)的algorithm中。 这种方法当然可以包括其他传感器。 这也要求你有机会获得相当多的训练数据。
一旦你决定了一个algorithm,你可能会想使用像Matlab或SciPy这样的东西来testing你的algorithm在你的电脑上使用你在Android手机上制作的录音。 将加速度计数据转储到手机上的cvs文件中,logging文件所代表的步骤数,将文件复制到计算机上,然后在数据上运行algorithm,查看是否获得了正确的步数。 这样你可以检测到algorithm的问题并纠正它们。
如果这听起来很困难,那么获得良好的步进检测的最佳方式可能是等待更多的手机带有KitKat启用的内置步进计数器。
我正在步行仪器中使用步进检测。 我得到了很好的步骤检测结果。 我使用achartengine绘制加速计数据。 看看这里: https : //github.com/MichalDanielDobrzanski/WalkingSynth/blob/master/app/src/main/java/com/dobi/walkingsynth/accelerometer/AccelerometerProcessing.java我做什么:
- 加速度传感器的量值vector分析。
- 设置可更改的阈值级别。 当来自加速度计的信号超过它时,我把它算作一步。
- 在第一次超过阈值后设置非活动状态的时间(用于步进检测)。
第3点计算:
- 任意设定我们行走的最大速度 (如120bpm)
- 如果每步60bpm – 1000msec,则每步120bpm – 500msec
- 加速度计传递具有特定期望频率的数据(SENSOR_DELAY_NORMAL,SENSOR_DELAY_GAME等)。 当DELAY_GAME:T〜 = 20ms (包含在Android文档中)
- n – 样本省略(超过阈值后)
- n = 500msec / T
- n = 500/20 = 25 (其中有很多,你可以调整这个值)。
- 之后,门槛变得活跃 。
看看这个图片: 我的应用程序
这是我的认识。 这是约1.5-2年前写的。 我真的不记得我写的所有东西。 但它的工作。 这对我的需求很有帮助。
我知道这是真的很大的类(有些方法被删除),但可能会有所帮助。 如果没有,我只是删除这个答案…
public class StepDetector implements SensorEventListener { public static final int MAX_BUFFER_SIZE = 5; private static final int Y_DATA_COUNT = 4; private static final double MIN_GRAVITY = 2; private static final double MAX_GRAVITY = 1200; public void onSensorChanged(final SensorEvent sensorEvent) { final float[] values = sensorEvent.values; final Sensor sensor = sensorEvent.sensor; if (sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { magneticDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l)); } if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) { accelDetector(values, sensorEvent.timestamp / (500 * 10 ^ 6l)); } } private ArrayList<float[]> mAccelDataBuffer = new ArrayList<float[]>(); private ArrayList<Long> mMagneticFireData = new ArrayList<Long>(); private Long mLastStepTime = null; private ArrayList<Pair> mAccelFireData = new ArrayList<Pair>(); private void accelDetector(float[] detectedValues, long timeStamp) { float[] currentValues = new float[3]; for (int i = 0; i < currentValues.length; ++i) { currentValues[i] = detectedValues[i]; } mAccelDataBuffer.add(currentValues); if (mAccelDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE) { double avgGravity = 0; for (float[] values : mAccelDataBuffer) { avgGravity += Math.abs(Math.sqrt( values[0] * values[0] + values[1] * values[1] + values[2] * values[2]) - SensorManager.STANDARD_GRAVITY); } avgGravity /= mAccelDataBuffer.size(); if (avgGravity >= MIN_GRAVITY && avgGravity < MAX_GRAVITY) { mAccelFireData.add(new Pair(timeStamp, true)); } else { mAccelFireData.add(new Pair(timeStamp, false)); } if (mAccelFireData.size() >= Y_DATA_COUNT) { checkData(mAccelFireData, timeStamp); mAccelFireData.remove(0); } mAccelDataBuffer.clear(); } } private void checkData(ArrayList<Pair> accelFireData, long timeStamp) { boolean stepAlreadyDetected = false; Iterator<Pair> iterator = accelFireData.iterator(); while (iterator.hasNext() && !stepAlreadyDetected) { stepAlreadyDetected = iterator.next().first.equals(mLastStepTime); } if (!stepAlreadyDetected) { int firstPosition = Collections.binarySearch(mMagneticFireData, accelFireData.get(0).first); int secondPosition = Collections .binarySearch(mMagneticFireData, accelFireData.get(accelFireData.size() - 1).first - 1); if (firstPosition > 0 || secondPosition > 0 || firstPosition != secondPosition) { if (firstPosition < 0) { firstPosition = -firstPosition - 1; } if (firstPosition < mMagneticFireData.size() && firstPosition > 0) { mMagneticFireData = new ArrayList<Long>( mMagneticFireData.subList(firstPosition - 1, mMagneticFireData.size())); } iterator = accelFireData.iterator(); while (iterator.hasNext()) { if (iterator.next().second) { mLastStepTime = timeStamp; accelFireData.remove(accelFireData.size() - 1); accelFireData.add(new Pair(timeStamp, false)); onStep(); break; } } } } } private float mLastDirections; private float mLastValues; private float mLastExtremes[] = new float[2]; private Integer mLastType; private ArrayList<Float> mMagneticDataBuffer = new ArrayList<Float>(); private void magneticDetector(float[] values, long timeStamp) { mMagneticDataBuffer.add(values[2]); if (mMagneticDataBuffer.size() > StepDetector.MAX_BUFFER_SIZE) { float avg = 0; for (int i = 0; i < mMagneticDataBuffer.size(); ++i) { avg += mMagneticDataBuffer.get(i); } avg /= mMagneticDataBuffer.size(); float direction = (avg > mLastValues ? 1 : (avg < mLastValues ? -1 : 0)); if (direction == -mLastDirections) { // Direction changed int extType = (direction > 0 ? 0 : 1); // minumum or maximum? mLastExtremes[extType] = mLastValues; float diff = Math.abs(mLastExtremes[extType] - mLastExtremes[1 - extType]); if (diff > 8 && (null == mLastType || mLastType != extType)) { mLastType = extType; mMagneticFireData.add(timeStamp); } } mLastDirections = direction; mLastValues = avg; mMagneticDataBuffer.clear(); } } public static class Pair implements Serializable { Long first; boolean second; public Pair(long first, boolean second) { this.first = first; this.second = second; } @Override public boolean equals(Object o) { if (o instanceof Pair) { return first.equals(((Pair) o).first); } return false; } } }
我发现你的实现和grepcode项目中的代码之间的一个主要区别就是注册监听器的方式。
你的代码:
mSensorManager.registerListener(mStepDetector, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
他们的代码:
mSensorManager.registerListener(mStepDetector, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
这是一个很大的区别。 SENSOR_DELAY_NORMAL
用于方向更改,因此速度并不快(从来没有注意到在旋转设备和设备实际旋转之间需要一些时间?这是因为这是一些function,不需要超快速甚至可能会非常烦人),你得到更新的速度不是那么高)。
另一方面, SENSOR_DELAY_FASTEST
适用于像计步器这样的事情:您希望传感器数据尽可能快速且经常,因此您的计算步骤将尽可能准确。
尝试切换到SENSOR_DELAY_FASTEST
速率,并再次testing! 这应该是一个很大的区别。