AIoT应用创新大赛-基于TencentOS Tiny 的本地关键词识别

2022-03-17 09:41:14 浏览数 (1)

概述

随着深度学习的不断发展,生活中各种随处可见的问题都可以利用很多网络来解决。一个训练好的神经网络作为一个黑箱,直接输入原始数据就能够得到对应的结果,在很多直接通过传统算法不好解决的问题中,利用网络却往往较为简单。但是大部分网络都是在x86的平台上进行训练和部署,且其资源占用也比较大,较难以直接搬到资源紧张的嵌入式平台上。这其中就包括关键词识别问题,该问题如果利用传统算法实现起来较为困难,但是通过神经网络却能够很好的解决。

主要功能

  • 本地实时采集音频信号,方便收集数据训练网络
  • 读取文件系统中的音频数据,用于网络模型的推理,得到对应的关键词
  • 实时读取麦克风的音频数据,并通过网络模型推理出对应的关键词

硬件

TencentOS Tiny AIoT开发套件TencentOS Tiny AIoT开发套件

TencentOS Tiny AIoT开发套件:

  • 内置TencentOS Tiny开源物联网操作系统。
  • 核心板采用的RT1062处理器属于i.MX RT 系列 MCU,是由 NXP 推出的跨界处理器,跨界是指该系列MCU的定位既非传统的微控制器、也非传统的微处理器,i.MX RT 系列 MCU 则综合了两者的优势,既具备高频率(最高主频600M)、高处理性能,也具备中断响应迅速、实时性高的特点。
  • 1M RAM 16M SDRAM 64MB qspi flash 128MB spi flash。
  • 板载Type-C接口CMSIS DAP仿真器。
  • 板载PCIE接口,可扩展4G类物联网模组。
  • 板载物联网俱乐部WAN Interface接口,可支持NB-IoT、WiFi、4G cat1、LoRa等模组。
  • 板载物联网俱乐部E53 Interface接口,可扩展全系E53传感器。
  • 板载标准24P DVP摄像头接口,可支持最高500万像素摄像头。
  • 板载RGB显示接口,可转换HDMI输出。
  • 板载高性能音频解码芯片,可做语音识别测试。
  • 预留SD卡、用户按键、SPI Flash。

由于板载咪头出厂时存在问题,自己修改并换过多个咪头后仍旧无法获取音频信号,故通过MAX9814模块 ADC直接采集音频信号:

MAX9814模块MAX9814模块

原理

通过语音信号对关键词进行识别的主要原理是通过获取音频信号的梅尔频率倒谱系数(Mel-Frequency Cepstral Coefficients, MFCC),并利用神经网络将MFCC特征视为图像进行分类即可。如下图所示即为某一音频的MFCC特征图:

MFCC特征图样例MFCC特征图样例

在本项目中,所使用的音频数据以及采集的信号格式均为16bit,16kHz,单通道。MFCC特征的窗口大小为31.25ms,在本项目中的音频数据格式下,对应的数据长度为512点。对应的窗口重叠区域为50%,即16.125ms。

本项目所选用的网络为Keyword Spotting Through Image Recognition,其主要由Conv2D组成,对应的网络框架如下所示:

网络框架网络框架

而板子上实际的网络模型是通过上述修改后的,减去了一层CNN,其结构如下:

实际模型结构实际模型结构

软件架构

本项目在MCU端的主要功能主要由以下三个命令实现:

主要命令主要命令

其中:

  • record命令通过传入文件名和记录时间来实时记录当前麦克风的音频数据到文件中
  • kws命令主要通过传入的文件名来打开文件系统上对应的文件,并将其传给kws线程来实现主要的关键词推理
  • rt_kws命令通过实时获取麦克风数据,并通过事件集和双缓冲来将该数据送到kws线程中来实现关键词识别

而主要的推理线程kws的流程图如下所示:

kws线程流程图kws线程流程图

而PC端主要通过TensorFlow来搭建和训练模型,其中数据集主要采用的Google speech command dataset。其中主要包括以下的语音数据:

代码语言:txt复制
'backward', 'bed', 'bird', 'cat', 'dog', 'down', 'eight', 'five', 'follow', 'forward',
'four','go','happy','house','learn','left','marvin','nine','no','off','on','one','right',
'seven','sheila','six','stop','three','tree','two','up','visual','yes','zero'

