飞道的博客

信号波峰波谷二阶差分识别算法

319人阅读  评论(0)

1、聊一聊

    其实每个人在无助的时候都需要一句"Cry On My Shoulder!"

    今天跟大家介绍一种波峰波谷的检测方法,不是很难,不过能够凸显数学在编程算法中的重要作用。

2、正文部分

1

波峰波谷用处

对于信号波峰波谷识别在嵌入式领域应该是非常广泛的,因为大部分的信号都处于一种时变的状态,信号在时域上处于一种类似于正弦波的波动状态。

比如计步软件就是通过IMU模块所采集的变化的波形状态来识别波峰波谷,最终估算你所走过步数;

图片来源网络侵删

上图显示了一个典型的x-, y-和z-测量模式,对应于一个跑步者的垂直,向前和侧面加速度。无论如何佩戴计步器,至少有一个轴会有相对较大的周期性加速度变化,因此通过检测其波峰波谷等算法即可对于检测步行或跑步的单位周期至关重要。

图片来源网络侵删

还有在电力系统中的交流电压电流,我们需要通过检测波峰波谷来确定电压电流在交流周期中的最大最小值,从而动态调节系统参数来达到自适应的目的,所以波峰波谷的检测是非常有用的。

2

比较法识别

常规的设计办法为比较法 : 其中x表示当前采样点

波峰:f(x) > f(x−1) 且 f(x) > f(x+1)

波谷:f(x) < f(x−1) 且 f(x) < f(x+1)

然而这样识别对于没有什么噪声,且每个采样点为不同的信号来说还是合适的,但在严苛的环境中还需要构造更多的判断条件来进行一些错误判断的规避,终究还是麻烦了一些,并且容易遗漏。

3

差分识别

在学生阶段我们就学习了导数的概念,如果一个函数一阶导数左右异号,那分别就是波峰或者波谷。而对于数字信号的处理通过采样都会变成离散信号,信号对时间的微分在离散域内即为差分。

在进行波形识别之前数据采集是必不可少的,其中最重要的是采样速率和精度,以便从采样信号中不失真的恢复原连续信号。(香农采样)

采样的过程中由于电子器件的杂讯等,数据难免会引入噪声,为了简化识别算法一般都会进行滤波处理,比如一些平滑处理等,然后才开始波峰波谷识别。

A

识别算法过程

1、获得采样点序列

2、进行差分处理

3、由于不在乎具体的差分幅值,把所有数据归一到-1,0,1

4、差分值为0的点即为相同点,如果使用比较法则峰值检测可能失效,便需要更多的条件,而这里我们直接把相同点0置为前一个非0即可规避该问题。

5、最终Diff再次进行差分,-2/+2即为波峰/波谷。

B

