Yin算法应用(单片机\嵌入式)

  1. 根据采样定理设置好采样周期,至少大于2倍要采样的信号周期。这里以吉他6根弦为例

名称

频率(hz)
E1 329.6276
2B 246.9417
3G 195.9977
4D 146.8324
5A 110
6E 82.4069

这里最高是329.6276Hz  最低是82.4069  ,顾采样周期一点要大于329.6276*2=659.2552Hz,考虑到好有紧弦,频率可能会更高,这里我设置了4kHz采样周期以及8kHz采样周期,采集压电陶瓷经过运放放大后的信号,两种方案。均大于659.2552。其它信号自己类比一下,类似的。设置好ADC采样周期,这里我使用定时器设置4kH或者8khz触发ADC+DMA的形式进行采样,采样4096个点,具体配置可以自己查看别的帖子。这里主要讲算法。

 2.下面进入到yin算法部分

首先在一段信号中确定一个起始点,记为Xi(这个起始点如何确定一会再说),然后在这段信号中定一段信号范围,这里可以是从前面的4096个点里的1024个点作为算法的作用范围,因为采样到的4096个点很多是无用点,或者是信号周期性不明显的点,一会再说如何找出这个需要yin处理的1024个点的范围)。

Y_l=\frac{1}{n}\sum_{i=1}^{n}|X_i-X_(i+len)|     这是单个yin的计算公式,其中Len是由Fv(一会再说这个Fv,以及如何确定len)确定的

我们的最终目标计算得出yin的序列,即:Y=[y1,y2,y3.......yn],这里可以视为一个yin序列数组,根据这一个数组就可以得出信号周期。

第一步:找出1024个有用点,以及Xi起始点的第一个点:方法思路:从4096个ADC采样点钟逐次找xi~xi+1023的方差或者是标准差,符合大于某个方差(标准差)阈值的数据则视为有效数据,方差、标准差反应了数据样本的离散程度,越大则代表信号越强,幅值越大,这个阈值需要自己确定,可以打印出来对比,以及吉他为例,我在不拨动琴弦的情况下,计算出来的方差均为100以下的数值,一但拨动了琴弦则上升到300以上,那么方差大于300的则为有效的数据。,在这段数据的第一个数则Xi的起点了。

第二步:确定Fv,以及len,以吉他为例子,根据上表我们可以大致确定一个吉他空弦可能的范围,(弦可能松也可能紧),这里我们定为F_v\epsilon[20Hz,1000Hz],这里我们的采样率为Fs(4k或者8k),则每两个采样点之间的时间间隔为\frac{1}{Fs},由于len是一个变化的值,则假设Xi~X(i+len)这个间隔刚好等于信号的一个周期T,这里的话Xi-X(i+len)则得到最低值,(yin的核心思想就是找最低值),那么有T= len* \frac{1}{Fs},而这个T周期肯定是满足Fv频率范围的,因为周期与频率是倒数关系所以有F_v = \frac{1}{len}*F_s,即

20\leq \frac{1}{len}*F_s\leq1000,假设采样率Fs为8000,则len的最小值为8,最大值为400,那么Yin序列内的个数就应该是400-8=393个yin数据也就是yin序列数组的大小,

	//开始yin序列
	for(len=8;len<=400;len++)  //计算len范围内的序列 Ylen=I到N的累加|Xi-Xi+len|  (小写为下标)
	{        //BUG
		for(i=Start_x;i<=(max2-len);i++)//累加
		{

				 Yin_Seq[n]+=abs(input[i]-input[i+len]);
				 mean++;
		}
		
		Yin_Seq[n]/=mean;
		mean=0;
	//	printf("%ld\n",Yin_Seq[n]);
		n++;
	}

其中input为输入的1024个有效样本,最后得出的大小为393的Yin_Seq序列数组。

假设采样率设置为4000k,(因为8k的采集波形我没有做记录,只做了4k采样率的波形图记录,这里采样率4k\8k不影响最终的结果,影响的只是精度以及yin序列数组的大小)这里拨动一下吉他空弦的6弦(频率为82.4069Hz,周期为0.0121349061789729s),采样一组数据打印,放到excel作分析,如下图所示:

可以看到在X轴的第95、143的点取得最低(Xi-X(i+len)),那么这两个点的间隔就是该弦目前的频率,143-95=48,我们的采样率是4000Hz,即为0.00025s,0.0121349061789729/0.00025=48.53962471589151,刚好吻合,其他琴弦计算方法也是一样,这里就不一一列举了。

上图的这种波形用C语言找最小值不太好找,所以甚至有可能,有一个点也是比较低的,但是相邻的却不是你想要的点,这种情况下我们可以再做多一步处理,加多一次限幅,这个限幅阈值可以自己根据打印出来的波形自己分析确定,我的思路是先排序按照升序的方法,然后找出前n个最小值,这个n自己确定。然后取平均,超过这个n的幅值则统统设为一个较大的数字,这里我设为400,限幅处理后的波形如下图所示:

怎么样,这样一来是不是就很好处理了,简单写一段代码就可以找出两个极值点的间隔了。

最后总结一下:该过程涉及到了奈奎斯特采样定理、方差(标准差)、排序、yin、限幅滤波等一系列算法,由于算法代码是用到工作上的,出于保密,不方便透露,本着互联网开放共享的原则,这里和大家分享,这里的总体思路已经非常的明朗了,可以自己写代码实现。这里不单单是可以用在音频处理上,也可以用在很多信号的处理上,具体的可以自己去研究。

猜你喜欢

转载自blog.csdn.net/qq_23898287/article/details/106991989