三个小白是如何在三个月内搭一个基于kaldi的嵌入式在线语音识别系统的

article/2025/11/8 19:04:34

前面的博客里说过最近几个月我从传统语音(语音通信)切到了智能语音(语音识别)。刚开始是学语音识别领域的基础知识,学了后把自己学到的写了PPT给组内同学做了presentation(语音识别传统方法(GMM+HMM+NGRAM)概述)。一段时间后老板就布置了具体任务:在我们公司自己的ARM芯片上基于kaldi搭建一个在线语音识别系统,三个人花三个月左右的时间完成。由于我们都是语音识别领域的小白,要求可以低些,就用传统的GMM-HMM来实现。说实话接到这个任务我们心里是有点没底的,不知道能不能按时完成,毕竟我们对语音识别不熟,对kaldi不熟。既然任务下达了,硬着头皮也要上,并尽最大努力完成。我本能的先在网上用百度/google搜了搜,看有没有一些经验可供参考,好让我们少走弯路。遗憾的是没搜到有价值的东西。没办法,我们只能根据自己以前的经验摸索着前进。最终我们按计划花了不到三个月的时间完成了嵌入式平台上在线语音识别系统的搭建。虽然只是demo,但是为后面真正做商用的产品打下了良好的基础,累积了不少的经验。今天我就把我们怎么做的分享出来,给也想做类似产品的朋友做个参考。

既然作为一个项目来做,就要有计划,分几个阶段完成这个项目。我在学习语音识别基础知识时对kaldi有一个简单的了解(在做语音识别前就已知kaldi的大名,没办法这几年人工智能(AI)太热了。智能语音作为人工智能的主要落地点之一,好多都是基于kaldi来实现的。我是做语音的,自然会关注这个热门领域的动态)。根据对kaldi的简单了解,我把项目分成了三个阶段,第一阶段是学习kaldi,对kaldi有一个更深的认识,同时搞清楚基于kaldi做方案后面有哪些事情要做,计划花一个月左右的时间完成。第二阶段是设计软件架构、写代码、训练模型等,也是花一个月左右的时间完成。第三阶段是调试,提升识别率,还是花一个月左右的时间完成。计划的时间会根据实际情况做微调。

1,第一阶段

第一阶段就是学习kaldi。由于我们是三个人做这个项目,我就把学习任务分成三块:数据准备和MFCC、GMM-HMM模型训练、解码网络创建和解码。在其他两位同学挑好模块后剩下的解码网络创建和解码就有我来学习了。学习过程就是看网上文章、看博客和看kaldi代码、脚本的过程。学完后大家搞清楚了后面有哪些事情要做,同时做了PPT给组内同学讲,让大家共同提高。解码相关的见我前面的文章(基于WFST的语音识别解码器 )。Kaldi中解码有两种类型:offline(多用于模型调试等)和online(多用于在线识别等),其中online也有两种方式,一种是通过PortAudio从MIC采集语音数据做在线语音识别,另一种是通过读音频WAV文件的方式做在线语音识别。我们要做的是在线语音识别,这两个就是很好的参考,尤其是通过PortAudio从MIC采集方式的,很有必要弄明白运行机制。于是我根据网上的博客基于thchs30搭建了PC上的在线识别来调试,基本上搞清楚了代码的运行机制。Kaldi中设定采样率为16kHZ,每帧25ms(其中帧移10ms),每27帧为一组集中做MFCC特征提取和解码,这样处理一组的语音时长是285ms(25+(27-1)*10=285),共4560(16*285=4560)个采样点。每次处理完一组后就从buffer中再取出一组做MFCC和解码,解码后看有没有识别的字出来,有的话就打印出来。

2,第二阶段 

