检测手机加速度计的周期性数据

时间:2011-11-04 08:14:14

标签: android algorithm signal-processing accelerometer

我正在开发Android应用程序,我需要检测用户上下文(如果步行或以最小的方式行驶)

我正在使用加速度计和所有轴的总和来检测加速度矢量。它在行走时能看到一些周期性值的方式非常好。但我需要以编程方式检测这些poeriods。

请问是否有任何类型的数学函数来检测值集的周期?我听说傅里叶变换可用于此,但我真的不知道如何实现它。看起来很复杂:)

请帮忙

3 个答案:

答案 0 :(得分:8)

检测数据周期性的最简单方法是autocorrelation。这也很容易实现。要在i处获得自相关,您只需将数据的每个数据点与每个数据点相乘i即可。这是一些伪代码:

for i = 0 to length( data ) do
  autocorrel[ i ] = 0;
  for j = 0 to length( data ) do
     autocorrel[ i ] += data( j ) * data( ( j + i ) mod length( data ) )
  done
done

这将为您提供一系列值。最高的“周期性”在具有高值的索引处。通过这种方式,您可以提取任何周期性部分(通常不止一个)。

另外我建议你不要在应用程序中尝试实现自己的FFT。虽然这个算法非常适合学习,但是很多人可以做错,很难测试,而且你的实现也可能比现有的慢得多。如果你的系统有可能,我会建议你使用FFTW,这在任何方面都是不可能的,当谈到FFT实现时。

修改

解释,为什么它甚至可以在不重复的值上工作:

计算自相关的通常和完全正确的方法是从数据中减去均值。假设你有[1, 2, 1.2, 1.8 ]。然后你可以从每个样本中提取1.5,留下[-.5, .5, -.3, .3 ]。现在,如果你将其与零自身相乘,则负数将乘以负数,正数乘以正数,产生(-.5)^2 + (.5)^2 + (-.3)^2 + (.3)^2=.68。在一个负数的偏移量将乘以产生(-.5)*(.5) + (.5)*(-.3) + (-.3)*(.3) + (.3)*(-.5)=-.64的正数。在偏移量为2时,负数将乘以负数,正数乘以正数。在偏移三个类似于一个偏移的情况再次发生。如您所见,在偏移0和2(周期)和负值1和4时得到正值。

现在只检测周期,不必减去均值。如果您按原样保留样品,则每次添加时都会添加suqared mean。由于将为每个计算的系数添加相同的值,因此比较将产生与您首先减去平均值相同的结果。在最坏的情况下,您的数据类型可能会运行(如果您使用某种类型的整数类型),或者您可能会在值开始变大时解决错误(如果您使用float,通常这不是问题)。如果发生这种情况,首先减去均值并尝试结果是否会好转。

使用自相关与某种快速傅立叶变换的最大缺点是速度。自动关联需要O(n^2),因为FFT仅需O(n log(n))。如果您需要经常计算非常长的序列的周期,则在您的情况下自动关联可能不起作用。

如果你想知道傅立叶变换是如何工作的,以及关于真实部分和虚部,幅度和相位的所有这些东西(看看Manu发布的代码)意味着,我建议你有一个看看this book

<强> EDIT2:

在大多数情况下,数据既不是完全周期性的,也不是完全混沌和非周期性的。通常,您的数据将由几个不同强度的周期性组成。句点是一个时差,您可以通过它来移动数据以使其与自身相似。如果将数据移动一定量,则自相关计算数据的相似程度。因此,它为您提供所有可能时期的力量。这意味着,没有“重复值索引”,因为当数据完全周期性时,所有索引都将重复。具有最强值的索引为您提供数据与自身最相似的移位。因此,此索引提供时间偏移,而不是数据的索引。为了理解这一点,重要的是要理解时间序列如何被认为是由完美周期函数(正弦基函数)的总和组成。

如果您需要在很长时间内检测到这一点,通常最好将窗口滑过数据,然后检查这个较小数据帧的周期。但是,您必须注意,您的窗口会为您的数据添加额外的时间段,您必须注意这些时间段。

我在上次编辑中发布的链接中有更多内容。

答案 1 :(得分:7)