参考代码


   
  1. 1#include <stdio.h>
  2. 2#include <stdlib.h>
  3. 3#define SAMPLE_MAX   20
  4. 4#define PV_MAX       10
  5. 5
  6. 6float Sample[SAMPLE_MAX]={ 1, 2, 3, 4, 4, 4, 5, 2, 1, 0, 0, 5, 1, 0, 0, 1, 2, 3, 4, 0};
  7. 7float SampleDiff[SAMPLE_MAX]={ 0};
  8. 8
  9. 9typedef  struct _tag_FindPV
  10. 10{
  11. 11     int Pos_Peak[PV_MAX];     //波峰位置存储
  12. 12     int Pos_Valley[PV_MAX];   //波谷位置存储
  13. 13     int Pcnt;                 //所识别的波峰计数
  14. 14     int Vcnt;                 //所识别的波谷计数
  15. 15}SFindPV;
  16. 16
  17. 17SFindPV stFindPV;
  18. 18
  19. 19 /********************************************
  20. 20 *  Fuction : initialFindPV
  21. 21 *  Note    : 初始化相关数据 
  22. 22 *******************************************/ 
  23. 23void initialFindPV(void)
  24. 24{
  25. 25     int Index =  0;
  26. 26
  27. 27     for(Index =  0; Index < SAMPLE_MAX;Index ++)
  28. 28    {
  29. 29        SampleDiff[Index] =  0;
  30. 30    }
  31. 31
  32. 32     for(Index =  0; Index < PV_MAX;Index ++)
  33. 33    {
  34. 34        stFindPV.Pos_Peak[Index] =  -1;
  35. 35        stFindPV.Pos_Valley[Index] =  -1;
  36. 36    }
  37. 37    stFindPV.Pcnt =  0;
  38. 38    stFindPV.Vcnt =  0;
  39. 39
  40. 40}
  41. 41
  42. 42 /********************************************
  43. 43 *  Fuction : FindPV
  44. 44 *  Note    : 找波峰波谷 
  45. 45 *******************************************/ 
  46. 46void FindPV(SFindPV *pFindPV,float *Sample)
  47. 47{
  48. 48     int i =  0;
  49. 49
  50. 50     //step 1 :首先进行前向差分,并归一化
  51. 51     for(i=  0; i < SAMPLE_MAX -  1; i++)
  52. 52    {
  53. 53         if (Sample[i +  1] - Sample[i]> 0)
  54. 54            SampleDiff[i] =  1;
  55. 55         else  if (Sample[i +  1] - Sample[i] <  0)
  56. 56            SampleDiff[i] =  -1;
  57. 57         else
  58. 58            SampleDiff[i] =  0;
  59. 59    }
  60. 60
  61. 61     //step 2 :对相邻相等的点进行领边坡度处理
  62. 62     for(i=  0; i < SAMPLE_MAX -1; i++)
  63. 63    {
  64. 64         if(SampleDiff[i] ==  0)
  65. 65        {
  66. 66             if(i == (SAMPLE_MAX -2))
  67. 67            {
  68. 68                 if (SampleDiff[i -  1] >=  0)
  69. 69                    SampleDiff[i] =  1;
  70. 70                 else
  71. 71                    SampleDiff[i] =  -1;
  72. 72            }
  73. 73             else
  74. 74            {
  75. 75                 if (SampleDiff[i +  1] >=  0)
  76. 76                    SampleDiff[i] =  1;
  77. 77                 else
  78. 78                    SampleDiff[i] =  -1;
  79. 79            }
  80. 80
  81. 81        }
  82. 82    }
  83. 83
  84. 84     //step 3 :对相邻相等的点进行领边坡度处理
  85. 85     for(i=  0; i < SAMPLE_MAX -1; i++)
  86. 86    {
  87. 87         if(SampleDiff[i +  1] - SampleDiff[i] ==  -2//波峰识别
  88. 88        {
  89. 89            pFindPV->Pos_Peak[pFindPV->Pcnt] = i +  1;
  90. 90            pFindPV->Pcnt++; 
  91. 91        }
  92. 92         else  if(SampleDiff[i +  1] - SampleDiff[i] ==  2//波谷识别
  93. 93        {
  94. 94            pFindPV->Pos_Valley[pFindPV->Vcnt] = i +  1;
  95. 95            pFindPV->Vcnt++;
  96. 96        }
  97. 97    }
  98. 98}
  99. 99
  100. 100 /********************************************
  101. 101 *  Fuction : main
  102. 102 *  Note    : 模拟查找波峰波谷 
  103. 103 *******************************************/ 
  104. 104int main( int argc, char *argv[]) {
  105. 105
  106. 106     int i =  0;
  107. 107
  108. 108    initialFindPV();
  109. 109
  110. 110    FindPV(&stFindPV,Sample);
  111. 111
  112. 112    printf( "Peak\n");
  113. 113     for(i =  0 ;i< stFindPV.Pcnt;i++)
  114. 114    {
  115. 115        printf( "-%d",stFindPV.Pos_Peak[i] +  1);   //加1是为了与上图横坐标一致 
  116. 116    }
  117. 117
  118. 118    printf( "\nValley\n");
  119. 119     for(i =  0 ;i< stFindPV.Vcnt;i++)
  120. 120    {
  121. 121        printf( "-%d",stFindPV.Pos_Valley[i] +  1);
  122. 122    }
  123. 123
  124. 124    printf( "\n\n");
  125. 125    printf( "欢迎关注:最后一个bug\n");
  126. 126     return  0;
  127. 127}

2、最后

   

    当然在实际的项目中为了更加稳定的识别波峰波谷可能会对波峰波谷的出现特点进行限制,从而进一步减少误识别,也有许多人使用数据拟合的办法来识别波峰波谷,那么识别的准确度就与所拟合的函数有关,通过数学方法对所拟合函数进行波峰波谷的求解,最终得到信号的波峰波谷,不过这样的拟合过程对平台的处理能力提出了一定的要求。

    好了,今天的分享就到这里,我是bug菌,最近在公众号推送了一些软文,当不会忘本!感谢各位,顺手点个赞!

推荐专辑  点击蓝色字体即可跳转

☞  MCU进阶专辑 

☞  嵌入式C语言进阶专辑 

☞  “bug说”专辑 

☞ 专辑|Linux应用程序编程大全

☞ 专辑|学点网络知识

☞ 专辑|手撕C语言

☞ 专辑|手撕C++语言

☞ 专辑|经验分享


转载:https://blog.csdn.net/qq_33471732/article/details/114006379
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场