第一阶段主要是学习,第二阶段就要真正干活了。我们在Linux上开发,先制定系统搭建完成后的目标:设备用数据线连在PC上,能在线实时识别英文数字0—9(选识别这些是因为网上有现成的英国人说的音频源,我们可以省去录音频源的工作,好节约时间),即人对着设备说出英文数字0—9后PC屏幕上能实时打印出来,识别率接近GMM-HMM模型下的较好值。大家的任务还是沿袭第一阶段的。学习数据准备和MFCC的同学先数据准备相关的工作,如标注等,好给模型训练的同学用,然后移植kaldi中MFCC相关的代码。学习模型训练的同学先开始模型训练的准备工作,等要准备的数据好了后就开始训练。我负责整个软件架构的设计,同时还要把kaldi中的绝大部分(除了MFCC)移植进我们系统中。通过对kaldi的学习,使我对怎么设计这个在线语音识别的软件架构有了更深的认识。语音识别分两个阶段,即训练阶段和识别阶段。训练阶段就是得到模型给识别阶段用。它相对独立,我们就基于kaldi来训练模型,最终得到final.mdl等文件给识别阶段的软件用(在初始化时读取这些文件得到解码网络)。识别阶段的软件主要分两部分,声音采集和识别(包括特征提取和解码)。这样系统就有两个thread,一个是声音采集thread(audio capture thread),它基于ALSA来做,负责声音的采集和前处理(如噪声抑制),另一个是识别thread(kaldi process thread),负责MFCC和解码。两个thread通过ring buffer交互数据,同时要注意数据的保护。这样系统的软件架构框图如下:

大家对软件架构讨论觉得没什么问题后我就开始写代码搭建软件框架了。在 Linux中创建thread等都是一些套路活。Audio capture thread里先做初始化,包括ALSA的配置以及前处理模块的初始化等。然后就每隔一定时间通过ALSA_LIB的API完成一次音频数据的采集工作,读完数据后就做前处理,处理好后把音频数据放进ring buffer中,同时激活kaldi process thread,让kaldi process thread开始干活。Kaldi thread也是先做一些初始化的工作,然后睡下去等待激活。激活后先从ring buffer里取语音数据,然后做MFCC和decoder。完成后又睡下去等待下次再被激活。搭建软件框架时kaldi相关的代码还没被移植进去,kaldi process thread里仅仅把从ring  buffer里拿到的语音数据写进PCM文件,然后用CoolEdit听,声音正常就说明软件框架基本成型了。刚开始时audio capture thread里也没加前处理模块,调试时把从ALSA里获取的数据写进PCM文件听后发现有噪声,就加了噪声抑制(ANS)模块。这个模块用的是webRTC里的。webRTC里的三大前处理模块(AEC/ANS/AGC)几年前我就用过,这次拿过来简单处理一下就用好了,去噪效果也挺好的。ANS一个loop是10ms,而前面说过kaldi里在线识别解码一次处理一组27帧是285ms,我就取两者的最小公倍数570ms作为audio capture thread的loop时间。从ALSA取到语音数据后分57(570/10 = 57)次做噪声抑制,再把抑制后的语音数据写进ring buffer。Kaldi thread激活后还是每次取出285ms语音数据做处理,只不过要取两次(570/285 = 2)。

软件架构搭好后就开始移植kaldi代码了。Kaldi代码量大,不可能也没必要全部移植到我们系统里,只需要移植我们需要的就可以了。怎样才能移植我们需要的代码呢?考虑后我用了如下的方法:先把在线解码相关的代码移植进去,然后开始不停的编译,报什么错提示缺什么就加什么,直到编译通过。这种方法保证了把需要的文件都移植进系统了,但有可能某些文件中的函数没用到,即到文件级还没到函数级。由于时间紧,这个问题就暂时不管了。移植过程更多的是一个体力活,需要小心细致。在移植过程中遇到问题就去网上搜,最后都圆满解决了。Kaldi主要用到了三个开源库:openfst、BLAS、LAPACK。BLAS和LAPACK我用的常规方法,即到官网上下载编译后生成库,然后把库和头文件放到系统的”/usr/lib”和“/use/include”下,让其他代码用。kaldi支持的有BALS库有 ATLAS / CLAPACK / openBLAS / MKL等。在X86的Ubuntu PC上跑kaldi时就用的Intel的MKL,在ARM上就不能用了,需要用其他的几种之一。我评估下来用了openBLAS,主要因为三点:1)它是BSD的;2)它支持多种架构(ARM/X86/MIPS/….),是开源库里性能最好的(各种架构里都嵌了很多的汇编代码),被多家著名公司使用,如IBM/ARM/nvidia/huawei等;3)它有多个编译选项可供选择,比如单线程/多线程选择、设定线程数等。BLAS的早期代码都是用fortran写的,后来用C对其进行了封装,所以系统还要加上对fortran的支持。对openFST,我发现用到的代码并不多,也就没用常规的方法,而是直接把用到的代码移植进系统。我移植好编译没问题后另一个同学把剩下的MFCC以及和ALSA接口(用ALSA接口替代kaldi里的PortAudio接口)相关的也移植进去了。这样移植工作就算结束了。对比了下移植进系统的kaldi代码和kaldi里SRC下的代码,应该是只用了其中一小部分。下图显示了移植进系统的kaldi文件(没列出相关的头文件)。同时负责模型训练的同学也有了一个初步的模型生成的文件,把这些文件放进系统里就可以跑起来了,人说话后PC屏幕上就有词打印出来,不过不正确。这也正常呀,因为还没调试呢!

