当前位置:   article > 正文

基于STM32简易DIY智能聊天机器人_stm32 ai在屏幕对话

stm32 ai在屏幕对话

前言


大二忙里偷闲,花了一个月左右自己利用了Python+ESP8266 DIY 了一个智能聊天机器人,调用的是图灵机器人的体验API,现在把DIY过程记录下来,希望能分享给别的对这方面有兴趣的人。


DIY前的准备


1.STM32F429IG作为主控芯片

2.ESP8266,用来与自己电脑上服务器通信

3.VS1053,用来保存和播放音乐

硬件方面很简单,当然也可以自己兴趣拓展,比如自己加一块显示屏什么的,都是可以的。


电脑端服务器Python


思路是,电脑利用Python开服务器,等待ESP8266的连接,连接上后,STM32会发送给服务端刚刚录下的音乐,然后调用百度语音识别api,就可以将刚刚的录下的音乐发送给百度语音识别,百度语音会返回识别完成的字符串,再调用图灵机器人的api,把识别后的字符串发送出去,就会得到聊天的回复语句,最后一步,将回复语句发送给 百度语音合成,生成的回复语句的mp3,发送给stm32,stm32再通过VS1053播放,以上就实现了聊天的功能。

流程就是   vs1053>录音下的语句(stm32)  >百度语音识别 >图灵机器人 >百度语音生成 >stm32>vs1053

