1、聊一聊
其实每个人在无助的时候都需要一句"Cry On My Shoulder!" 今天跟大家介绍一种波峰波谷的检测方法,不是很难,不过能够凸显数学在编程算法中的重要作用。
2、正文部分
波峰波谷用处
对于信号波峰波谷识别在嵌入式领域应该是非常广泛的,因为大部分的信号都处于一种时变的状态,信号在时域上处于一种类似于正弦波的波动状态。
比如计步软件就是通过IMU模块所采集的变化的波形状态来识别波峰波谷,最终估算你所走过步数;
上图显示了一个典型的x-, y-和z-测量模式,对应于一个跑步者的垂直,向前和侧面加速度。无论如何佩戴计步器,至少有一个轴会有相对较大的周期性加速度变化,因此通过检测其波峰波谷等算法即可对于检测步行或跑步的单位周期至关重要。
还有在电力系统中的交流电压电流,我们需要通过检测波峰波谷来确定电压电流在交流周期中的最大最小值,从而动态调节系统参数来达到自适应的目的,所以波峰波谷的检测是非常有用的。
比较法识别
常规的设计办法为比较法 : 其中x表示当前采样点
波峰:f(x) > f(x−1) 且 f(x) > f(x+1)
波谷:f(x) < f(x−1) 且 f(x) < f(x+1)
然而这样识别对于没有什么噪声,且每个采样点为不同的信号来说还是合适的,但在严苛的环境中还需要构造更多的判断条件来进行一些错误判断的规避,终究还是麻烦了一些,并且容易遗漏。
差分识别
在学生阶段我们就学习了导数的概念,如果一个函数一阶导数左右异号,那分别就是波峰或者波谷。而对于数字信号的处理通过采样都会变成离散信号,信号对时间的微分在离散域内即为差分。 在进行波形识别之前数据采集是必不可少的,其中最重要的是采样速率和精度,以便从采样信号中不失真的恢复原连续信号。(香农采样) 采样的过程中由于电子器件的杂讯等,数据难免会引入噪声,为了简化识别算法一般都会进行滤波处理,比如一些平滑处理等,然后才开始波峰波谷识别。
A、识别算法过程
1、获得采样点序列
2、进行差分处理
3、由于不在乎具体的差分幅值,把所有数据归一到-1,0,1
4、差分值为0的点即为相同点,如果使用比较法则峰值检测可能失效,便需要更多的条件,而这里我们直接把相同点0置为前一个非0即可规避该问题。
5、最终Diff再次进行差分,-2/+2即为波峰/波谷。
B、参考代码
1#include2#include 3#define SAMPLE_MAX 20 4#define PV_MAX 10 5 6float Sample[SAMPLE_MAX]={1,2,3,4,4,4,5,2,1,0,0,5,1,0,0,1,2,3,4,0}; 7float SampleDiff[SAMPLE_MAX]={0}; 8 9typedef struct _tag_FindPV 10{ 11 int Pos_Peak[PV_MAX]; //波峰位置存储 12 int Pos_Valley[PV_MAX]; //波谷位置存储 13 int Pcnt; //所识别的波峰计数 14 int Vcnt; //所识别的波谷计数 15}SFindPV; 16 17SFindPV stFindPV; 18 19/******************************************** 20 * Fuction : initialFindPV 21 * Note : 初始化相关数据 22 *******************************************/ 23void initialFindPV(void) 24{ 25 int Index = 0; 26 27 for(Index = 0; Index < SAMPLE_MAX;Index ++) 28 { 29 SampleDiff[Index] = 0; 30 } 31 32 for(Index = 0; Index < PV_MAX;Index ++) 33 { 34 stFindPV.Pos_Peak[Index] = -1; 35 stFindPV.Pos_Valley[Index] = -1; 36 } 37 stFindPV.Pcnt = 0; 38 stFindPV.Vcnt = 0; 39 40} 41 42/******************************************** 43 * Fuction : FindPV 44 * Note : 找波峰波谷 45 *******************************************/ 46void FindPV(SFindPV *pFindPV,float *Sample) 47{ 48 int i = 0; 49 50 //step 1 :首先进行前向差分,并归一化 51 for(i= 0; i < SAMPLE_MAX - 1; i++) 52 { 53 if (Sample[i + 1] - Sample[i]>0) 54 SampleDiff[i] = 1; 55 else if (Sample[i + 1] - Sample[i] < 0) 56 SampleDiff[i] = -1; 57 else 58 SampleDiff[i] = 0; 59 } 60 61 //step 2 :对相邻相等的点进行领边坡度处理 62 for(i= 0; i < SAMPLE_MAX-1; i++) 63 { 64 if(SampleDiff[i] == 0) 65 { 66 if(i == (SAMPLE_MAX-2)) 67 { 68 if (SampleDiff[i - 1] >= 0) 69 SampleDiff[i] = 1; 70 else 71 SampleDiff[i] = -1; 72 } 73 else 74 { 75 if (SampleDiff[i + 1] >= 0) 76 SampleDiff[i] = 1; 77 else 78 SampleDiff[i] = -1; 79 } 80 81 } 82 } 83 84 //step 3 :对相邻相等的点进行领边坡度处理 85 for(i= 0; i < SAMPLE_MAX-1; i++) 86 { 87 if(SampleDiff[i + 1] - SampleDiff[i] == -2) //波峰识别 88 { 89 pFindPV->Pos_Peak[pFindPV->Pcnt] = i + 1; 90 pFindPV->Pcnt++; 91 } 92 else if(SampleDiff[i + 1] - SampleDiff[i] == 2) //波谷识别 93 { 94 pFindPV->Pos_Valley[pFindPV->Vcnt] = i + 1; 95 pFindPV->Vcnt++; 96 } 97 } 98} 99 100/******************************************** 101 * Fuction : main 102 * Note : 模拟查找波峰波谷 103 *******************************************/ 104int main(int argc, char *argv[]) { 105 106 int i = 0; 107 108 initialFindPV(); 109 110 FindPV(&stFindPV,Sample); 111 112 printf("Peak "); 113 for(i = 0 ;i< stFindPV.Pcnt;i++) 114 { 115 printf("-%d",stFindPV.Pos_Peak[i] + 1); //加1是为了与上图横坐标一致 116 } 117 118 printf(" Valley "); 119 for(i = 0 ;i< stFindPV.Vcnt;i++) 120 { 121 printf("-%d",stFindPV.Pos_Valley[i] + 1); 122 } 123 124 printf(" "); 125 printf("欢迎关注:最后一个bug "); 126 return 0; 127}
2、最后
当然在实际的项目中为了更加稳定的识别波峰波谷可能会对波峰波谷的出现特点进行限制,从而进一步减少误识别,也有许多人使用数据拟合的办法来识别波峰波谷,那么识别的准确度就与所拟合的函数有关,通过数学方法对所拟合函数进行波峰波谷的求解,最终得到信号的波峰波谷,不过这样的拟合过程对平台的处理能力提出了一定的要求。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !