需要一种算法来检测振荡数据中的大峰值

Need an algorithm to detect large spikes in oscillating data

我正在微控制器上一次一大块地解析 SD 卡上的数据。它是加速度计数据,因此它不断振荡。在某些点上会发生巨大的振荡(如图所示)。我需要一种算法来检测这些大的振荡,更重要的是,确定包含此尖峰的数据范围。

我有一些示例数据:

这是整体图,感兴趣的只有一个尖峰,第一个。

这里放大了一点

如您所见,这是一个产生尖峰的大振荡。

因此,任何可以扫描数据集并确定包含相对于某个阈值的峰值的数据部分的算法都很棒。这个数据集大约有 50,000 个样本,每个样本的长度为 32 位。我有足够的 RAM 来保存这么多数据。

谢谢!

你可以看看统计分析。计算数据集的标准偏差,然后检查数据何时超出范围。

您可以选择以两种方式执行此操作;您可以对固定数量(相对较少)的样本使用 运行 平均值,也可以对整个数据集取平均值。当我在你的集合中看到多个尖峰时,我会建议第一个。这样您就可以在每次发现尖峰时停止处理(然后继续)。

为了您的目的,您实际上不需要计算标准差西格玛。你实际上可以把它留在西格玛的平方。这将为您提供轻微的性能优化,而无需计算平方根。

一些伪代码:

// The data set.
int x[N];   

// The number of samples in your mean and std calculation.
int M <= N;  

// Simga at index i over the previous M samples.
int sigma_i = sqrt( sum( pow(x[i] - mean(x,M), 2) ) / M );

// Or the squared of sigma 
int sigma_squared_i = sum( pow(x[i] - mean(x,M), 2) ) / M;

这种方法的缺点是需要为触发的sigma值设置一个阈值。然而,可以非常肯定地说,当将阈值设置为平均 sigma 的 4 或 5 倍时,您将拥有一个可用的系统。

设法得到一个工作算法。基本上,确定数据点之间的平均差异。如果我的数据开始连续超过该值的某个倍数,那么很可能会出现峰值。

对于以下信号:

如果取两个连续样本之间差值的绝对值,则得到:

这还不足以明确区分轻微的 "unsustained" 干扰。但是,如果您随后采用 abs-differentials 的简单 移动和 (漏积分器)。这里使用了 window 宽度 4 diff-samples:

移动平均线引入了滞后或相移,在存储数据和处理不是 real-time 的情况下,可以很容易地通过减去一半来补偿来自时间的 window 宽度:

对于 real-time 处理,如果滞后很严重,则更复杂的 IIR 滤波器可能是合适的。无论如何,可以从这些数据中 select 得出一个明确的阈值。

在上述数据集的代码中:

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

static int32_t dataset[] = { 0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
                             0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,3,0,0,0,0,0,
                             0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
                             0,-10,-15,-5,20,25,50,-10,-20,-30,0,30,5,-5,
                             0,0,5,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
                             0,0,0,0,0,0,0,0,0,1,0,0,0,0,6,0,0,0,0,0,0,0} ;

#define DATA_LEN (sizeof(dataset)/sizeof(*dataset))
#define WINDOW_WIDTH 4
#define THRESHOLD 15 
int main()
{
    uint32_t window[WINDOW_WIDTH] = {0} ;
    int window_index = 0 ;
    int window_sum = 0 ;
    bool spike = false ;

    for( int s = 1; s < DATA_LEN ; s++ )
    {
        uint32_t diff = abs( dataset[s] - dataset[s-1] ) ;
        window_sum -= window[window_index] ;
        window[window_index] = diff ;
        window_index++ ;
        window_index %= WINDOW_WIDTH ;
        window_sum += diff ;

        if( !spike && window_sum >= THRESHOLD )
        {
            spike = true ;
            printf( "Spike START @ %d\n", s - WINDOW_WIDTH / 2 ) ;
        }
        else if( spike && window_sum < THRESHOLD )
        {
            spike = false ;
            printf( "Spike END @   %d\n", s - WINDOW_WIDTH / 2 ) ;
        }
    }

    return 0;
}

输出为:

Spike START @ 66
Spike END @   82

https://onlinegdb.com/ryEw69jJH

将原始数据与检测阈值进行比较得出:

对于您的真实数据,您将需要 select 合适的 window 宽度和阈值以获得所需的结果,这两者都取决于您希望的扰动的带宽和幅度检测。

如果您的样本足够大,您可能还需要防止算术溢出。它们需要小于 232 / window-width 以保证积分器不会溢出。或者,您可以使用 floating-point 或 uint64_t 作为 window 类型,或者添加代码来处理 saturation.