流程很简单,那么直接上代码

  1. #coding=utf-8
  2. from socket import *
  3. import sys
  4. import json
  5. import base64
  6. from urllib.request import urlopen
  7. from urllib.request import Request
  8. from urllib.error import URLError
  9. from urllib.parse import urlencode
  10. import string
  11. import requests
  12. class DemoError(Exception):
  13. pass
  14. """ 获取TOKEN"""
  15. def fetch_token():
  16. TOKEN_URL = 'http://openapi.baidu.com/oauth/2.0/token'
  17. SCOPE = 'audio_voice_assistant_get' # 有此scope表示有asr能力,没有请在网页里勾选
  18. API_KEY = '你的api_key'
  19. SECRET_KEY = '你的api_secret'
  20. params = {'grant_type': 'client_credentials',
  21. 'client_id': API_KEY,
  22. 'client_secret': SECRET_KEY}
  23. post_data = urlencode(params)
  24. post_data = post_data.encode( 'utf-8')
  25. req = Request(TOKEN_URL, post_data)
  26. try:
  27. f = urlopen(req)
  28. result_str = f.read()
  29. except URLError as err:
  30. result_str = err.read()
  31. result_str = result_str.decode()
  32. result = json.loads(result_str)
  33. if ('access_token' in result.keys() and 'scope' in result.keys()):
  34. if not SCOPE in result['scope'].split(' '):
  35. raise DemoError('scope is not correct')
  36. return result['access_token']
  37. else:
  38. raise DemoError('MAYBE API_KEY or SECRET_KEY not correct: access_token or scope not found in token response')
  39. """ 语音识别"""
  40. ASR_URL = 'http://vop.baidu.com/server_api'
  41. def voice_judge():
  42. token = fetch_token()
  43. # 需要识别的文件
  44. AUDIO_FILE = '8k.wav' #只支持 pcm/wav/amr
  45. # 文件格式
  46. FORMAT = AUDIO_FILE[-3:]; # 文件后缀 pcm/wav/amr
  47. # 根据文档填写PID,选择语言及识别模型
  48. DEV_PID = 1537; # 1537 表示识别普通话,使用输入法模型。1536表示识别普通话,使用搜索模型
  49. CUID = '123456PYTHON';
  50. # 采样率
  51. RATE = 8000; # 固定值
  52. speech_data = []
  53. with open(AUDIO_FILE, 'rb') as speech_file:
  54. speech_data = speech_file.read()
  55. length = len(speech_data)
  56. if length == 0:
  57. raise DemoError('file %s length read 0 bytes' % AUDIO_FILE)
  58. speech = base64.b64encode(speech_data)
  59. speech = str(speech, 'utf-8')
  60. params = {'dev_pid': DEV_PID,
  61. 'format': FORMAT,
  62. 'rate': RATE,
  63. 'token': token,
  64. 'cuid': CUID,
  65. 'channel': 1,
  66. 'speech': speech,
  67. 'len': length
  68. }
  69. post_data = json.dumps(params, sort_keys=False)
  70. req = Request(ASR_URL, post_data.encode('utf-8'))
  71. req.add_header('Content-Type', 'application/json')
  72. try:
  73. f = urlopen(req)
  74. result_str = f.read()
  75. except URLError as err:
  76. result_str = err.read()
  77. result_str = str(result_str, 'utf-8')
  78. return (result_str)
  79. """ 聊天回复"""
  80. def get_response(msg):
  81. api = 'http://openapi.tuling123.com/openapi/api/v2'
  82. dat = {
  83. "perception": {
  84. "inputText": {
  85. "text": msg
  86. },
  87. "inputImage": {
  88. "url": "imageUrl"
  89. },
  90. "selfInfo": {
  91. "location": {
  92. "city": "厦门",
  93. }
  94. }
  95. },
  96. "userInfo": {
  97. "apiKey": '你的api_key',
  98. "userId": "随意的用户id,用来判断是否为同一人,因为图灵机器人会根据上文回复"
  99. }
  100. }
  101. dat = json.dumps(dat)
  102. r = requests.post(api, data=dat).json()
  103. mesage = r['results'][0]['values']['text']
  104. return mesage
  105. """ 语音生成"""
  106. def voice_make(msg):
  107. token = fetch_token()
  108. # 发音人选择, 0为普通女声,1为普通男生,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女声
  109. PER = 4
  110. # 语速,取值0-15,默认为5中语速
  111. SPD = 2
  112. # 音调,取值0-15,默认为5中语调
  113. PIT = 5
  114. # 音量,取值0-9,默认为5中音量
  115. VOL = 5
  116. # 下载的文件格式, 3:mp3(default) 4: pcm-16k 5: pcm-8k 6. wav
  117. AUE = 3
  118. FORMATS = {3: "mp3", 4: "pcm", 5: "pcm", 6: "wav"}
  119. FORMAT = FORMATS[AUE]
  120. CUID = "123456PYTHON"
  121. TTS_URL = 'http://tsn.baidu.com/text2audio'
  122. params = {'tok': token, 'tex': msg, 'per': PER, 'spd': SPD, 'pit': PIT, 'vol': VOL, 'aue': AUE, 'cuid': CUID,
  123. 'lan': 'zh', 'ctp': 1} # lan ctp 固定参数
  124. data = urlencode(params)
  125. req = Request(TTS_URL, data.encode('utf-8'))
  126. has_error = False
  127. try:
  128. f = urlopen(req)
  129. result_str = f.read()
  130. has_error = ('Content-Type' not in f.headers.keys() or f.headers['Content-Type'].find('audio/') < 0)
  131. except URLError as err:
  132. result_str = err.read()
  133. has_error = True
  134. save_file = "error.txt" if has_error else 'result.' + FORMAT
  135. with open(save_file, 'wb') as of:
  136. of.write(result_str)
  137. if has_error:
  138. result_str = str(result_str, 'utf-8')
  139. #服务器,主程序
  140. HOST = '你当前电脑的ip地址'
  141. PORT = 80
  142. BUFSIZ = 0x500000
  143. ADDR=(HOST,PORT)
  144. AUDIO_FILE = '8k.wav' #只支持 pcm/wav/amr
  145. s = socket(AF_INET, SOCK_STREAM)
  146. s.bind(ADDR)
  147. s.listen(5)
  148. while True:
  149. print('waiting for connecting...')
  150. print('')
  151. c, addr = s.accept()
  152. print('..connected from:', addr)
  153. speech_file= open(AUDIO_FILE, 'r+')
  154. speech_file.seek(0)
  155. speech_file.truncate() #清空文件
  156. speech_file.close( )
  157. while True:
  158. data = c.recv(BUFSIZ)
  159. if not data:
  160. break
  161. speech_file= open(AUDIO_FILE, 'ab+')
  162. speech_file.write(data)
  163. speech_file.flush()
  164. speech_file.close( )
  165. c.close()
  166. mystr=voice_judge()
  167. result = json.loads(mystr)
  168. if(result['err_no']==0):
  169. mystr = "".join(result['result'])
  170. else:
  171. mystr="无效"
  172. print(mystr)
  173. mybyte = bytes(mystr, encoding = "gbk")
  174. reply=get_response(mystr)
  175. voice_make(reply)
  176. print(reply)
  177. c, addr = s.accept()
  178. speech_file= open('result.mp3', 'rb')
  179. data=speech_file.read()
  180. speech_file.close( )
  181. c.sendall(data)
  182. time.sleep(1)
  183. c.close()
  184. s.close()

 

 

STM32F429代码


 

stm32f429的流程也很简单,就是按下按键,开始录音,再按一下结束录音,然后等待回传回来的音频文件并且播放。

至于VS1053的代码,我之前有篇博客说了,如果不懂可以看看https://blog.csdn.net/qq_41495871/article/details/83686514

