本文讨论的是实际使用过程中计算过零率的一种自适应方法。过零率常用于语音检测识别中,一般叫短时过零率更贴切些(指一段短时平稳信号才能计算过零率),简单有效,近期亦打算将这个简单的概念用于识别脚步声和卡车声(近似短时平稳)。过零率的定义计算一般通过下面的表达式描述:
[img]http://www.forkosh.com/mathtex.cgi?%20%5CLarge%20Z_n=%5Cfrac%7B1%7D%7BN%7D%5Csum_%7Bm=0%7D%5E%7BN-1%7D%5C%7C%7Bsgn[x_n%28m+1%29]-sgn[x_n%28m%29]%7D%5C%7C[/img]
过零率是对频率从时域进行的一种简单的度量,一般情况下,过零率越大频率近似越高,反正亦然,相关推倒可参考相关文献。
自适应噪声阈值自然信号由于电路、环境引入的噪声会在0水平位置波动,直接按上式计算过零率,严重影响识别效果。因此,本文在计算过零率时,对信号的幅值进行阈值限定,阈值的计算方法是:
[img]http://www.forkosh.com/mathtex.cgi?%20%5CLarge%20THR%28t%29=%5Cfrac%7B1%7D%7B2%7D%5C%7BTHR%28t-1%29+F*V_%7Bpp%7D%28t%29%5C%7D,%20F=[0.1%5C%200.2][/img]
其中F为阈值与峰峰值的系数因子,一般F在0.1~0.2之间,Vpp表示当前短时信号的峰峰值,THR(t)表示t时段的阈值。
因此增加阈值判别后计算过零率的流程图为:
对应的C程序为:
/* signal thresh adaptive structure */struct st_zerorate_thr{ uint32_t pre; uint32_t cur;};/* * @brief * Calculate short time zero cross rate. Short time means n, n often choose to * be a frame(256,512 and so on) * * The diffrence with upstairs is that this one consider the adaptive thresh * when checking the signal, which removes the influence of noise. * @inputs * x[] - input sound signal * n - size of a frame * @outputs * @retval * zero cross rate in a frame length */uint16_t zero_rate(int16_t x[], int n, struct st_zerorate_thr thr){ int i = 1; /* Init to 1 */ uint16_t zero_cnt = 0; float tmp = 0; uint8_t x_pre = 0; while ( (x < thr.cur) && (x > -thr.cur) && (i<n) ) { i++; } x_pre = x[i++]; while ((i < n) && ((x > thr.cur) || (x < -thr.cur)) ) { tmp = x * x_pre; if (tmp < 0) { zero_cnt = zero_cnt + 1; } x_pre = x; i++; } return zero_cnt;}上面代码定义了阈值结构体st_zerorate_thr,更新阈值的方法按(2)式执行,
#define TH_FACTOR (0.2f) /* 0.1~0.2 */void zerorate_thr_update(struct st_zerorate_thr thr, uint32_t peak_value){ thr.pre = thr.cur; thr.cur = (uint32_t)(((thr.pre + TH_FACTOR * peak_value) / 2) );}关于初始阈值的选择:
[img]http://www.forkosh.com/mathtex.cgi?%20%5CLarge%20THR%280%29=F*V_%7Bpp%7D%280%29,%20F=[0.1%5C%200.2][/img]
改进:增加能量阈值的策略这时要考虑的情况有些复杂,除了噪声阈值外,还要考虑能量阈值(确定是否是信号段),如下图:
上图为采集的地震信号(比如脚步声信号),只有有些数据才是我们想要分析的,因此可以通过能量对数据进行划分。为简单起见,直接通过信号的幅值对确定有效信号区域也是可以的。
当大于某个固定的阈值energy_thr时,并持续一段时间时,认为信号进入有效区域,开始按“自适应噪声阈值”的方案进行计算过零率
能量阈值一般都大于自适应噪声阈值,可以通过实验获得:
- 采集无信号时的背景噪声,计算峰峰值;
- 采集有目标时的信号,计算峰峰值
在两者之间找到一条错误率最低的水平分界值,作为能量阈值。
为简化程序的设计,本程序使用状态机的设计思想,2个状态:安静(无目标)状态、有目标状态。C程序上直接使用switch...case语句就可以了,对应的C程序如下:
/* * @brief * Calculate short time zero cross rate. Short time means n, n often choose to * be a frame(256,512 and so on) * * The diffrence with upstairs is that this one consider the adaptive thresh * when checking the signal, which removes the influence of noise. * @inputs * x[] - input sound signal * n - size of a frame * thr - threshold(Vp) for removing noise ripple * energy_thr - threshold(Vp) for ensure wether is signal start or stop * @outputs * @retval * zero cross rate in a frame length */uint16_t zero_rate(int16_t x[], uint32_t n, struct st_zerorate_thr thr, uint16_t energy_thr){ uint32_t i = 0; uint16_t zero_cnt = 0; int16_t x_pre = 0; uint8_t status = 0; uint8_t energy_cnt = 0;#define CONTINUOUS_BEGIN (2) // 持续大于能量阈值的点数,用于检测信号开始#define CONTINUOUS_END (5) // 持续小于能量阈值的点数,用于检测信号结尾 for (i=0; i<n; i++) { switch(status) { case 0: // Slience or maybe signal if (abs(x) > energy_thr) { if (++energy_cnt >= CONTINUOUS_BEGIN) { status = 1; energy_cnt = 0; x_pre = x; } else { // do nothing } } break; case 1: // Signal if (abs(x) > thr.cur) { if ( (x * x_pre) < 0) { zero_cnt = zero_cnt + 1; } x_pre = x; energy_cnt = 0; } else { if (abs(x) < energy_thr) { if (++energy_cnt >= CONTINUOUS_END) { status = 0; energy_cnt = 0; } } else { // do nothing } } break; default: status = 0; break; } } return zero_cnt;}补充:测试效果及新方法提出好吧:结论只有一个:
在含有低频噪声的环境,使用过零率是很难区分脚步声和卡车的。
如下,是分别采集脚步声和卡车的图形:
因为卡车的采集信号高频都载波到低频信号上了,而在未判断实际目标之前,是无法使用低通滤波进行滤波(可能滤掉脚步声信号)。
综上,我看过很多的论文,大都提到使用过零率对脚步声了卡车进行识别,而且识别效果还不错,我不知他们的实验环境是什么样的,至少从上面的实验分析,还是有问题的。
因此,提出新的特征,用于区分两类信号——数字化占空比。其过程是:设定一个阈值,从头到尾扫描一帧信号,大于阈值的点设为1,否则设为0,我称之为数字化过程,然后计算高电平(即1)占所有一帧(512点)的百分比,用该百分比作为脚步声和卡车的识别数学特征。好了,我们用数学表述得专业些:
其中{x>=THR}表示序列中值大于THR的点数。
测试结果:取THR=0.4*Vpp,则计算脚步信号的Dutycycle=4,而卡车信号的Dutycycle=113,差异非常明显。 |