3,第三阶段

第三阶段就是调试。第二阶段结束后说话就有词出来,但都是错的,需要排查定位问题。在线语音识别系统从大的角度可以分两块:模型和代码实现。首先我们需要定位是模型的问题还是代码实现的问题,先从模型排查。在第一阶段时利用thchs30大致搞清楚了在线解码的机制,是用模型tri1调的,当时识别率很差。现在要关注识别率了,把模型换成了tri2b,识别率有所提高。这说明kaldi里的在线解码的代码是没有问题的,识别率差问题出在模型。况且全球这么多人在用kaldi,如果在线解码有问题应该早就fix了。所以我们决定把我们生成的模型文件放进thchs30里来验证模型是否有问题。为了排除从MIC输入的音频数据有噪声等的干扰,先用读文件的方式验证。把我们的模型文件放进去后发现基本识别不正确,这说明模型是有问题的。负责模型的同学去调查,发现用于训练的音源都是8K采样的,但是在线解码用的都是16K采样的,这是我们自己挖的坑,用重采样程序把8K的全部转成16K的,这个坑也就填好了,但是识别率依旧不好。又发现训练集全是英国人的发音,而测试集是我们中国人的发音,有一定口音的,最好用我们中国人自己的发音作为训练集。于是我们自己又录了用于训练的音源,为了加大训练的数据,又请好多其他人录了音源。训练后得到了新的模型,再放到thchs30里面验证,识别率有六七成了,这说明模型的大方向对了,为了提高识别率,模型还需要继续调试。

接下来就要看代码部分是否有问题了。把新生产的模型放进我们自己的系统,并且用从音频文件都数据的方式(我们的系统既可以从MIC采集数据也可以从音频文件读数据,从音频文件读数据是为了debug)来替代从MIC采集到的数据(这样做是为了排除噪声等因素的干扰)来看代码是否有问题。运行下来发现识别率依旧很差,这说明我们的代码也是有问题的。在第二阶段我已经调试过部分代码,确保了在kaldi process thread里从PCM ring buffer里拿到的音频数据是没有问题的。还有两方面需要调试,一是送进MFCC的PCM数据要是OK的,二是我们的在线解码机制要跟kaldi里的在线解码机制完全一样。一很快就调试好了。二是先再深入研究吃透kaldi里的在线解码机制,改正我们与它不一样的地方,经过两三天调试后识别率跟thchs30里的差不多了,这说明我们的代码经过调试后也有一个好的base了,后面就要开始调性能了。

