如何使用android中的加速度计计算精确的步数?

时间:2013-12-02 09:02:25

标签: android algorithm accelerometer

我正在使用algorithm开发一些像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);

在算法中我有不同的敏感度作为公共空白

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进行步骤计算,请让我知道一些算法,以便我能满足要求。

6 个答案:

答案 0 :(得分:18)

您需要做的第一件事是决定算法。据我所知,大致上有三种方法可以使用文献中描述的加速度计来检测步骤:

  1. 使用毕达哥拉斯定理计算加速度计中每个样本的加速度矢量的大小。对幅度信号进行低通滤波以消除高频噪声,然后在滤波后的信号中寻找峰值和谷值。您可能需要添加其他要求以消除误报。这是迄今为止检测步骤最简单的方法,它也是大多数(如果不是所有)普通计步器可以从体育用品商店购买的方式。

  2. 在(1)中使用Pythagoras,然后通过FFT运行信号,并将FFT的输出与已知的步行输出进行比较。这要求您可以访问大量的培训数据。

  3. 将加速度计数据输入到使用某种合适的机器学习技术的算法中,例如神经网络或数字小波变换。当然,您可以在此方法中包含其他传感器。这也要求您可以访问相当多的培训数据。

  4. 一旦你决定使用算法,你可能会想要使用像Matlab或SciPy这样的东西来测试你的计算机上的算法,使用你在Android手机上制作的录音。将加速度计数据转储到手机上的cvs文件,记录文件所代表的步数,将文件复制到计算机并对数据运行算法以查看它是否正确计数。这样,您就可以检测算法的问题并进行纠正。

    如果这听起来很难,那么获得良好步骤检测的最佳方法可能是等到更多手机配备KitKat启用的内置步进计数器。

答案 1 :(得分:17)

https://github.com/bagilevi/android-pedometer

我希望这可能会有所帮助

答案 2 :(得分:5)

我在走路仪器中使用步进检测。 我得到了很好的步检测结果。 我用 achartengine 绘制加速度计数据。 看看here。 我做了什么:

  1. 加速计传感器的幅度矢量分析。
  2. 设置可更改阈值级别。当来自加速度计的信号高于它时,我将其视为一个步骤。
  3. 第一次越过阈值后设置非活动状态(步骤检测)的时间。
  4. 计算第3点:

    • 任意设置我们行走的最大速度(例如120bpm)
    • 如果60bpm - 每步1000msec,那么 120bpm - 500msec 每步
    • 加速计传递具有特定所需频率的数据(SENSOR_DELAY_NORMAL,SENSOR_DELAY_GAME等)。当DELAY_GAME: T~ = 20ms 时(这包含在Android文档中)
    • n - 省略的样本(通过阈值后)
    • n = 500毫秒/ T
    • n = 500/20 = 25 (很多。您可以调整此值。)
    • 之后,阈值变为活动

    看看这张照片: My application

答案 3 :(得分:2)

我在你的实现和grepcode项目中的代码之间发现的一个主要区别是你注册监听器的方式。

您的代码:

mSensorManager.registerListener(mStepDetector,
                                mSensor,
                                SensorManager.SENSOR_DELAY_NORMAL);

他们的代码:

mSensorManager.registerListener(mStepDetector,
                                mSensor,
                                SensorManager.SENSOR_DELAY_FASTEST);

这是一个很大的不同。 SENSOR_DELAY_NORMAL用于方向更改,因此不是那么快(曾经注意到旋转设备和设备实际旋转之间需要一些时间?这是因为这是一些不需要超级的功能快(这可能会非常烦人)。你获得更新的速度并不高。

另一方面,SENSOR_DELAY_FASTEST适用于计步器等事项:您希望传感器数据尽可能快且经常,因此您的步骤计算将尽可能准确。

尝试切换到SENSOR_DELAY_FASTEST费率,然后再次测试!它应该有很大的不同。

答案 4 :(得分:2)

这是我的意识。它写于大约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;
        }
    }
}

答案 5 :(得分:0)

 public void onSensorChanged(SensorEvent event) {
   if (event.sensor.getType()==Sensor.TYPE_ACCELEROMETER ){
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];

            currentvectorSum = (x*x + y*y + z*z);
            if(currentvectorSum < 100 && inStep==false){
                inStep = true;
            }
            if(currentvectorSum > 125 && inStep==true){
                inStep = false;
                numSteps++;
                Log.d("TAG_ACCELEROMETER", "\t" + numSteps);
            }
        }
}