本文共 8692 字,大约阅读时间需要 28 分钟。
百度AI简介
语音识别服务是百度AI众多服务中的一项,应用该服务,你可以将语音识别为文字,适用于手机应用语音交互、语音内容分析、智能硬件、呼叫中心智能客服等多种场景。下面,我将分享一个基于RT-Thread将该服务应用于STM32上的案例。
本期分享来自RT-Thread的社区小伙伴霹雳大乌龙,如果你也有文章愿意分享/希望获得官方的写作指导,可以发送文章/联系方式邮件至邮箱:xuqianqian@rt-thread.com
一. 项目介绍
硬件:IoT Board(正点原子 - 潘多拉L475开发板)
平台:RT-Thread + 百度AI
使用RT-Thread的 stm32l475-atk-pandora BSP 或 RT-Thread IoT-Board SDK;
挂载elm FatFS文件系统,用于存放待识别音频;
初始化板载WIFI模块 AP6181 或 使用AT组件+ESP8266,使开发板具备网络功能;
使用Audio组件,实现录音功能,并将音频存入文件系统;
使用webclient软件包,将文件系统中的音频上传到百度AI服务端,识别后返回Json数据;
使用cJson软件包解析数据,根据解析出的数据作出响应动作(控制RGB灯);
将中文字库烧写进外部spi flash,使用SUFD+FAL软件包读写flash,实现LCD显示识别结果。
因为项目的重点以及难点在于百度语音识别,所以接下来的文章将着重讲解上述的4-7点,其他部分大家自行前往RT-Thread的文档中心学习。
二. 百度语音识别服务使用流程
在项目开始之前,我们需要先熟悉一遍百度语音服务的调用流程,不然直接写代码,你可能会一脸懵逼。百度语音识别简单来说就是百度AI通过API的方式给开发者提供一个通用的HTTP接口,开发者通过这个接口上传音频文件,服务器返回识别结果,就这么简单,具体怎么做我们接着往下看:
1. 首先我们要注册一个百度开发者账号,然后创建一个语音识别的应用
2.语音识别的过程
向授权服务地址https://aip.baidubce.com/oauth/2.0/token 发送请求(推荐使用POST),并在URL中带上以下参数:
grant_type:必须参数,固定为client_credentials;
client_id:必须参数,应用的API Key;
client_secret:必须参数,应用的Secret Key;
● 使用浏览器获取Access Token:
1https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=Gqt6jzFQDB1UrfVlkTBxr43k&client_secret=dsAQulSVgXEUq2xxyUGFegQOpUWVDpx2
● 使用Postman获取Access Token:
这里给大家推荐一个好用的软件Postman,其是一款功能强大的网页调试与发送网页HTTP请求的接口测试神器,使用它你可以方便的体验百度语音识别的完整流程:
有了Access Token,就可以开始进行语音识别了,我们采用raw方式,上传本地文件(使用官方提供的16k采样率pcm文件样例),还是用Postman来测试:
● 语音识别调用地址:https://vop.baidu.com/pro_api
● 语音数据的采样率和压缩格式在 HTTP-HEADER 里的Content-Type 表明,例:Content-Type: audio/pcm;rate=16000
● 填写URL参数
● 上传本地音频文件,发送识别,返回结果
自此完整的百度语音识别流程就结束了,更多详细内容,大家参考百度AI文档中心的相关部分:https://ai.baidu.com/docs#/ASR-API/77e2b22e
三. 动手实践
实践开始之前,先确保你已经做了以下两件事:
注册百度开发者账号,并创建了一个语音识别应用,而且成功获取了Access Token;
建立一个基于你自己的STM32平台的RT-Thread工程,它必须具备Finsh控制台,文件系统,网络功能(不明白的参见RT-Thread文档中心,网络功能推荐使用AT组件+ESP8266,因为这是最简单快捷的方法)
如果上面说的准备事项没有问题,那么请继续往下看:这次我们将先跳过录音功能,而使用事先准备好的音频文件进行语音识别并控制板载RGB灯(项目介绍的5,6点),所以你还需要准备一些音频文件,比如“红灯开”,“蓝灯关”。。。我是用手机录的音频,然后使用ffmpeg工具将音频转为百度语音官方认为最适合的16k采样率pcm文件,最后将这些音频文件放进sd卡中,我们的文件系统也是挂载在sd卡上的。
本次工程会用到RT-Thread的两个软件包:webclient 和 cJson 软件包,你需要使用ENV工具将这两个软件包添加进工程里。
● 添加webclient和cJson软件包
好了,开始写代码:
1/* bd_speech_rcg.c */ 2 3#include4#include 5 6#include //网络功能需要的头文件 7#include //webclient软件包头文件 8#include //文件系统需要的头文件 9#include //CJSON软件包头文件 10 11/* 使用外设需要的头文件 */ 12#include 13#include 14 15 16/* 获取RGB灯对应的引脚编号 */ 17#define PIN_LED_R GET_PIN(E, 7) 18#define PIN_LED_G GET_PIN(E, 8) 19#define PIN_LED_B GET_PIN(E, 9) 20 21#define RES_BUFFER_SIZE 4096 //数据接收数组大小 22#define HEADER_BUFFER_SIZE 2048 //最大支持的头部长度 23 24/* URL */ 25#define POST_FILE_URL "http://vop.baidu.com/server_api?dev_pid=1536&cuid=lxzzzzzxl&token=25.9119f50a60602866be9288f1f14a1059.315360000.1884092937.282335-15525116" 26/* 头部数据(必需) */ 27char *form_data = "audio/pcm;rate=16000"; 28 29/* 预定义的指令 */ 30char *cmd1 = "打开红灯"; 31char *cmd2 = "关闭红灯"; 32char *cmd3 = "打开蓝灯"; 33char *cmd4 = "关闭蓝灯"; 34char *cmd5 = "打开绿灯"; 35char *cmd6 = "关闭绿灯"; 36 37 38 39/************************************************ 40函数名称 :bd 41功 能 : 将音频文件发送到百度语音服务器,并接收响应数据 42参 数 : 音频文件名(注意在文件系统中的位置,默认根目录) 43返 回 值 :void 44作 者 :rtthread;霹雳大乌龙 45*************************************************/ 46void bd(int argc, char **argv) 47{ 48 char *filename = NULL; 49 unsigned char *buffer = RT_NULL; 50 int content_length = -1, bytes_read = 0; 51 int content_pos = 0; 52 int ret = 0; 53 54 /* 判断命令是否合法 */ 55 if(argc != 2) 56 { 57 rt_kprintf("bd \r\n"); 58 return; 59 } 60 61 /* 获取音频文件名 */ 62 filename = argv[1]; 63 64 /* 以只读方式打开音频文件 */ 65 int fd = open(filename, O_RDONLY, 0); 66 if(fd < 0) 67 { 68 rt_kprintf("open %d fail!\r\n", filename); 69 goto __exit; 70 } 71 72 /* 获取音频文件大小 */ 73 size_t length = lseek(fd, 0, SEEK_END); 74 lseek(fd, 0, SEEK_SET); 75 76 /* 创建响应数据接收数据 */ 77 buffer = (unsigned char *) web_malloc(RES_BUFFER_SIZE); 78 if(buffer == RT_NULL) 79 { 80 rt_kprintf("no memory for receive response buffer.\n"); 81 ret = -RT_ENOMEM; 82 goto __exit; 83 } 84 85 /* 创建会话 */ 86 struct webclient_session *session = webclient_session_create(HEADER_BUFFER_SIZE); 87 if(session == RT_NULL) 88 { 89 ret = -RT_ENOMEM; 90 goto __exit; 91 } 92 93 /* 拼接头部数据 */ 94 webclient_header_fields_add(session, "Content-Length: %d\r\n", length); 95 webclient_header_fields_add(session, "Content-Type: %s\r\n", form_data); 96 97 /* 发送POST请求 */ 98 int rc = webclient_post(session, POST_FILE_URL, NULL); 99 if(rc < 0)100 {101 rt_kprintf("webclient post data error!\n");102 goto __exit;103 }else if (rc == 0)104 {105 rt_kprintf("webclient connected and send header msg!\n");106 }else107 {108 rt_kprintf("rc code: %d!\n", rc);109 }110111 while(1)112 {113 rt_memset(buffer, 0, RES_BUFFER_SIZE);114 length = read(fd, buffer, RES_BUFFER_SIZE);115 if(length <= 0)116 {117 break;118 }119 ret = webclient_write(session, buffer, length);120 if(ret < 0)121 {122 rt_kprintf("webclient write error!\r\n");123 break;124 } 125 rt_thread_mdelay(100);126 }127 close(fd);128 rt_kprintf("Upload voice data successfully\r\n");129130 if(webclient_handle_response(session) != 200)131 {132 rt_kprintf("get handle resposne error!");133 goto __exit;134 } 135136 /* 获取接收的响应数据长度 */137 content_length = webclient_content_length_get(session);138 rt_thread_delay(100);139140 do141 {142 bytes_read = webclient_read(session, buffer, 1024);143 if (bytes_read <= 0)144 {145 break;146 }147148 for(int index = 0; index < bytes_read; index++)149 {150 rt_kprintf("%c", buffer[index]);151 }152153 content_pos += bytes_read;154 }while(content_pos < content_length); 155156 /* 解析json数据 */157 bd_data_parse(buffer);158159 __exit:160 if(fd >= 0)161 close(fd);162 if(session != NULL)163 webclient_close(session);164 if(buffer != NULL)165 web_free(buffer);166167 return;168}169170/* 导出为命令形式 */171MSH_CMD_EXPORT(bd, webclient post file);
1/* bd_speech_rcg.c */ 2 3/************************************************ 4函数名称 :bd_data_parse 5功 能 : 解析json数据,并作出响应动作 6参 数 :data ------ 百度语音服务返回的数据(json格式) 7返 回 值 :void 8作 者 :RT-Thread;霹雳大乌龙 9*************************************************/10void bd_data_parse(uint8_t *data)11{12 cJSON *root = RT_NULL, *object = RT_NULL, *item =RT_NULL;1314 root = cJSON_Parse((const char *)data);15 if (!root)16 {17 rt_kprintf("No memory for cJSON root!\n");18 return;19 }2021 object = cJSON_GetObjectItem(root, "result");2223 item = object->child;2425 rt_kprintf("\nresult :%s \r\n", item->valuestring);2627 rt_pin_mode(PIN_LED_R, PIN_MODE_OUTPUT);28 rt_pin_mode(PIN_LED_G, PIN_MODE_OUTPUT);29 rt_pin_mode(PIN_LED_B, PIN_MODE_OUTPUT);30 rt_pin_write(PIN_LED_R,1);31 rt_pin_write(PIN_LED_G,1);32 rt_pin_write(PIN_LED_B,1);3334 if(strstr((char*)data, cmd1) != NULL)35 {36 /* 打开红灯 */37 rt_pin_write(PIN_LED_R,0);38 } 39 if(strstr((char*)data, cmd2) != NULL)40 {41 /* 关闭红灯 */42 rt_pin_write(PIN_LED_R,1);43 }44 if(strstr((char*)data, cmd3) != NULL)45 {46 /* 打开蓝灯 */47 rt_pin_write(PIN_LED_B,0);48 }49 if(strstr((char*)data, cmd4) != NULL)50 {51 /* 关闭蓝灯 */52 rt_pin_write(PIN_LED_B,1);53 }54 if(strstr((char*)data, cmd5) != NULL)55 {56 /* 打开绿灯 */57 rt_pin_write(PIN_LED_G,0);58 }59 if(strstr((char*)data, cmd6) != NULL)60 {61 /* 关闭绿灯 */62 rt_pin_write(PIN_LED_G,1);63 }646566 if (root != RT_NULL)67 cJSON_Delete(root);68}
只需以上的代码(代码参考自论坛各位小伙伴),你就可以实现百度语音识别以及控制相应外设了。下面看看实际效果:我使用的潘多拉开发板板载了stlink,且其为我们提供了一个虚拟串口,用usb数据线将开发板和电脑连接起来,将代码烧写进开发板后,我们利用这个虚拟串口,使用Xshell一类的终端软件,就可以看到如下的开机画面:
这便是RT-Thread提供的Finsh控制台组件,使用这个组件,我们可以方便地观察程序的运行状态,以命令行的形式调试运行程序,从图中我们可以看到,我们需要的文件系统和网络功能都已经初始化成功。
使用ls命令看看:
欸~,这便是我事先准备好的音频文件。
在上面的代码中,我们可以看到有这样一句:
1MSH_CMD_EXPORT(bd, webclient post file);
通过这行代码,我们就可以在Finsh控制台里使用bd这个命令,这个命令就是将音频文件发送到百度语音服务器,试试看:
使用bd命令将greenon.pcm发送到百度语音服务器,正确识别出结果:“打开绿灯”;与此同时,rgb灯也亮起了绿色。
尝试其他音频文件,效果完美!
RT-Thread线上/下活动
1、【RT-Thread开发者大会报名】上海站马上开始!2019年RT-Thread开发者大会将登入成都、上海、深圳与开发者们见面,还有RT-Thread在中高端智能领域的应用、一站式RTT开发工具、打造IoT极速开发模式等干货演讲,期待您的参与!
立即报名
#题外话# 喜欢RT-Thread不要忘了在GitHub上留下你的STAR哦,你的star对我们来说非常重要!链接地址:https://github.com/RT-Thread/rt-thread
你可以添加微信17775982065为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群
RT-Thread
让物联网终端的开发变得简单、快速,芯片的价值得到最大化发挥。Apache2.0协议,可免费在商业产品中使用,不需要公布源码,无潜在商业风险。长按二维码,关注我们
点击“阅读原文”报名开发者大会
转载地址:http://hanef.baihongyu.com/