前面是通过从音频文件中读取数据来做在线识别的,数据相对干净些。现在要从MIC读取音频数据做真正在线识别了,试下来后识别率明显偏低,这说明我们的前处理还没完全做好(前面调试时只加了ANS模块)。我把前处理后的音频数据dump出来用CoolEdit听,的确有时候音质不好,于是我又把webRTC中的AGC模块加上去,再次dump出前处理后的音频数据听,多次听后都感觉音质正常。再来运行加了AGC后的从MIC采集音频数据的在线识别,识别率果然有了明显的提升。前处理能做的都做了,要想再提高识别率,就要靠模型发力了。做模型的同学一边请更多的人录音源来训练,一边尝试各种模型,最终用的是tri4b,有了一个相对不错的识别率。由于我们用的是GMM-HMM,如今主流的语音识别中已不再使用,老板就觉得没有必要再调了,后面肯定会用主流的模型的,但是整个嵌入式上的在线语音识别软件代码尤其软件架构和音频采集还是有用的,后面就要基于这些代码做真正的产品。

对语音识别领域的资深人士来说,这个嵌入式在线语音识别系统还很稚嫩。但通过搭这个系统,让我们对语音识别领域有了多一点的感性认识,也有了一个良好的开端,给老板以信心,并且可以继续做下去。这次工程上的事情偏多,后面希望更深入的做下去,累积更多的语音识别领域的经验。搭这个系统没有任何可供参考的资料,纯粹是根据我们以往的经验摸索着搭出来的。做的产品可能不一样,但很多解决问题的思路都是一样的。如果有朋友也搭过嵌入式上的在线语音识别系统,欢迎探讨,搭出一个更好的在线语音识别系统。


http://chatgpt.dhexx.cn/article/xTgjYOEn.shtml

相关文章

【毕业设计】基于stm32的语音识别 - 单片机 嵌入式 物联网 语音识别

文章目录 0 简介1 项目目标2 项目背景意义3 需求分析3.1 功能性需求3.2 非功能性需求 4 设备器件4.1 STM32单片机4.2 LD3320语音识别芯片4.3 TFT-LCD液晶显示屏 5 设计方案6 语音识别模块的开发7 报警电路模块的开发8 上位机模块的开发9 项目成果9 最后 0 简介 Hi,…

基于80251的嵌入式语音识别

一、文档介绍 嵌入式语音识别技术在251内核的实现。 缩写、术语 解 释 Specific Person Isolated Word Speech Recognition 特定人孤立词语音识别 Endpoint detection 端点检测 Feature parameter extraction 特征参数提取 DTW (Dynamic Time Warping) 动态时间规整…

嵌入式linux 声控,基于Cortex-A9的嵌入式语音识别系统设计

打开文本图片集 摘 要:在人工智能技术发展的进程中,语音识别已经成为重要分支之一。语音识别技术作为人与机器的一种交互方式,将前端处理之后的信号转换成文本或者指令,进而用于控制相关智能设备。当前,基于深度学习的云端语音识别技术已经逐渐成熟,但考虑到离线设备无法…

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

3.1 Scratch简介 在开始本章之前,需要介绍下TWen-ASR-ONE的用户开发语言-Scratch,Scratch是麻省理工学院开发的一款简易图形化编程工具主要是为青少年开发的一种编程工具,它图形化编程工具当中最广为人知的一种形式,所有人都可以…

Android导航语音识别——语音听写(嵌入式)

嵌入式项目之Android导航语音识别——语音听写 文章目录 系列文章目录前言一、原理流程图二、语音听写 1.初始化无ui识别听写2.设置识别监听器3.启动功能总结 前言 最近在学习嵌入式系统的一个功能——导航语音识别,该功能是基于Android和科大讯飞语音识别语音合成…

static的三个作用

static作用 一、修饰局部变量-静态局部变量二、修饰全局变量-静态全局变量三、修饰函数-静态函数四、总结和注意点 一、修饰局部变量-静态局部变量 static修饰局部变量时,改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束&…

java中的静态变量的作用域_详解JAVA中static的作用

1、深度总结 引用一位网友的话,说的非常好,如果别人问你static的作用;如果你说静态修饰 类的属性 和 类的方法 别人认为你是合格的;如果是说 可以构成 静态代码块,那别人认为你还可以; 如果你说可以构成 静态内部类, 那别人认为你不错;如果你说了静态导包,那别人认为你…

express.static 作用及用法

express相信是很多人用nodejs搭建服务器的首选框架,相关教程有很多,也教会了大家来如何使用。如果你想更深的了解他的细节,不妨和我一起来研究一下。 先来看一个每个人都用到的方法app.use(express.static(__dirname/public)); 你肯定在你的a…