针对上述kws线程的核心代码如下所示:

代码语言:javascript复制
mfcc = mfcc_create(MFCC_COEFFS_LEN, MFCC_COEFFS_FIRST, MFCC_TOTAL_NUM_BANK, AUDIO_FRAME_LEN, 0.97f, true);

while(1)
{
	if(use_file_flag == 0)
	{
		tos_event_pend(&audio_evt, 1|2|4, &evt, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);

		if(evt & 4)
			break;
		if(evt & 1)
			p_raw_audio = dma_audio_buffer;
		else if(evt & 2)
			p_raw_audio = &dma_audio_buffer[AUDIO_FRAME_LEN];

	}else
	{
		res = f_read(fp, dma_audio_buffer, AUDIO_FRAME_LEN*sizeof(int16_t), &br);
		if (res != FR_OK)
		{
			PRINTF("Failure:%srn", RESAULT_TO_STR(res));
			f_close(fp);
			return ;
		}
		if (br == 0)
			break;
		p_raw_audio = dma_audio_buffer;
	}
	// memory move
	// audio buffer = | 256 byte old data |   256 byte new data 1 | 256 byte new data 2 |
	//                         ^------------------------------------------|
	memcpy(audio_buffer_16bit, &audio_buffer_16bit[AUDIO_FRAME_LEN], (AUDIO_FRAME_LEN/2)*sizeof(int16_t));

	// convert it to 16 bit.
	// volume*4
	for(int i = 0; i < AUDIO_FRAME_LEN; i  )
	{
		audio_buffer_16bit[AUDIO_FRAME_LEN/2 i] = p_raw_audio[i];
	}

	for(int i=0; i<2; i  )
	{
		mfcc_compute(mfcc, &audio_buffer_16bit[i*AUDIO_FRAME_LEN/2], mfcc_features_f);
		quantize_data(mfcc_features_f, mfcc_features[mfcc_feat_index], MFCC_COEFFS, 3);

		mfcc_feat_index  ;
		if(mfcc_feat_index >= MFCC_LEN)
			mfcc_feat_index = 0;
	}

	uint32_t len_first = MFCC_FEAT_SIZE - mfcc_feat_index * MFCC_COEFFS;
	uint32_t len_second = mfcc_feat_index * MFCC_COEFFS;
	memcpy(&mfcc_features_seq[0][0], &mfcc_features[0][0]   len_second,  len_first);
	memcpy(&mfcc_features_seq[0][0]   len_first, &mfcc_features[0][0], len_second);

	memcpy(nnom_input_data, mfcc_features_seq, MFCC_FEAT_SIZE);
	nnom_predict(model, &label, &prob);

	// output
	if(prob > 0.5f)
	{
		PRINTF("time : %d ms t",tos_tick2millisec(tos_systick_get()) - last_time);
		PRINTF("%s : %d%%rn", (char*)&label_name[label], (int)(prob * 100));
	}
	last_time = tos_tick2millisec(tos_systick_get());
}
if(use_file_flag)
	f_close(fp);

mfcc_delete(mfcc);

可以看到其基本逻辑为:

1. 创建mfcc对象用于生成mfcc特征图

2. 等待从文件或者麦克风过来的数据。如果是文件则直接调用fatfs的api,如果是麦克风则通过事件集等待

3. 利用mfcc对象将音频数据生成mfcc特征图

4. 将特征图送入模型输入

5. 打印输出模型结果

6. 没有音频信号后,删除mfcc对象,释放必要空间,kws线程结束

运行结果

针对上述部分的命令行截图如下:

1. 在程序启动时会自动编译模型,使得之后的推理无需再次编译,缩短推理时间

启动部分启动部分

2. kws命令用于读取板子上插入的sd卡中的文件,输出其wav的基本格式信息,并送入模型进行推理

kws命令kws命令

3. rt_kws命令为通过内部ADC实时采集麦克风信号,并送入网络中进行推理

rt_kws命令rt_kws命令

4. record命令主要用于采集板子上麦克风的音频信号,用于训练网络使用。同时也可以配合kws命令一起使用

record命令record命令

PPT

AIoT应用创新大赛-基于TencentOS Tiny 的本地关键词识别.pptx

视频

演示demo

代码

tencentos_kws: Keyword spotting based on TencentOS Tiny

0 人点赞