C 需要一种算法来检测振荡数据中的大峰值吗
我在微控制器上通过sd卡上的数据一次解析一大块数据。它是加速度计数据,因此它不断振荡。在图中所示的某些点上会发生巨大的振荡。我需要一个算法来检测这些大的振荡,更重要的是,确定包含这个峰值的数据范围 我有一些示例数据: 这是整个图表,只有一个感兴趣的尖峰,第一个 这里放大了一点 正如你所看到的,是一个大的振荡产生了尖峰 因此,任何能够扫描数据集并确定包含与某个阈值相关的尖峰的数据部分的算法都是非常棒的。这个数据集大约有50000个样本,每个样本有32位长。我有足够的内存来保存这么多的数据C 需要一种算法来检测振荡数据中的大峰值吗,c,algorithm,embedded,signal-processing,C,Algorithm,Embedded,Signal Processing,我在微控制器上通过sd卡上的数据一次解析一大块数据。它是加速度计数据,因此它不断振荡。在图中所示的某些点上会发生巨大的振荡。我需要一个算法来检测这些大的振荡,更重要的是,确定包含这个峰值的数据范围 我有一些示例数据: 这是整个图表,只有一个感兴趣的尖峰,第一个 这里放大了一点 正如你所看到的,是一个大的振荡产生了尖峰 因此,任何能够扫描数据集并确定包含与某个阈值相关的尖峰的数据部分的算法都是非常棒的。这个数据集大约有50000个样本,每个样本有32位长。我有足够的内存来保存这么多的数据 谢
谢谢 你可以看看统计分析。计算数据集的标准偏差,然后在数据超出范围时进行检查 你可以选择用两种方式来做这件事;您可以在固定的相对较少的样本数上使用运行平均值,也可以在整个数据集上取平均值。当我看到你的组合中有多个尖峰时,我建议你选择第一个。通过这种方式,您可能会停止处理,然后在每次发现峰值时继续处理 出于您的目的,您实际上不需要计算标准偏差σ。你可以把它放在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值设置一个阈值。但是,可以肯定地说,当将阈值设置为平均西格玛的4或5倍时,您将拥有一个可用的系统。您可以查看统计分析。计算数据集的标准偏差,然后在数据超出范围时进行检查 你可以选择用两种方式来做这件事;您可以在固定的相对较少的样本数上使用运行平均值,也可以在整个数据集上取平均值。当我看到你的组合中有多个尖峰时,我建议你选择第一个。通过这种方式,您可能会停止处理,然后在每次发现峰值时继续处理 出于您的目的,您实际上不需要计算标准偏差σ。你可以把它放在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值设置一个阈值。但是,可以肯定地说,当将阈值设置为平均西格玛的4或5倍时,您将拥有一个可用的系统。成功地获得了一个有效的算法。基本上,确定数据点之间的平均差异。如果我的数据开始连续超过该值的某个倍数,则极有可能出现峰值。设法得到一个有效的算法。基本上,确定数据点之间的平均差异。如果我的数据开始连续超过该值的某个倍数,则极有可能出现峰值。对于以下信号: 如果取两个连续样本之间差值的绝对值,则得到: 这还不够好,无法明确区分那些未持续的小干扰。但若你们用一个简单的移动和,一个abs微分的漏积分。此处使用了4个不同样本的窗口宽度: 移动平均引入了滞后或相移,在数据存储且处理不实时的情况下,可通过从定时中减去一半窗口宽度来轻松补偿: 对于实时处理,如果滞后非常关键,则可能需要更复杂的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;
}
将原始数据与检测阈值进行比较,得出:
对于真实数据,您需要选择合适的窗口宽度和阈值以获得所需结果,这两者都取决于您希望检测的干扰的带宽和振幅
此外,如果样本数量足够大,则可能需要防止算术溢出。它们需要小于232/窗口宽度,以确保积分器中没有溢出。或者,您可以对窗口类型使用浮点或uint64\t,或者添加代码来处理饱和度。对于以下信号: 如果取两个连续样本之间差值的绝对值,则得到: 这还不够好,无法明确区分那些未持续的小干扰。但是 如果你用一个简单的移动和,一个abs微分的漏积分。此处使用了4个不同样本的窗口宽度: 移动平均引入了滞后或相移,在数据存储且处理不实时的情况下,可通过从定时中减去一半窗口宽度来轻松补偿: 对于实时处理,如果滞后非常关键,则可能需要更复杂的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;
}
将原始数据与检测阈值进行比较,得出:
对于真实数据,您需要选择合适的窗口宽度和阈值以获得所需结果,这两者都取决于您希望检测的干扰的带宽和振幅
此外,如果样本数量足够大,则可能需要防止算术溢出。它们需要小于232/窗口宽度,以确保积分器中没有溢出。或者,您可以使用浮点或uint64_t作为窗口类型,或者添加代码来处理饱和。您可以使用PID算法的微分部分来确定峰值。目前,将两个连续值的差值作为某种度量,可能与您提到的类似,简单检测将在短时间内寻找正偏移和负偏移,例如,在时间0.2内高于1.0且低于-1.0的样本。我发现您的数据已经以零为中心。如果尖峰的宽度是一致的和可预测的,为什么不取几个样品的模量平均值呢。然后,您可以设置阈值以检测峰值。也许你也可以使用滑动窗口来增加一点可靠性。这可能是一个X-Y问题——你把这种尖峰检测用于什么目的?是什么导致了尖峰?这是一种由你需要检测的加速度引起的现象,还是一种你想要拒绝的信号?如果是后者,则低通IIR滤波器更合适。看起来像是敲击加速计时得到的信号。此外,如果对样本进行积分以获得速度,或者对样本进行二次积分以获得位移,然后,这些干扰可能不会产生影响,因为它们似乎是短期的,并且在稳态附近是平衡的。您可以使用PID算法的微分部分来确定什么是尖峰。目前,将两个连续值的差值作为某种度量,可能与您提到的类似,简单检测将在短时间内寻找正偏移和负偏移,例如,在时间0.2内高于1.0且低于-1.0的样本。我发现您的数据已经以零为中心。如果尖峰的宽度是一致的和可预测的,为什么不取几个样品的模量平均值呢。然后,您可以设置阈值以检测峰值。也许你也可以使用滑动窗口来增加一点可靠性。这可能是一个X-Y问题——你把这种尖峰检测用于什么目的?是什么导致了尖峰?这是一种由你需要检测的加速度引起的现象,还是一种你想要拒绝的信号?如果是后者,则低通IIR滤波器更合适。看起来像是敲击加速计时得到的信号。此外,如果对样本进行积分以获得速度,或对样本进行二次积分以获得位移,则这些扰动可能不会产生影响,因为它们似乎是短期的,并且在稳态附近是平衡的。很少需要计算平均值-总和足够了,不会通过舍入丢弃信息。你所建议的似乎是差分的移动平均值,我建议是移动和。很少需要计算平均值-和足够了,不会通过舍入丢弃信息。看起来你建议的是差分的移动平均值,我建议移动和。