语音识别技术(AutoSpeechRecognize,简称ASR),就是把人的自然语言音频数据转换成文本数据的技术。理论上在线ASR是可以把所有的语音转换成对应的文本,但是我们这里只是介绍离线ASR的应用,就是只在本地识别个数受限制的语音。这里的离线ASR识别就是离线的命令词识别,又可以叫语法识别,它是基于语法规则,可以将与语法一致的自然语言音频转换为文本输出的技术。语法识别的结果值域只在语法文件所列出的规则里,故有很好的匹配率。另外,语法识别结果携带了结果的置信度,应用可以根据置信分数,决定这个结果是否有效。语法识别多用于要更准确结果且有限说法的语音控制,如在家庭环境中空调、电视、电灯的控制。因为这些设备的只有几个确定的常用固定功能。所以做出离线的识别就非常适合,这里在使用离线语法识别时,需要先编写一个语法文件,然后通过调用QISRBuildGrammar接口编译本地语法文件,以及获得语法ID,并在会话时,传入语法ID,以使用该语法,在之后的会话中,继续使用此语法进行识别,无需再次构建。
当下载好SDK后,我们就可以将其发送到树莓派板子上,大家可以使用如下的命令进行操作:
scpLinux_aitalk1226_5d5b***.zipcorvin@192.168.*.*:~/
这里需要注意的是发送的文件名和树莓派板的IP地址,大家需要根据自己的情况来做相应修改即可,如下图所示:
在树莓派上我们首先需要将离线命令词识别的SDK压缩包解压,完整的解压命令如下:
unzip-qLinux_aitalk1226_5d5b9efd.zip-dxf_aitalk/
接下来就是修改一下Makefile文件了,因为在make.sh中我们修改了编译时加载库的路径。主要就是修改13行的LDFLAGS,最终修改后的Makefile文件如下:
#commonmakefileheaderDIR_INC=../../includeDIR_BIN=../../binDIR_LIB=../../libsTARGET=asr_offline_record_sampleBIN_TARGET=$(DIR_BIN)/$(TARGET)CROSS_COMPILE=CFLAGS=-g-Wall-I$(DIR_INC)LDFLAGS:=-L$(DIR_LIB)/LDFLAGS+=-lmsc-lrt-ldl-lpthread-lasound-lstdc++OBJECTS:=$(patsubst%.c,%.o,$(wildcard*.c))$(BIN_TARGET):$(OBJECTS)$(CROSS_COMPILE)gcc$(CFLAGS)$^-o$@$(LDFLAGS)%.o:%.c$(CROSS_COMPILE)gcc-c$(CFLAGS)$<-o$@clean:@rm-f*.o$(BIN_TARGET).PHONY:clean#commonmakefilefoot在修改好make.sh和Makefile文件后,我们就可以来开始编译程序了,编译过程也很简单,我们直接在源码目录下执行make.sh脚本就可以了:
当我们执行完make.sh编译脚本后,就可以在bin目录下生成可执行文件asr_offline_record_sample。但是在执行之前,我们需要认识一下这个巴科斯范式格式的语法文件,因为这个语法文件就是我们后面要离线识别的命令词了。如下图所示:
在了解了语法文件后,我们还需要查看下示例源码,因为这里的语法文件中的规则,我们是可以动态的更新的。就是说我们不用重新启动程序,就可以实时的修改检测的语法规则,更新语法规则的API如下:
那接下来看一下在asr_offline_record_sample.c源码中是如何更新本次语法词典文件的,调用该API的代码如下:
那在了解整个示例程序的流程后,我们就可以来测试离线命令词识别的效果了,如下视频所示:
前面的一节介绍的是科大讯飞提供的示例代码,在了解背后的原理后,我们就可以来自己修改源码,实现自己想要的功能了。那我们在这里可以利用离线命令词识别这个功能,实现一个家庭里电灯的控制。由于我们的语音板上自带有一个可以编程控制的LED灯,那么我们就把这颗LED灯当做家里的电灯来使用。这样我们利用离线命令词识别,就可以实现一个电灯控制的示例了。那首先第一件事,我们就是要创建我们自己的语法文件了。在编写语法文件之前,我们还是要先来学习一下这种巴科斯范式的语法规则。大家可以在SDK源码目录下的docs中找到该完整的手册,我这里给大家简单介绍下需要的知识点,如果要想学习完整的语法编写规则还是要好好的学习下文档。
1.记号:对应英文为Token,其描述了用户语音所对应的文本内容,类似于“说法“,如“中国|美国”表示支持中国|美国两个记号的并列。
2.语义:对应英文为Sementic,表示用户说法所对应的用户所关心的内容,在应用开发中,可以将部分语义内置于语法文本中,以方便应用程序的处理。如在语音拨号的场景中,记号对应为“火警”,而语义为“119”。通过在语法中定义“火警”的语义,当用户说法为“火警”时,识别引擎将“119”返回给应用程序,而无须在应用程序内部再进行文本的解释工作,大大方便了应用程序的开发。
3.规则:规则定义了一系列记号及其相互关系的集合,且可以包含其它子规则。通过指定规则的唯一的名称,使其它的规则可以通过名称引用该规则。
4.槽:槽是一种特殊的规则。槽描述了一系列记号的并列关系,且不包含任何子规则。利用AitalkSDK,用户可以在程序运行过程中实时增减槽中的记号。借用此功能,用户通过定义槽,描述语法中的频繁变化的内容,如通讯录中的人名,而无须更改语法文件。
在这里我们编写一个自己的语法识别文件,我们命名为control.bnf,具体的内容如下:
#BNF+IAT1.0UTF-8;!grammarcontrol;!slot
最后我们来测试一下,看看我们自己的语法文件能否来控制灯和风扇,在这里的风扇控制我是外接了一个继电器模块,通过树莓派上的GPIO.25来控制继电器的通断,从而可以控制风扇的通电、断电。可以先看一下测试设备,如下图所示:
下面来看视频演示效果:
在这里需要注意的是离线语音识别返回的结果格式,这里返回的是xml格式的结果。其实还可以修改为json格式,大家可以根据自己的编程习惯来选择对应的来解析。我这里默认选择的是xml格式,那反馈的xml格式结果就是如下图所示这样:
如果你想要反馈json格式的结果该怎么修改代码呢?这里就需要修改下run_asr()函数中调用语音识别时候的参数了,如下图所示:
假如修改为json格式,那反馈的结果是什么样子呢?如下所示:
voidcontrolDevice(intdeviceID,intflag){if(LED_DEVICE==deviceID)//controlledlight{if(ON==flag)//lighton{digitalWrite(LED_PIN,LOW);}else//lightoff{digitalWrite(LED_PIN,HIGH);}}else//controlfandevice{if(ON==flag)//fanon{digitalWrite(FAN_PIN,HIGH);}else//fanoff{digitalWrite(FAN_PIN,LOW);}}}0x05测试源码下载我已经将所有的测试代码都上传到AIVoiceSystem这个代码仓库中,大家可以从以下链接中找到对应的代码,所有这些测试代码都是放在example目录下:
当前在代码仓库中有两个分支,一个是master分支,主要是为非树莓派4板子使用的。另外一个分支是pi4-devel,这个是专门为树莓派4新开的分支代码。如果大家使用的是ROS小课堂发布的树莓派4的ROS系统镜像的话,那就可以使用这个分支的代码。