【问题标题】:Smoothing data from a sensor平滑来自传感器的数据
【发布时间】:2011-06-04 10:32:43
【问题描述】:

我有一个测量 v(x,y,z) 数据的 3D 传感器。我只使用 x 和 y 数据。仅平滑 x 和 y 就足够了。

如果我使用日志来显示数据,它会显示如下内容: (时间) 0.1 ... (数据记录) x = 1.1234566667 (时间) 0.2 ... (数据记录) x = 1.1245655666 (时间) 0.3 ... (数据记录) x = 1.2344445555

实际上数据更准确,但我想在 1.1234 值和 1.2344 值之间进行平滑处理,因为对我来说它是相同的,我可以使用整数,仅显示“x=1”但我需要小数那么,我也需要在这里显示一种“平滑”的值。

有人知道吗?我正在使用 c# 编程,但并非所有功能都可以正常工作,因此我需要构建自己的功能。

【问题讨论】:

    标签: algorithm sensors smoothing


    【解决方案1】:

    最简单的方法是对数据进行移动平均。也就是说,保持一系列传感器数据读数并对其进行平均。像这样的东西(伪代码):

      data_X = [0,0,0,0,0];
    
      function read_X () {
          data_X.delete_first_element();
          data_X.push(get_sensor_data_X());
          return average(data_X);
       }
    

    这样做需要权衡取舍。您使用的数组越大,结果越平滑,但结果与实际读数之间的滞后越大。例如:

                               /\_/\
                            /\/     \_/\
      Sensor reading:  __/\/            \/\
                                           \/\  _/\___________
                                              \/
                                  _
                               __/ \_
                           ___/      \__
      Small array:     ___/             \_/\_       _
                                             \   __/ \________
                                              \_/
    
                                     ____
                                  __/    \__
                               __/           \__
      Large array:     _______/                 \__      __
                                                   \_   /  \__
                                                     \_/
    
    
    (forgive my ASCII-ART but I'm hoping it's good enough for illustration).
    

    如果您想要快速响应但无论如何要获得良好的平滑效果,那么您将使用数组的加权平均值。这基本上是数字信号处理(带有大写的 DSP),与其名称相反,它与模拟设计更密切相关。这是一篇关于它的简短维基百科文章(如果你想走这条路,你应该阅读好的外部链接):http://en.wikipedia.org/wiki/Digital_filter

    以下是来自 SO 的一些关于低通滤波器的代码,可能适合您的需求:Low pass filter software?。请注意,在该答案的代码中,他使用了一个大小为 4 的数组(或信号处理术语中的 4 阶,因为此类滤波器称为四阶滤波器,它实际上可以通过四阶多项式方程建模:ax^ 4 + bx^3 + cx^2 + dx).

    【讨论】:

    • 非常感谢!!!这真的帮助了我,我现在正在实现代码,从现在开始我会帮助像你这样的人,好吧,如果我能的话。谢谢!!
    • 移动平均滤波器是低通滤波器的一个特例,它是一个非常糟糕的滤波器(就性能而言)。一阶低通滤波器在频率响应、计算负载和程序复杂性方面通常(通常?)优于移动平均值。对于许多应用程序,您可以忽略这些细节,例如可以缓慢响应的指南针显示,移动平均线会很棒。如果您的游戏需要使用嘈杂的传感器进行快速响应,那么移动平均线将不是一个糟糕的解决方案,因为它会导致给定数量的过滤产生滞后。
    • 由 ASCII 艺术制作的绝妙答案
    • 与此相关的另一个问题,这是我根据您的伪代码创建的 Java+Android 代码:stackoverflow.com/a/24600534/663058
    【解决方案2】:

    所以我来这里是为了解决同样的问题(Android 中的传感器输入平滑),这就是我想出的:

    /*
     * time smoothing constant for low-pass filter
     * 0 ≤ α ≤ 1 ; a smaller value basically means more smoothing
     * See: http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization
     */
    static final float ALPHA = 0.2f;
    
    protected float[] accelVals;
    
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
            accelVals = lowPass( event.values, accelVals );
    
        // use smoothed accelVals here; see this link for a simple compass example:
        // http://www.codingforandroid.com/2011/01/using-orientation-sensors-simple.html
    }
    
    /**
     * @see http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
     * @see http://en.wikipedia.org/wiki/Low-pass_filter#Simple_infinite_impulse_response_filter
     */
    protected float[] lowPass( float[] input, float[] output ) {
        if ( output == null ) return input;
    
        for ( int i=0; i<input.length; i++ ) {
            output[i] = output[i] + ALPHA * (input[i] - output[i]);
        }
        return output;
    }
    

    感谢@slebetman 将我指向 Wikipedia 链接,经过一番阅读后,该链接将我吸引到了 wikipedia Low-pass filter 文章中的算法。我不会发誓我拥有最好的算法(甚至是正确的!),但轶事证据似乎表明它正在发挥作用。

    【讨论】:

    • 只是想说,如果你在 android 上使用这段代码,最初返回 input.copy() 而不是数组本身。我的传感器写入同一个数组,即输入和输出是同一个数组,平滑不起作用。
    【解决方案3】:

    嗯,有很多方法可以平滑传感器数据,这取决于它是什么类型的传感器以及适合什么类比。 我在我的项目中使用了这些算法:

    1. 高通滤波器 [HPF] 和低通滤波器 [LPF]- 如所选答案所示。
    2. 移动平均算法-MAA
    3. Gaely 算法mm[MAA 的更好版本]
    4. 快速傅里叶变换 -FFT

    代码:

    HPF-高通滤波器

    private float[] highPass(float x, float y, float z) {
        float[] filteredValues = new float[3];
        gravity[0] = ALPHA * gravity[0] + (1 – ALPHA) * x;
        gravity[1] = ALPHA * gravity[1] + (1 – ALPHA) * y;
        gravity[2] = ALPHA * gravity[2] + (1 – ALPHA) * z;
        filteredValues[0] = x – gravity[0];
        filteredValues[1] = y – gravity[1];
        filteredValues[2] = z – gravity[2];
        return filteredValues;   
        }
    

    LPF-低通滤波器

    private float[] lowPass(float x, float y, float z) {
        float[] filteredValues = new float[3];
        filteredValues[0] = x * a + filteredValues[0] * (1.0f – a);
        filteredValues[1] = y * a + filteredValues[1] * (1.0f – a);
        filteredValues[2] = z * a + filteredValues[2] * (1.0f – a);
        return filteredValues;
        }
    

    MAA-移动平均线

         private final int SMOOTH_FACTOR_MAA = 2;//increase for better results   but hits cpu bad
    
         public ArrayList<Float> processWithMovingAverageGravity(ArrayList<Float> list, ArrayList<Float> gList) {
                int listSize = list.size();//input list
                int iterations = listSize / SMOOTH_FACTOR_MAA;
                if (!AppUtility.isNullOrEmpty(gList)) {
                    gList.clear();
                }
                for (int i = 0, node = 0; i < iterations; i++) {
                    float num = 0;
                    for (int k = node; k < node + SMOOTH_FACTOR_MAA; k++) {
                        num = num + list.get(k);
                    }
                    node = node + SMOOTH_FACTOR_MAA;
                    num = num / SMOOTH_FACTOR_MAA;
                    gList.add(num);//out put list
                }
                return gList;
            }
    

    【讨论】:

    • 什么是盖利算法?
    • @Jayant 获取加速度传感器数据,我们应该使用高通滤波器还是低通滤波器?
    • 经过一些读数后,我最终选择了低通滤波器来平滑加速度传感器数据中的尖峰。
    • 顺便说一句,这是一个很好的答案,值得更多的投票。
    【解决方案4】:

    这是一个基于 iOS 版 Event Handling guide 的 MotionEvents 部分中的逻辑的示例。

    float ALPHA = 0.1;
    
    protected float[] lowPass( float[] input, float[] output ) {
        if ( output == null ) return input;
    
        for ( int i=0; i<input.length; i++ ) {
            output[i] = (input[i] * ALPHA) + (ouptut[i] * (1.0 - ALPHA));
        }
        return output;
    }
    

    【讨论】:

    • 指南的url内容已更改,找不到MotionEvents部分,你能找到吗?
    【解决方案5】:

    @thom_nic 的低通滤波器代码中有一个很小但非常重要的错字,这种实现的结果有很大不同。

    protected float[] lowPass( float[] input, float[] output ) {
        if ( output == null ) return input;
    
        for ( int i=0; i<input.length; i++ ) {
            output[i] = output[i] + ALPHA * (input[i] - output[i]); // ERROR HERE
        }
        return output;
    }
    

    根据我的数据,这段代码的结果(橙色线)将是:

    你可以在这里找到正确的代码wikipedia Low-pass_filter,C 代码是这样的:

    protected float[] lowPass( float[] input, float[] output ) {
        if ( output == null ) return input;
    
        output[0] = input[0];
        for (int i=1; i<input.length; i++) {
            output[i] = output[i-1] + ALPHA * (input[i] - output[i-1]);
        }
        return output;
    }
    

    请注意替换的索引 (i -> i-1)。

    在相同的数据和相同的 ALPHA 上,此代码生成:

    【讨论】:

    • 不错,但并不完全正确。在 wiki 公式的情况下,output[i-1] 代表输出数组中包含测量历史的前一个值,但@thom_nic 答案中的lowPass() 函数将过滤器应用于单个测量的所有指标。您不能只在同一个公式中使用这些指标。所以@thom_nic 的函数是正确的。
    【解决方案6】:

    在这里挖掘一个老问题,但如果你在 .NET 领域,你可以使用 RX 为你做这件事。

    例如,将 RX 与 WebClient.DownloadFileAsync 结合使用来计算“平滑”的下载速度:

    double interval = 2.0; // 2 seconds
    long bytesReceivedSplit = 0;
    
    WebClient wc = new WebClient();
    var downloadProgress = Observable.FromEventPattern<
        DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
        h => wc.DownloadProgressChanged += h,
        h => wc.DownloadProgressChanged -= h)
        .Select(x => x.EventArgs);
    
    downloadProgress.Sample(TimeSpan.FromSeconds(interval)).Subscribe(x =>
        {
            Console.WriteLine((x.BytesReceived - bytesReceivedSplit) / interval);
            bytesReceivedSplit = x.BytesReceived;
        });
    
    Uri source = new Uri("http://someaddress.com/somefile.zip");
    wc.DownloadFileAsync(source, @"C:\temp\somefile.zip");
    

    显然,间隔越长,平滑度就越大,但您等待初始读数的时间也就越长。

    【讨论】:

      猜你喜欢
      • 2022-01-17
      • 1970-01-01
      • 2019-02-10
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多