java static的作用

static是什么 static是java中的关键字,static表示”全局”、“静态”的意思,用来修饰成员变量和成员方法,定义的变量,我们通常将用static修饰的成员称为静态变量,静态方法或者静态成员。 static的作用 被static修饰…

static的作用有哪些?

C语言中的static的作用有:1.修饰局部变量。2.修饰全局变量。3.修饰函数。 1.修饰局部变量。 如图: 当只用int 定义变量a时,此时程序运行结果为: 程序运行结果为10个2,这是为什么呢?? 原因是此时的变量a为…

C语言中 static作用

在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条。隐藏保持数据持久初始化为0 该图是下面文字的总结: 1.先来介绍它的第一条也是最重要的一条:隐藏。 当我们同时编译多个文件时,所有未加static前…

C语言中static作用

1,static修饰局部变量 在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。 特点如下: 1)存储区:由于栈变为静态存储区rw data,生存期为整个源程序,只能在定义该变量的函数内使用。退出该函…

对DSP的基本了解(一)--DSP是什么?

Preface:在学习一个新东西之前,我们需要对一些基本概念或者专业术语进行基本的理解,这样才能扫除壁垒,让学习更加高效。因此,DSP学习系列首先会比较注重对DSP相关术语上的理解。所以最开始,我们先来学习一些…

DSP学习

DSP数字电源学习一 软件环境搭建相关硬件资源TMS320C28x 32 位 CPU可编程控制律加速器 (CLA)片上存储器时钟和系统控制1.2V 内核、3.3V I/O 设计系统外设通信外设模拟系统 软件相关控制算法数据记录功能代码示例MathWorks Simulink 模型文档 由于最近工作需要,需要学…

DSP/BIOS详解入门

DSP/BIOS详解入门 百度百科DSPBIOS DSP/BIOS是TI公司特别为其TMS320C6000TM,TMS320C5000TM和TMS320C28xTM系列DSP平台所设计开发的一个尺寸可裁剪的实时多任务操作系统内核,是TI公司的Code Composer StudioTM开发工具的组成部分之一。 DSP/BIOS 实时操作…

DSP基础知识回顾

文章目录 1. 什么是DSP2. DSP与其他主流芯片的区别2.1 DSP与MCU的区别2.2 DSP与ARM的区别2.3 DSP与FPGA的区别 3. TI公司DSP具体型号的含义4. TMS320x2814的片内外设4.1 事件管理器4.2 模拟量转换为数字量的ADC采样模块4.3 串行通信接口SCI4.4 串行外围设备接口SPI4.5 局域网通…

DSP学习 -- 前言

已经换工作20天了,感觉又是换行业了一样。跳入DSP和FPGA的大坑了。 除了C语言基础还算可以,其他要学的东西太多了。 也是,终于可以又回到那种能学习和吸取新鲜知识的节奏了。相对来说虽然很累,但是我还是比较喜欢这种状态。总比吃…

最强ADI SHARC系列DSP:ADSP-SC589的开发入门详解

作者的话 ADI的SHARC系列DSP,现阶段最强的型号是SC589,那么我就以SC589为例,写一点资料,让新手能够比较便捷的熟悉开发环境,软硬件,顺利的进入开发。 文档解决的是入门的问题,所以不会深入去写…

嵌入式开发之DSP学习

一、ARM、DSP、FPGA ARM具有比较强的事务管理功能,可以用来跑界面以及应用程序等,其优势主要体现在控制方面;ARM是32位的单片机,其内部硬件资源的性能较高,可以加载操作系统成为其主要特点,有了操作系统&am…

[培训-DSP快速入门-2]:C54x DSP处理器的架构

作者主页(文火冰糖的硅基工坊):https://blog.csdn.net/HiWangWenBing 本文网址:https://blog.csdn.net/HiWangWenBing/article/details/118889692 目录 1. C54x芯片的总体基本架构 1.1 冯诺依曼结构 1.2 哈佛结构 1.3 混合结构 1.4 C54x芯片结构 …