查看: 14549|回复: 28

[原创] 【LPC54114双核最终任务】--简单的语音识别【完成】

[复制链接]
  • TA的每日心情
    开心
    2 小时前
  • 签到天数: 815 天

    [LV.10]以坛为家III

    71

    主题

    2438

    帖子

    24

    金牌会员

    Rank: 6Rank: 6

    积分
    5494
    最后登录
    2024-4-19
    发表于 2017-8-14 14:06:21 | 显示全部楼层 |阅读模式
    本帖最后由 leo121_3006061 于 2017-11-5 14:10 编辑

       不知不觉已经到了最终任务了,网上搜索了不少关于语音识别(语音控制)的资料,脑补了一下,基本了解了(单机语音识别)以下几个必备的知识,            关键词:1.HWVAD(代替软VAD),实现数据采集,噪声过滤

                             2.MFCC 语音的Mel频率倒谱系数(MFCC),主要由FFT(快速傅立叶变换)来实现。
                          3.DTW 动态时间弯折算法与特征模板相匹配,最终输出识别结果
                             4.matlab 用Matlab对上述算法进行仿真,经多次试验得出算法中所需各系数的最优值(我本人对这个不是太熟悉,略过)
        语音处理流程:先通过LPC54110自带的硬件HWVAD采集有效的语音数据,利用DMA直接传送给RAM(不占用MCU资源),,再把语音数据发送给MFCC处理,然后通过DTW算法进行模版匹配,找到最相近的语音,返回识别结果。本例实现0~9的数字识别,其中0~7对应控制板子的8个led灯      

    说明:目前我的工程实现了以上流程的每个步骤,但是还没有把这些步骤按照整个个流程串联起来,后边要花点时间来做这件事情。

    整个工程是在例程里的dmic_ hwvad的基础上修改而成的。并且引入ARM_math,如下图所示
    finaltask_setup.jpg
    如果编译报错信息为==》
    #error "Define according the used Cortex core ARM_MATH_CM7, ARM_MATH_CM4, ARM_MATH_CM3, ARM_MATH_CM0PLUS or ARM_MATH_CM0"还需要在Project->Options for target"XXXX")中的C/C++选项卡的Preprocessor Symbols栏的Define中加入如下的语句:ARM_MATH_CM4, __FPU_PRESENT=1, __FPU_USED =1, __CC_ARM

    第一步,用dmic_hwvad实现数据采集,过滤噪声
    finaltask_func.jpg

    dmic_init()采用标准的例程里的代码,在前边的任务里都做过了,毋须多言了

    finaltask_dmic_init.jpg
    dmic_dma_init() 如下图

    finaltask_dmic_dma.jpg

    我们要在void DMIC0_HWVAD_Callback(void)里实现语音数据采集,红框下边是打印输出采集到的数据
    finaltask_input.jpg

    为了验证采集的数据,我们对着DMIC说话,或者拍手,通过串口助手,显示采集数据是变化的,(也可以通过wm8904来播放采集到的数据,加以验证)

    finaltask_scom.jpg

    第二步 实现MFCC,第一步采集到的数据,是非常复杂的语音数据,是不能直接用于识别的,需要通过MFCC进行处理
    语音信号特征提取。提取有效语音中每帧语音信号的Mel频率倒谱系数(MFCC)系数。
    1. <blockquote>/*******   MFCC.C    *******/
    复制代码


    乱码的部分请参考下边的截图
    finaltask_MFCC.jpg

    第三步 要建立一个性能良好的语音识别系统仅有好的语音特征还不够,还要有适当的语音识别的模型和算法。在现阶段,语音识别的过程是根据模式匹配的原则,计算未知语音模式与语音模板库中的每一个模板的距离测度,从而得到最佳的匹配模式。目前,语音识别所应用的模型匹配方法主要有动态时间弯折(DTW:Dynamic Time Warping)、隐马尔可夫模型(HMM:Hidden Markov Model)和人工神经网络(ANN:Artificial Neural Networks)等。当今孤立词识别领域最常用的识别算法是DTW和HMM。

    DTW算法是较早的一种模式匹配和模型训练技术,它应用动态规划的方法成功解决了语音信号特征参数序列比较时时长不等的难题,在孤立词语音识别中获得了良好的性能。DTW算法是建立在动态规划(DP:Dynamic Programming)的理论基础上的。动态规划是一个很有效的方法来求取一个问题的最佳解。其中心思想简单的说可以描述为:在一条最佳的路径上,其中任意一条子路径也都必须是相关子问题的最佳路径,否则原路径就不是最佳路径。下边附上代码
    1. /*******       DTW.C         ********/


    2. #include <math.h>
    3. #include "MFCC.H"
    4. #include "DTW.H"

    5. #include "fsl_device_registers.h"
    6. #include "fsl_debug_console.h"
    7. #include "board.h"
    8. #include "fsl_dmic.h"
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include "app_led.h"
    12. #include "app_key.h"
    13. #include "app_dmic.h"

    14. #include "pin_mux.h"
    15. #include <stdbool.h>
    16. #include "stdint.h"
    17. /*        
    18.         DTWËã·¨ ͨ¹ý¾Ö²¿ÓÅ»¯µÄ·½·¨ÊµÏÖ¼ÓȨ¾àÀë×ܺÍ×îС
    19.         
    20.         Ê±¼ä¹æÕûº¯Êý£º
    21.         C={c(1),c(2),¡­,c(N)}
    22.         NΪ·¾¶³¤¶È£¬c(n)=(i(n),j(n))±íʾµÚn¸öÆ¥ÅäµãÊÇÓвο¼Ä£°åµÄ
    23. µÚi(n)¸öÌØÕ÷ʸÁ¿Óë´ý²âÄ£°åµÄµÚj(n)¸öÌØÕ÷ʸÁ¿¹¹³ÉµÄÆ¥Åäµã¶Ô£¬Á½
    24. ÕßÖ®¼äµÄ¾àÀëd(x(i(n)),y(j(n)))³ÆΪƥÅä¾àÀë¡£
    25.         Ê±¼ä¹æÕûº¯ÊýÂú×ãÒ»ÏÂÔ¼Êø£º
    26.         1.µ¥µ÷ÐÔ£¬¹æÕûº¯Êýµ¥µ÷Ôö¼Ó¡£
    27.         2.ÆðµãÖÕµãÔ¼Êø£¬Æðµã¶ÔÆðµã£¬ÖÕµã¶ÔÖյ㡣
    28.         3.Á¬ÐøÐÔ£¬²»ÔÊÐíÌø¹ýÈκÎÒ»µã¡£
    29.         4.×î´ó¹æÕûÁ¿²»³¬¹ýijһ¼«ÏÞÖµ¡£|i(n)-j(n)|<M,MΪ´°¿í¡£¹æÕû
    30. º¯ÊýËù´¦µÄÇøÓòλÓÚƽÐÐËıßÐÎÄÚ¡£¾Ö²¿Â·¾¶Ô¼Êø£¬ÓÃÓÚÏÞÖƵ±µÚn²½
    31. Ϊ(i(n),j(n))ʱ£¬Ç°¼¸²½´æÔÚ¼¸ÖÖ¿ÉÄܵÄ·¾¶¡£

    32.         DTW²½Ö裺
    33.         1.³õʼ»¯¡£Áîi(0)=j(0)=0,i(N)=in_frm_num,j(N)=mdl_frm_num.
    34. È·¶¨Ò»¸öƽÐÐËıßÐΣ¬ÓÐÁ½¸öλÓÚ(0,0)ºÍ(in_frm_num,mdl_frm_num)µÄ¶¥µã£¬ÏàÁÚб±ßб
    35. ÂÊ·Ö±ðÊÇ2ºÍ1/2.¹æÕûº¯Êý²»¿É³¬³ö´ËƽÐÐËıßÐΡ£
    36.         2.µÝÍÆÇóÀۼƾàÀë¡£
    37.         
    38.         ÈôÊäÈëÌØÕ÷ÓëÌØÕ÷Ä£°åµÄÖ¡Êý²î±ð¹ý´ó£¬Ö±½Ó½«Æ¥Åä¾àÀëÉèΪ×î´ó
    39.         frm_in_num<(frm_mdl_num/2)||frm_in_num>(frm_mdl_num*2)
    40. */

    41. /*
    42.         »ñÈ¡Á½¸öÌØÕ÷ʸÁ¿Ö®¼äµÄ¾àÀë
    43.         ²ÎÊý
    44.         frm_ftr1        ÌØÕ÷ʸÁ¿1
    45.         frm_ftr2        ÌØÕ÷ʸÁ¿2
    46.         ·µ»ØÖµ
    47.         dis                        Ê¸Á¿¾àÀë
    48. */
    49. uint32_t get_dis(int16_t *frm_ftr1, int16_t *frm_ftr2)
    50. {
    51.         uint8_t         i;
    52.         uint32_t        dis;
    53.         int32_t dif;        //Á½Ê¸Á¿Ïàͬά¶ÈÉϵIJîÖµ
    54.         
    55.         dis=0;
    56.         for(i=0;i<mfcc_num;i++)
    57.         {
    58.                 //PRINTF("dis=%d ",dis);
    59.                 dif=frm_ftr1[i]-frm_ftr2[i];
    60.                 dis+=(dif*dif);
    61.         }
    62.         //PRINTF("dis=%d ",dis);
    63.         dis=sqrtf(dis);
    64.         //PRINTF("%d\r\n",dis);
    65.         return dis;
    66. }

    67. //ƽÐÐËıßÐÎÁ½ÍâÁ½¶¥µã X×ø±êÖµ
    68. static uint16_t        X1;                        //Éϱ߽»µã
    69. static uint16_t        X2;                        //ϱ߽»µã
    70. static uint16_t        in_frm_num;        //ÊäÈëÌØÕ÷Ö¡Êý
    71. static uint16_t        mdl_frm_num;//ÌØÕ÷Ä£°åÖ¡Êý


    72. #define ins                0
    73. #define outs        1

    74. /*
    75.         ·¶Î§¿ØÖÆ
    76. */
    77. uint8_t dtw_limit(uint16_t x, uint16_t y)
    78. {
    79.         if(x<X1)
    80.         {
    81.                 if(y>=((2*x)+2))
    82.                 {
    83.                         return outs;
    84.                 }
    85.         }
    86.         else
    87.         {
    88.                 if((2*y+in_frm_num-2*mdl_frm_num)>=(x+4))
    89.                 {
    90.                         return outs;
    91.                 }
    92.         }
    93.         
    94.         if(x<X2)
    95.         {
    96.                 if((2*y+2)<=x)
    97.                 {
    98.                         return outs;
    99.                 }
    100.         }
    101.         else
    102.         {
    103.                 if((y+4)<=(2*x+mdl_frm_num-2*in_frm_num))
    104.                 {
    105.                         return outs;
    106.                 }
    107.         }
    108.         
    109.         return ins;
    110. }

    111. /*        
    112.         DTW ¶¯Ì¬Ê±¼ä¹æÕû
    113.         ²ÎÊý
    114.         ftr_in        :ÊäÈëÌØÕ÷Öµ
    115.         ftr_mdl        :ÌØÕ÷Ä£°æ
    116.         ·µ»ØÖµ
    117.         dis                :ÀÛ¼ÆÆ¥Åä¾àÀë
    118. */

    119. uint32_t dtw(v_ftr_tag *ftr_in, v_ftr_tag *frt_mdl)
    120. {
    121.         uint32_t dis;
    122.         uint16_t x,y;
    123.         uint16_t step;
    124.         int16_t *in;
    125.         int16_t *mdl;
    126.         uint32_t up,right,right_up;
    127.         uint32_t min;
    128.         
    129.         in_frm_num=ftr_in->frm_num;
    130.         mdl_frm_num=frt_mdl->frm_num;
    131.         
    132.         if((in_frm_num>(mdl_frm_num*2))||((2*in_frm_num)<mdl_frm_num))
    133.         {
    134.                 //PRINTF("in_frm_num=%d mdl_frm_num=%d\r\n", in_frm_num,mdl_frm_num);
    135.                 return dis_err;
    136.         }
    137.         else
    138.         {
    139.                         // ¼ÆËãÔ¼ÊøƽÐÐËıßÐζ¥µãÖµ
    140.                 X1=(2*mdl_frm_num-in_frm_num)/3;
    141.                 X2=(4*in_frm_num-2*mdl_frm_num)/3;
    142.                 in=ftr_in->mfcc_dat;
    143.                 mdl=frt_mdl->mfcc_dat;

    144.                 dis=get_dis(in,mdl);
    145.                 x=1;
    146.                 y=1;
    147.                 step=1;
    148.                 do
    149.                 {
    150.                         up=(dtw_limit(x,y+1)==ins)?get_dis(mdl+mfcc_num,in):dis_err;
    151.                         right=(dtw_limit(x+1,y)==ins)?get_dis(mdl,in+mfcc_num):dis_err;
    152.                         right_up=(dtw_limit(x+1,y+1)==ins)?get_dis(mdl+mfcc_num,in+mfcc_num):dis_err;
    153.                         
    154.                         min=right_up;
    155.                         if(min>right)
    156.                         {
    157.                                 min=right;
    158.                         }
    159.                         if(min>up)
    160.                         {
    161.                                 min=up;
    162.                         }
    163.                         
    164.                         dis+=min;
    165.                         
    166.                         if(min==right_up)
    167.                         {
    168.                                 in+=mfcc_num;
    169.                                 x++;
    170.                                 mdl+=mfcc_num;
    171.                                 y++;
    172.                         }
    173.                         else if(min==up)
    174.                         {
    175.                                 mdl+=mfcc_num;
    176.                                 y++;
    177.                         }
    178.                         else
    179.                         {
    180.                                 in+=mfcc_num;
    181.                                 x++;
    182.                         }
    183.                         step++;        
    184.                         //PRINTF("x=%d y=%d\r\n",x,y);
    185.                 }
    186.                 while((x<in_frm_num)&&(y<mdl_frm_num));
    187.                 //PRINTF("step=%d\r\n",step);
    188.         }
    189.         return (dis/step);//²½³¤¹éÒ»»¯
    190. }


    191. void get_mean(int16_t *frm_ftr1, int16_t *frm_ftr2, int16_t *mean)
    192. {
    193.         uint8_t         i;

    194.         for(i=0;i<mfcc_num;i++)
    195.         {
    196.                 mean[i]=(frm_ftr1[i]+frm_ftr2[i])/2;
    197.                 PRINTF("x=%d y=%d ",frm_ftr1[i],frm_ftr2[i]);
    198.                 PRINTF("mean=%d\r\n",mean[i]);
    199.         }
    200. }

    201. /*        
    202.         ´ÓÁ½ÌØÕ÷ʸÁ¿»ñÈ¡ÌØÕ÷Ä£°å
    203.         ²ÎÊý
    204.         ftr_in1        :ÊäÈëÌØÕ÷Öµ
    205.         ftr_in2        :ÊäÈëÌØÕ÷Öµ
    206.         ftr_mdl        :ÌØÕ÷Ä£°æ
    207.         ·µ»ØÖµ
    208.         dis                :ÀÛ¼ÆÆ¥Åä¾àÀë
    209. */

    210. uint32_t get_mdl(v_ftr_tag *ftr_in1, v_ftr_tag *ftr_in2, v_ftr_tag *ftr_mdl)
    211. {
    212.         uint32_t dis;
    213.         uint16_t x,y;
    214.         uint16_t step;
    215.         int16_t *in1;
    216.         int16_t *in2;
    217.         int16_t *mdl;
    218.         uint32_t up,right,right_up;
    219.         uint32_t min;
    220.         
    221.         in_frm_num=ftr_in1->frm_num;
    222.         mdl_frm_num=ftr_in2->frm_num;
    223.         
    224.         if((in_frm_num>(mdl_frm_num*2))||((2*in_frm_num)<mdl_frm_num))
    225.         {
    226.                 return dis_err;
    227.         }
    228.         else
    229.         {
    230.                 // ¼ÆËãÔ¼ÊøƽÐÐËıßÐζ¥µãÖµ
    231.                 X1=(2*mdl_frm_num-in_frm_num)/3;
    232.                 X2=(4*in_frm_num-2*mdl_frm_num)/3;
    233.                 in1=ftr_in1->mfcc_dat;
    234.                 in2=ftr_in2->mfcc_dat;
    235.                 mdl=ftr_mdl->mfcc_dat;

    236.                 dis=get_dis(in1,in2);
    237.                 get_mean(in1, in2, mdl);
    238.                 x=1;
    239.                 y=1;
    240.                 step=1;
    241.                 do
    242.                 {
    243.                         up=(dtw_limit(x,y+1)==ins)?get_dis(in2+mfcc_num,in1):dis_err;
    244.                         right=(dtw_limit(x+1,y)==ins)?get_dis(in2,in1+mfcc_num):dis_err;
    245.                         right_up=(dtw_limit(x+1,y+1)==ins)?get_dis(in2+mfcc_num,in1+mfcc_num):dis_err;
    246.                         
    247.                         min=right_up;
    248.                         if(min>right)
    249.                         {
    250.                                 min=right;
    251.                         }
    252.                         if(min>up)
    253.                         {
    254.                                 min=up;
    255.                         }
    256.                         
    257.                         dis+=min;
    258.                         
    259.                         if(min==right_up)
    260.                         {
    261.                                 in1+=mfcc_num;
    262.                                 x++;
    263.                                 in2+=mfcc_num;
    264.                                 y++;
    265.                         }
    266.                         else if(min==up)
    267.                         {
    268.                                 in2+=mfcc_num;
    269.                                 y++;
    270.                         }
    271.                         else
    272.                         {
    273.                                 in1+=mfcc_num;
    274.                                 x++;
    275.                         }
    276.                         step++;        
    277.                         
    278.                         mdl+=mfcc_num;
    279.                         get_mean(in1, in2, mdl);
    280.                         
    281.                         PRINTF("x=%d y=%d\r\n",x,y);
    282.                 }
    283.                 while((x<in_frm_num)&&(y<mdl_frm_num));
    284.                 PRINTF("step=%d\r\n",step);
    285.                 ftr_mdl->frm_num=step;
    286.         }
    287.         return (dis/step); //²½³¤¹éÒ»»¯
    288. }

    复制代码
    还是乱码,先不管了,等全部完成了 ,大家如果有兴趣,我就把代码共享出来。(代码也是来自于网络,我做了些修改)

    编译都能通过了。后边找时间把整个流程串起来,就实现简单的语音识别了。
    finaltask_compile.jpg

    今天9月9号周末,终于可以有时间继续未完的任务,拖拉了很久争取这周完成
    先回忆一下我们用的这块板子的主要的2个特性,flash是256k,ram是192k,ram够大了,这样我就考虑简单点的办法,直接把单个语音模板(即只录制一个数字语音)放到缓存里,待识别的语音也放到缓存里,分别经过VAD,MFCC处理,然后再用DTW去比较识别。通过串口反馈回结果为真并控制相应的led亮起来。


    经过几天努力,终于有个了雏形,识别率不是很高。后边再慢慢的改进吧。操作步骤如下:


    1 .首先下载编译好的hex到板子
    2.程序里定义了key0用于录制语音模板,key1用于录制语音,用以识别
    录制语音的时候,0~9数字,要多重复说几次,例如,我录制模板时候,我想录制数字3,按下key0,串口助手会提示开始录制,那么尽可能重复说3这个数字。要不然会提示录制模板失败,主要是录制时间短,还没控制好开始和结束(后边想想办法利用hwvad回调函数),同样当按下key1开始语音识别的时候,也尽可能多重复说数字3,这样就可以提高识别率,如下图串口终端所示
    recognite.jpg

    程序里也要给这个语音模板定义3,这样识别正确才会返回数字3,也可以是其他数字,一样的操作方式
    number.jpg

    识别结果正确,相应的红色的led3亮起
    QQ图片20170919023811.jpg

    综述,仅仅实现了,单个数字录制模板,单个数字语音识别,其实单字识别也可以的。期望能起到抛砖引玉的作用。程序我后续整理一下放上来,个人水平有限,应该可优化的地方很多,期望社区大神来补充完善。至此最终任务基本完成了。hex文件 dmic_hwvad.zip (20.95 KB, 下载次数: 56)
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情

    2018-2-28 16:09
  • 签到天数: 65 天

    [LV.6]常住居民II

    8

    主题

    238

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    488
    最后登录
    2019-9-18
    发表于 2017-8-14 16:24:02 | 显示全部楼层
    厉害了!!
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    2021-9-9 22:51
  • 签到天数: 415 天

    [LV.9]以坛为家II

    79

    主题

    3088

    帖子

    21

    金牌会员

    Rank: 6Rank: 6

    积分
    5181
    最后登录
    2022-5-23
    发表于 2017-8-14 16:47:04 来自手机 | 显示全部楼层
    吊爆了~~~
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2023-9-15 08:42
  • 签到天数: 1952 天

    [LV.Master]伴坛终老

    1

    主题

    4686

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    9405
    最后登录
    2023-9-15
    发表于 2017-8-14 20:08:33 | 显示全部楼层
    厉害!!!
    今天天气不错!签到!
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2 小时前
  • 签到天数: 815 天

    [LV.10]以坛为家III

    71

    主题

    2438

    帖子

    24

    金牌会员

    Rank: 6Rank: 6

    积分
    5494
    最后登录
    2024-4-19
     楼主| 发表于 2017-8-15 12:54:29 | 显示全部楼层
    感谢各位大神捧场
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    前天 16:11
  • 签到天数: 2148 天

    [LV.Master]伴坛终老

    17

    主题

    4599

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    9740
    最后登录
    2024-4-17
    发表于 2017-9-19 08:47:56 | 显示全部楼层
    膜拜orz~~~~~
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2019-7-14 14:05
  • 签到天数: 484 天

    [LV.9]以坛为家II

    8

    主题

    854

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1991
    最后登录
    2019-7-14
    发表于 2017-9-19 09:01:11 | 显示全部楼层
    厉害了我的哥(不会是姐吧?)
    顶礼膜拜大神中
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2024-4-3 10:59
  • 签到天数: 329 天

    [LV.8]以坛为家I

    3

    主题

    1386

    帖子

    1

    金牌会员

    Rank: 6Rank: 6

    积分
    5339
    最后登录
    2024-4-16
    发表于 2017-9-19 09:50:33 | 显示全部楼层
    厉害了,回去拿板子试试
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    慵懒
    2019-7-14 14:05
  • 签到天数: 484 天

    [LV.9]以坛为家II

    8

    主题

    854

    帖子

    0

    金牌会员

    Rank: 6Rank: 6

    积分
    1991
    最后登录
    2019-7-14
    发表于 2017-9-19 09:51:49 | 显示全部楼层
    “大家如果有兴趣,我就把代码共享出来”
    大神,很有兴趣
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2019-2-11 11:14
  • 签到天数: 345 天

    [LV.8]以坛为家I

    18

    主题

    1317

    帖子

    4

    金牌会员

    Rank: 6Rank: 6

    积分
    3076
    最后登录
    2023-3-16
    发表于 2017-9-19 10:32:23 | 显示全部楼层
    学习了,感谢楼主分享!!!!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-19 12:48 , Processed in 0.165167 second(s), 29 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表