另外一个模块就是ESP8266,ESP8266的代码也是很简单的,我使用的是模组,所以很简单的调用api就好了,如果使用的是正统的esp8266,除了传输速度慢了一些,别的应该都一样。至于esp8266的配置,这边就不详细说明了,网络一大堆这个东西,我之前也用过NodeMcu实现过,Arduino调库调起来也是容易实现的。

 

  1. void User_BSP_Init()
  2. {
  3. delay_init(168); // 初始化系统时钟,主频为168M
  4. SDRAM_Init(); //SDRAM初始化
  5. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置NVIC为优先级组2
  6. LED_GPIO_Config(); //配置板载LED
  7. USART_Config(); //配置串口
  8. USART_IT_ENABLE(); //打开串口接收中断
  9. EXTI_Key_Config(); //打开Key的外部中断
  10. Fatfs_Flash_Format(); //初始化Fatfs_SPI_Flash
  11. M8266_Module_User_Init(); //初始化M8266,并打印相关信息
  12. VS_Init(); //初始化VS1053
  13. f_mount(&fs,"1:",1); //挂载SPI_Flash 为盘符 1:
  14. }

这是BSP的初始化

  1. void User_main()
  2. {
  3. OS_ERR err;
  4. OSSchedRoundRobinCfg(DEF_ENABLED,0,&err); //开启时间片转轮调度 10*系统节拍 即10ms
  5. OSMemCreate(&uC_mem,"uC/Data",uC_Data,4,16,&err); //开启内存管理系统 ,128个内存块,每个4个字节
  6. OSTaskCreate(&USART1_Get_TCB,"串口接收",USART1_Get,0,USART1_Get_PRIO,USART1_Get_STK,USART1_Get_STK_SIZE/10,USART1_Get_STK_SIZE,0,0,0,(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),&err);
  7. OSTaskCreate(&Key_TCB,"按键中断",Key_On,0,Key_PRIO,Key_Stk,Key_Stk_Size/10,Key_Stk_Size,0,0,0,(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),&err);
  8. OSTaskCreate(&USART1_OK_TCB,"串口接收完成",USART1_OK,0,USART1_OK_PRIO,USART1_OK_STK,USART1_OK_STK_SIZE/10,USART1_OK_STK_SIZE,0,0,0,(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),&err);
  9. OSTaskCreate(&M8266_Get_TCB,"M8266接收",M8266_Get,0,M8266_Get_PRIO,M8266_Gett_Stk,M8266_Get_Stk_Size/10,M8266_Get_Stk_Size,0,0,0,(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),&err);
  10. M8266_Module_Join_AP((u8*)WiFi_SSID,(u8*)WiFi_PAWD,Hostname);
  11. }

上面是几个主要任务,并且esp8266连接上你的热点

  1. static void Key_On (void *p_arg)
  2. {
  3. OS_ERR err;
  4. unsigned long file_size ;
  5. CPU_SR_ALLOC();
  6. LED_TOGGLE;
  7. OSTimeDly(300,OS_OPT_TIME_DLY,&err);
  8. LED_TOGGLE;
  9. OSTimeDly(300,OS_OPT_TIME_DLY,&err);
  10. LED_TOGGLE;
  11. OSTimeDly(300,OS_OPT_TIME_DLY,&err);
  12. LED_TOGGLE; //闪灯表示准备完成
  13. while(1)
  14. {
  15. OSTaskSemPend (0,OS_OPT_PEND_BLOCKING,NULL,&err);
  16. OS_CRITICAL_ENTER();
  17. M8266_Module_Set_Connect(Goal_Ip,Remote_Port,LinkNum,10); //连接
  18. OS_CRITICAL_EXIT();
  19. f_unlink("1:录音文件.wav");
  20. f_unlink("1:音乐文件.mp3");
  21. vs1053_record_start(); //开始录音
  22. OSTaskCreate(&Record_TCB,"录音",Record,0,Record_PRIO,Record_Stk,Record_Stk_Size/10,Record_Stk_Size,2,0,0,(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),&err);
  23. OSTaskSemPend (0,OS_OPT_PEND_BLOCKING,NULL,&err);
  24. OSTaskDel(&Record_TCB,&err);
  25. vs1053_record_stop("1:录音文件.wav"); //停止录音,并且保存在外部Flash中
  26. OSTimeDly(100,OS_OPT_TIME_DLY,&err);
  27. M8266_Module_SendFile((uint8_t*)"1:录音文件.wav",LinkNum); //发送录音文件
  28. printf("音乐文件大小是 %ld Byte,%.2f KB\r\n",file_size,(double)file_size/1024);
  29. M8266WIFI_SPI_Delete_Connection(LinkNum,NULL); //断开连接
  30. OSTimeDly(1500,OS_OPT_TIME_DLY,&err);
  31. M8266_Module_Set_Connect(Goal_Ip,Remote_Port,LinkNum,10); //开启连接,等待服务端发送处理好的回复音频文件
  32. OSTimeDly(1000,OS_OPT_TIME_DLY,&err);
  33. M8266WIFI_SPI_Delete_Connection(LinkNum,NULL);
  34. LED_TOGGLE;
  35. }
  36. }
  37. static void Record (void *p_arg)
  38. {
  39. OS_ERR err;
  40. CPU_SR_ALLOC();
  41. OSTimeDly(500,OS_OPT_TIME_DLY,&err);
  42. LED_TOGGLE;
  43. while(1)
  44. {
  45. OS_CRITICAL_ENTER();
  46. vs1053_record_run();
  47. OS_CRITICAL_EXIT();
  48. OSTimeDly(33,OS_OPT_TIME_DLY,&err); //经过测试大概33ms收集一次,音质最佳
  49. }
  50. }