还有一种方法可以使用FFT计算数据的自相关性,从而将复杂度从O(n^2)降低到O(n log n)。基本思路是采用周期性采样数据,使用FFT对其进行变换,然后通过将每个FFT系数乘以其复共轭来计算功率谱,然后采用功率谱的逆FFT。您可以找到预先存在的代码来毫不费力地计算功率谱。例如,查看Moonblink android library。该库包含FFTPACK(一个很好的FFT库)的JAVA转换,它还有一些用于计算功率谱的DSP类。我成功使用的自相关方法是McLeod Pitch Method(MPM),其java源代码可用here。我在类McLeodPitchMethod中编辑了一个方法,允许它使用FFT优化的自相关算法计算音高:

private void normalizedSquareDifference(final double[] data) {
    int n = data.length;
    // zero-pad the data so we get a number of autocorrelation function (acf)
    // coefficients equal to the window size
    double[] fft = new double[2*n];
        for(int k=0; k < n; k++){
        fft[k] = data[k];
    }
    transformer.ft(fft);
    // the output of fft is 2n, symmetric complex
    // multiply first n outputs by their complex conjugates
    // to compute the power spectrum
    double[] acf = new double[n];
    acf[0] = fft[0]*fft[0]/(2*n);
    for(int k=1; k <= n-1; k++){
        acf[k] = (fft[2*k-1]*fft[2*k-1] + fft[2*k]*fft[2*k])/(2*n);
    }
    // inverse transform
    transformerEven.bt(acf);
    // the output of the ifft is symmetric real
    // first n coefficients are positive lag acf coefficients
    // now acf contains acf coefficients
    double[] divisorM = new double[n];
    for (int tau = 0; tau < n; tau++) {
        // subtract the first and last squared values from the previous divisor to get the new one;
        double m = tau == 0 ? 2*acf[0] : divisorM[tau-1] - data[n-tau]*data[n-tau] - data[tau-1]*data[tau-1];
        divisorM[tau] = m;
        nsdf[tau] = 2*acf[tau]/m;
    }
}

其中transformer是来自java FFTPACK转换的FFTTransformer类的私有实例,transformerEvenFFTTransformer_Even类的私有实例。 使用您的数据调用McLeodPitchMethod.getPitch()可以非常有效地估算频率。

答案 2 :(得分:0)

以下是使用libgdx:

中的FFT类计算傅立叶变换android的示例
    package com.spec.example;
import android.app.Activity;
import android.os.Bundle;
import com.badlogic.gdx.audio.analysis.FFT;
import java.lang.String;
import android.util.FloatMath;
import android.widget.TextView;

public class spectrogram extends Activity {
    /** Called when the activity is first created. */
    float[] array = {1, 6, 1, 4, 5, 0, 8, 7, 8, 6, 1,0, 5 ,6, 1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    float[] array_hat,res=new float[array.length/2];
    float[] fft_cpx,tmpr,tmpi;
    float[] mod_spec =new float[array.length/2];
    float[] real_mod = new float[array.length];
    float[] imag_mod = new float[array.length];
    double[] real = new double[array.length];
    double[] imag= new double[array.length];
    double[] mag = new double[array.length];
    double[] phase = new double[array.length];
    int n;
    float tmp_val;
    String strings;
    FFT fft = new FFT(32, 8000);
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);

       fft.forward(array);
       fft_cpx=fft.getSpectrum();
       tmpi = fft.getImaginaryPart();
       tmpr = fft.getRealPart();
      for(int i=0;i<array.length;i++)
       {
           real[i] = (double) tmpr[i];
           imag[i] = (double) tmpi[i];
           mag[i] = Math.sqrt((real[i]*real[i]) + (imag[i]*imag[i]));
           phase[i]=Math.atan2(imag[i],real[i]);

           /****Reconstruction****/        
           real_mod[i] = (float) (mag[i] * Math.cos(phase[i]));
           imag_mod[i] = (float) (mag[i] * Math.sin(phase[i]));

       }

       fft.inverse(real_mod,imag_mod,res);
   }
}

此处有更多信息:http://www.digiphd.com/android-java-reconstruction-fast-fourier-transform-real-signal-libgdx-fft/