一个简单的语音识别程序

2020-01-15 19:24发布

本帖最后由 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的识别

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。