这是按键任务,应该是最主要的任务。还有录音时创建的任务。

  1. static void M8266_Get (void *p_arg)
  2. {
  3. OS_ERR err;
  4. u16 recv_data_num;
  5. u16 status;
  6. u16 wifi_get_flag;
  7. unsigned long file_size ;
  8. while(1)
  9. {
  10. status=M8266_Module_GetData(NULL,&recv_data_num);
  11. if(status!=0x0001)
  12. {
  13. wifi_get_flag=0;
  14. while(status)
  15. {
  16. memcpy(&VS1053_Mem[wifi_get_flag],test_get,recv_data_num);
  17. memset(test_get,0,recv_data_num);
  18. wifi_get_flag+= recv_data_num;
  19. status=M8266_Module_GetData(NULL,&recv_data_num);
  20. }
  21. memcpy(VS1053_Mem,test_get,recv_data_num);
  22. memset(test_get,0,recv_data_num);
  23. wifi_get_flag+=recv_data_num;
  24. //将接收到的音乐文件保存到 VS1053_Mem SDRAM中
  25. printf("接收到 %d Byte\r\n",wifi_get_flag);
  26. vs1053_write_misic_file("1:音乐文件.mp3",wifi_get_flag); //写入Flash中
  27. OSTimeDly(100,OS_OPT_TIME_DLY,&err);
  28. vs1053_player_song((uint8_t*)"1:音乐文件.mp3",&file_size); //播放刚刚保存的文集
  29. printf("音乐文件大小是 %ld Byte\r\n",file_size);
  30. }
  31. OSTimeDly(50,OS_OPT_TIME_DLY,&err);
  32. }
  33. }

 

然后是ESP8266接收到音频数据后,播放音频的任务

其余部分就是一些串口部分的任务了

  1. static void USART1_OK(void *p_arg) //串口接收完成任务
  2. {
  3. OS_ERR err;
  4. uint32_t M8266_flag;
  5. uint32_t Debug_flag;
  6. u16 status;
  7. while(1)
  8. {
  9. OSTaskSemPend (0,OS_OPT_PEND_BLOCKING,NULL,&err);
  10. M8266_flag=0;
  11. Debug_flag=0;
  12. printf("接收到 %d 串口数据\r\n",Write_Usart_flag);
  13. while(M8266_flag<Write_Usart_flag)
  14. {
  15. if(Write_Usart_flag-M8266_flag<=1024)
  16. {
  17. Debug_flag+=M8266WIFI_SPI_Send_Data(&Usart_Mem[M8266_flag],Write_Usart_flag-M8266_flag,LinkNum,&status);
  18. M8266_flag+=Write_Usart_flag-M8266_flag;
  19. }
  20. else
  21. {
  22. Debug_flag+=M8266WIFI_SPI_Send_Data(&Usart_Mem[M8266_flag],1024,LinkNum,&status);
  23. M8266_flag+=1024;
  24. }
  25. }
  26. printf("成功发送 %d Byte\r\n",Debug_flag);
  27. memset(Usart_Mem,0,Write_Usart_flag);
  28. Write_Usart_flag=0;
  29. }
  30. }
  31. static void USART1_Get (void *p_arg)
  32. {
  33. OS_ERR err;
  34. OS_MSG_SIZE msg_size;
  35. char * pMsg;
  36. while (DEF_TRUE)
  37. {
  38. pMsg = OSTaskQPend(0,OS_OPT_PEND_BLOCKING,&msg_size,NULL,&err); //无限期限堵塞等待
  39. Usart_Mem[Write_Usart_flag]=*pMsg;
  40. Write_Usart_flag++;
  41. OSMemPut(&uC_mem,pMsg,&err); // 退还内存块
  42. }
  43. }

以上就是几个主要部分了,很简单。


最后附上工程的GitHub地址:https://github.com/PeepOrange/M8266-STM32

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/965075
推荐阅读
相关标签
  

闽ICP备14008679号