本帖最后由 lcw_swust 于 2012-5-19 22:57 编辑
最近想做语音识别玩玩,网上查了查,找到一个用Atmega32实现的语音识别机械车
地址是:
http://courses.cit.cornell.edu/e ... h8_css34/index.html
貌似是利用带通滤波取得频谱(或者叫功率谱?),看不太懂.我决定用DFT算法,因为它节约内存
DFT程序借借鉴了 hendry 单片机 DTMF 软解码算法的实现
http://www.ourdev.cn/forum.php?m ... &highlight=dtmf
//--------------------------------------------------
//DFT运算
//注意,ad是有符号数,无符号的AD值需减128
//返回值为1表示已经计算了功率谱
//--------------------------------------------------
U8 dft(S8 ad)
{
U8 i;
U8 offset;//查表指针
U32 temp;
//ad-=128;//去直流分量
for(i=0;i<NFREQ;i++)//每个频点计算实部和虚部
{
offset=tabp
;//取查表指针
s_dft_image += (S16)ad * sintab[offset];//>>8;
offset+=PI2/4;//偏移1/4周期为cos表
s_dft_real += (S16)ad * sintab[offset];//>>8;//cos表
tabp+=tabinc;//指针下移
}
s_dft_p ++;
if (s_dft_p == NSAMP) //采样点已达到设定值,计算功率
{
s_dft_p = 0; //点数清0
for (i = 0; i < NFREQ; i ++)//每个频点计算功率
{
s_dft_real/=NSAMP*6; //除以合适的值能使得功率在一字节内
s_dft_image/=NSAMP*6;
temp=s_dft_real*s_dft_real + s_dft_image*s_dft_image;
if(temp>65535)temp=65535;
s_dft_real = sqrt16(temp);
//s_dft_real = sqrt32(((s_dft_real*s_dft_real) + (s_dft_image*s_dft_image)));
}
return 1;
}
return 0;
}
本程序流程大概是这样:
定时读取ADC,计算5个频率点的实部与虚部,采集64点后计算5个频率点的功率,称之为功率谱
当功率值达到一定值后,认为是一帧语音开始,此后计算的34次功率谱分别存入数组中.
若是在训练状态,则将这34个功率谱存入模板数组,训练完毕后进入识别状态.
在识别状态下,用这34个功率谱去与模板匹配,找出误差最小的,若误差小于一定值,则识别成功.
目前程序只识别两个命令,识别率还不太理想,偶尔会有误码.
如果把模板存进EEPROM,则可以增加命令的个数.
程序量较小,占用内存也小,可以很方便的移植到AVR单片机.
电路用了AGC,就是把2SK30A当作可变电阻,GS间负电压越大,DS间电阻越大.
据说可以用发光二极管+光敏电阻来做AGC.
初步的实验也可以用电脑音频输出串联电容到ADC输入口,至少这样每次放出来的声音是一样的,便于验证
频谱计算是否正确.
附件中有一个用VB写的查看波形的程序,只需通过串口向它发送数据就行了,格式是0xAA+数据字节数+数据
识别结果.jpg里每行最后一个字节是结果,为0表示未能识别,前10行是对命令1的识别,后10行是对命令2的识别
各有各的优点,模拟电路速度快,数字的方式更灵活.
其实8位机做FFT也不会消耗太多资源,比如用ATMEGA8就能实现128点FFT,如果这个用模拟电路搭出来得多少阶滤波多少个运放呀.
一周热门 更多>