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

时间:2019-06-20 18:39:04

标签: c algorithm embedded signal-processing

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

我有一些示例数据:

Overall graph 这是总体图,只有一个兴趣峰值,第一个。

zoomed in 在这里它放大了一点

oscillation 如您所见,是产生尖峰的大振荡。

因此,任何能够扫描数据集并确定包含相对于某个阈值的峰值的数据部分的算法都将非常出色。该数据集大约有50,000个样本,每个样本为32位长。我有足够的RAM来容纳这么多数据。

谢谢!

3 个答案:

答案 0 :(得分:3)

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

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

出于您的目的,您实际上不需要计算标准偏差sigma。您实际上可以将其保留为sigma的平方。这将为您提供轻微的性能优化,而不必计算平方根。

一些伪代码:

// 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倍时,您将拥有一个可用的系统。

答案 1 :(得分:3)

对于以下信号:

enter image description here

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

enter image description here

这还不足以明确地区分轻微的“不可持续”干扰。但是,如果您随后对abs-微分进行简单的移动总和(泄漏积分器)。这里使用了4个差异样本的窗口宽度:

enter image description here

移动平均会引入滞后或相移,在存储数据且处理不是实时的情况下,可以通过从时序中减去窗口宽度的一半来轻松补偿:

enter image description here

对于实时处理,如果延迟很关键,则可能需要更复杂的IIR滤波器。无论如何,可以从该数据中选择一个明确的阈值。

在上述数据集的代码中:

#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

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

enter image description here

对于实际数据,您需要选择合适的窗口宽度和阈值以获得所需的结果,这两者都取决于要检测的干扰的带宽和幅度。

如果样本量足够大,您可能还需要防止算术溢出。它们必须小于2 32 / window-width ,以确保积分器中没有溢出。另外,您可以对uint64_t类型使用浮点数或window,或添加代码来处理 saturation

答案 2 :(得分:0)

设法获得有效的算法。基本上,确定数据点之间的平均差异。如果我的数据开始连续超过该值的某个倍数,则很可能发生了峰值。