• 自己制作智能语音机器人(基于jetson nano)


    1 简介

    如上图,主要采用jetson上编写python代码实现,支持离线语音唤醒、在线语音识别、大模型智能文档、在线语音合成。

    所需硬件如下:

    1. jetson nano:linux
    2. 科大讯飞麦克风硬件:AIUI R818麦克阵列开发套件+6麦阵列,支持离线语音唤醒
    3. USB免驱声卡+喇叭

    所需软件如下:

    1. 科大讯飞在线语音识别API
    2. 科大讯飞在线语音合成API
    3. 语言大模型API

    视频示例:

    自己制作智能语音机器人,识别鸭脖和老鼠头_哔哩哔哩_bilibili

    2 jetson 安装pycharm

    安装pycharm主要是为了方便直接在jetson上进行python开发。

    1.下载地址下载PyCharm:JetBrains为专业开发者提供的Python IDE

    选择arm64版本。

    2. 解压进入bin目录。在终端输入sh pycharm.sh进行安装。

    3.创建桌面图标

    在主桌面打开终端,输入编辑命令

    sudo gedit /usr/share/applications/Pycharm.desktop

    这里使用了管理员权限,要输入管理员密码

    随后会弹出一个文本框,输入以下命令

    1. [Desktop Entry]
    2. Type=Application
    3. Name=Pycharm
    4. GenericName=Pycharm3
    5. Comment=Pycharm3:The Python IDE
    6. Exec=sh /home/xxx/pycharm.sh
    7. Icon=home/xxx/pycharm.png
    8. Terminal=pycharm
    9. Categories=Pycharm;

    上面给出的代码里的xxx为pycharm.sh和pycharm.png的位置。就是第2部解压后的路径。

    文本编辑好后点击保存,之后就可以在应用管理里找到pycharm的应用图标,并可以运行了。

    4. 新建工程,可以选择jetson自带的python。

    参考:jetson安装pycharm和visual studio code_龙先生__的博客-CSDN博客

    3 离线语音唤醒

    采用科大讯飞AIUI R818麦克阵列和6麦组合,支持离线语音识别

    科大讯飞AIUI R818麦克阵列开发套件

    AIUI R818麦克阵列开发套件是一款基于多麦克风阵列的完整前端声学软硬件一体化解决方案。采用4核

    高性能边缘计算处理器,内部集成科大讯飞语音算法,利用麦克风阵列的空域滤波特性,通过唤醒人的

    角度定位,形成定向拾音波束,并对波束以外的噪声进行抑制,提升远场拾音质量。同时针对人机交互

    一体终端,集成高性能回声消除算法,降低语音、语义识别难度,开发者可快速集成使产品具备多麦音

    频采集、唤醒、降噪和回声消除等功能。

    6麦阵列

    硬件连线

    如下图,主要连接UAC接口、串口、40排线这3个就行。

    配置自己的唤醒词

    需要登录科大讯飞控制台定制自己的唤醒词,文档参见R818 AIUI降噪板开发套件 - AIUI文档中心 - 站点标题

    1. 在网站AIUI开放平台,设置唤醒词

    1. 取下载资源中的 res.bin
      1. 通过adb 命令adb push res.bin /oem/build/vtn/res.bin
      2. adb shell 进入adb 控制台
      3. 输入sync 同步
      4. 输入 reboot 重启

    代码

    将上面硬件连接到jetson后,需要在jetson实现如下代码,实现python代码里接收到麦克风语音唤醒信号。

    1.安装串口函数库,终端执行下面的命令(如果使用的是官方镜像,应该是已经安装好了):

    sudo pip3 install pyserial

    2. 麦克风唤醒代码

    1. import serial
    2. import json
    3. import time
    4. import os
    5. from std_msgs.msg import String, Float32, Int32, Bool
    6. woshuo = b'\xa5\x01\x01\x04\x00\x00\x00\xa5\x00\x00\x00\xb0'
    7. queren = b'\xA5\x01\xff\x04\x00\x00\x00\xA5\x00\x00\x00\xB2'
    8. len_r = 0
    9. data_list = []
    10. buf = []
    11. buf_flag = 0
    12. first=0
    13. def deal(data_list):
    14. global first
    15. str1 = str(data_list)
    16. f_data = str1.find('{')
    17. l_data = str1.rfind('}')
    18. str1 = str1[f_data:l_data+1]
    19. str1 = str1.replace("\\","")
    20. str1 = str1.replace("', b'","")
    21. str1 = str1.replace('"{',"{")
    22. str1 = str1.replace('}"',"}")
    23. json_str = json.loads(str1)
    24. print("json_str: ", json_str)
    25. if 'code' in json_str and first == 0:
    26. sss = json_str['content']
    27. print(json_str['content'])
    28. first = 1
    29. else:
    30. angle = json_str['content']['info']['ivw']['angle']
    31. print("angle: ", angle)
    32. return angle
    33. #麦克风连接的usb端口
    34. #timeout这是终止时间,用以终止串口操作。程序就会持续读取timeout秒的时长来读取数据
    35. ser=serial.Serial("/dev/ttyUSB0", 115200, 8,'N',1,timeout = 5)
    36. print("Open the serial")
    37. try:
    38. while True:
    39. #serial.read_all()读取一个timeout周期内的全部数据(常用方法)
    40. #平时串口通信中,最好使用read_all()方法,并选定合适的timeout
    41. rcv = ser.read_all()
    42. len_r = len(rcv)
    43. if rcv == woshuo:
    44. ser.write(queren)
    45. elif(len_r > 1) :
    46. buf.append(rcv)
    47. buf_flag = 1
    48. elif len_r < 1 and buf_flag==1:
    49. buf_flag = 0
    50. data_list = buf
    51. buf = []
    52. angle_msg = deal(data_list)
    53. time.sleep(0.5)
    54. ser.close()
    55. except Exception as e:
    56. print("---Error---:",e)

    4 在线语音识别ASR

    在上面语音唤醒基础上,增加调用科大讯飞在线API接口实现语音识别。主要采用语音听写流式API demo python3语言。websocket接口。

    详见:语音听写(流式版)WebAPI 文档 | 讯飞开放平台文档中心

    1. # -*- coding:utf-8 -*-
    2. #
    3. # author: iflytek
    4. #
    5. # 本demo测试时运行的环境为:Windows + Python3.7
    6. # 本demo测试成功运行时所安装的第三方库及其版本如下,您可自行逐一或者复制到一个新的txt文件利用pip一次性安装:
    7. # cffi==1.12.3
    8. # gevent==1.4.0
    9. # greenlet==0.4.15
    10. # pycparser==2.19
    11. # six==1.12.0
    12. # websocket==0.2.1
    13. # websocket-client==0.56.0
    14. #
    15. # 语音听写流式 WebAPI 接口调用示例 接口文档(必看):https://doc.xfyun.cn/rest_api/语音听写(流式版).html
    16. # webapi 听写服务参考帖子(必看):http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=38947&extra=
    17. # 语音听写流式WebAPI 服务,热词使用方式:登陆开放平台https://www.xfyun.cn/后,找到控制台--我的应用---语音听写(流式)---服务管理--个性化热词,
    18. # 设置热词
    19. # 注意:热词只能在识别的时候会增加热词的识别权重,需要注意的是增加相应词条的识别率,但并不是绝对的,具体效果以您测试为准。
    20. # 语音听写流式WebAPI 服务,方言试用方法:登陆开放平台https://www.xfyun.cn/后,找到控制台--我的应用---语音听写(流式)---服务管理--识别语种列表
    21. # 可添加语种或方言,添加后会显示该方言的参数值
    22. # 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
    23. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    24. import websocket
    25. import datetime
    26. import hashlib
    27. import base64
    28. import hmac
    29. import json
    30. from urllib.parse import urlencode
    31. import time
    32. import ssl
    33. from wsgiref.handlers import format_date_time
    34. from datetime import datetime
    35. from time import mktime
    36. import _thread as thread
    37. STATUS_FIRST_FRAME = 0 # 第一帧的标识
    38. STATUS_CONTINUE_FRAME = 1 # 中间帧标识
    39. STATUS_LAST_FRAME = 2 # 最后一帧的标识
    40. class Ws_Param(object):
    41. # 初始化
    42. def __init__(self, APPID, APIKey, APISecret, AudioFile):
    43. self.APPID = APPID
    44. self.APIKey = APIKey
    45. self.APISecret = APISecret
    46. self.AudioFile = AudioFile
    47. # 公共参数(common)
    48. self.CommonArgs = {"app_id": self.APPID}
    49. # 业务参数(business),更多个性化参数可在官网查看
    50. self.BusinessArgs = {"domain": "iat", "language": "zh_cn", "accent": "mandarin", "vinfo":1,"vad_eos":10000}
    51. # 生成url
    52. def create_url(self):
    53. url = 'wss://ws-api.xfyun.cn/v2/iat'
    54. # 生成RFC1123格式的时间戳
    55. now = datetime.now()
    56. date = format_date_time(mktime(now.timetuple()))
    57. # 拼接字符串
    58. signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
    59. signature_origin += "date: " + date + "\n"
    60. signature_origin += "GET " + "/v2/iat " + "HTTP/1.1"
    61. # 进行hmac-sha256进行加密
    62. signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
    63. digestmod=hashlib.sha256).digest()
    64. signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
    65. authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
    66. self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
    67. authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
    68. # 将请求的鉴权参数组合为字典
    69. v = {
    70. "authorization": authorization,
    71. "date": date,
    72. "host": "ws-api.xfyun.cn"
    73. }
    74. # 拼接鉴权参数,生成url
    75. url = url + '?' + urlencode(v)
    76. # print("date: ",date)
    77. # print("v: ",v)
    78. # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
    79. # print('websocket url :', url)
    80. return url
    81. # 收到websocket消息的处理
    82. def on_message(ws, message):
    83. try:
    84. code = json.loads(message)["code"]
    85. sid = json.loads(message)["sid"]
    86. if code != 0:
    87. errMsg = json.loads(message)["message"]
    88. print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))
    89. else:
    90. data = json.loads(message)["data"]["result"]["ws"]
    91. # print(json.loads(message))
    92. result = ""
    93. for i in data:
    94. for w in i["cw"]:
    95. result += w["w"]
    96. print("sid:%s call success!,data is:%s" % (sid, json.dumps(data, ensure_ascii=False)))
    97. except Exception as e:
    98. print("receive msg,but parse exception:", e)
    99. # 收到websocket错误的处理
    100. def on_error(ws, error):
    101. print("### error:", error)
    102. # 收到websocket关闭的处理
    103. def on_close(ws,a,b):
    104. print("### closed ###")
    105. # 收到websocket连接建立的处理
    106. def on_open(ws):
    107. def run(*args):
    108. frameSize = 8000 # 每一帧的音频大小
    109. intervel = 0.04 # 发送音频间隔(单位:s)
    110. status = STATUS_FIRST_FRAME # 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧
    111. with open(wsParam.AudioFile, "rb") as fp:
    112. while True:
    113. buf = fp.read(frameSize)
    114. # 文件结束
    115. if not buf:
    116. status = STATUS_LAST_FRAME
    117. # 第一帧处理
    118. # 发送第一帧音频,带business 参数
    119. # appid 必须带上,只需第一帧发送
    120. if status == STATUS_FIRST_FRAME:
    121. d = {"common": wsParam.CommonArgs,
    122. "business": wsParam.BusinessArgs,
    123. "data": {"status": 0, "format": "audio/L16;rate=16000",
    124. "audio": str(base64.b64encode(buf), 'utf-8'),
    125. "encoding": "raw"}}
    126. d = json.dumps(d)
    127. ws.send(d)
    128. status = STATUS_CONTINUE_FRAME
    129. # 中间帧处理
    130. elif status == STATUS_CONTINUE_FRAME:
    131. d = {"data": {"status": 1, "format": "audio/L16;rate=16000",
    132. "audio": str(base64.b64encode(buf), 'utf-8'),
    133. "encoding": "raw"}}
    134. ws.send(json.dumps(d))
    135. # 最后一帧处理
    136. elif status == STATUS_LAST_FRAME:
    137. d = {"data": {"status": 2, "format": "audio/L16;rate=16000",
    138. "audio": str(base64.b64encode(buf), 'utf-8'),
    139. "encoding": "raw"}}
    140. ws.send(json.dumps(d))
    141. time.sleep(1)
    142. break
    143. # 模拟音频采样间隔
    144. time.sleep(intervel)
    145. ws.close()
    146. thread.start_new_thread(run, ())
    147. if __name__ == "__main__":
    148. # 测试时候在此处正确填写相关信息即可运行
    149. time1 = datetime.now()
    150. wsParam = Ws_Param(APPID='xxxxx', APISecret='xxxxx',
    151. APIKey='xxxxx',
    152. AudioFile=r'xxxxx')
    153. websocket.enableTrace(False)
    154. wsUrl = wsParam.create_url()
    155. ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
    156. ws.on_open = on_open
    157. ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
    158. time2 = datetime.now()
    159. print(time2-time1)

    5 对接大模型进行智能对话

    百度大模型调用链如下:

    代码

    1. import numpy as np
    2. import requests
    3. import json
    4. #获取授权token
    5. url = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxx&client_secret=xxx"
    6. payload = ""
    7. headers = {
    8. 'Content-Type': 'application/json',
    9. 'Accept': 'application/json'
    10. }
    11. response = requests.request("POST", url, headers=headers, data=payload)
    12. print(response.text)
    13. d = response.json()
    14. #调用模型
    15. url = 'https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token={'+d['access_token']+'}'
    16. payload = "{\"messages\":[{\"role\":\"user\",\"content\":\"什么是大模型\"}]}"
    17. payload=payload.encode('utf-8')
    18. headers = {
    19. 'Content-Type': 'application/json',
    20. 'Accept': 'application/json'
    21. }
    22. response = requests.request("POST", url, headers=headers, data=payload)
    23. print(response.text)
    24. d = response.json() #以字典格式获取json数据
    25. print(d['result'])

    6 在线语音合成

    采用科大讯飞接口,流水语音合成,websocket接口。

    参见:语音合成(流式版)WebAPI 文档 | 讯飞开放平台文档中心

    1. # -*- coding:utf-8 -*-
    2. #
    3. # author: iflytek
    4. #
    5. # 本demo测试时运行的环境为:Windows + Python3.7
    6. # 本demo测试成功运行时所安装的第三方库及其版本如下:
    7. # cffi==1.12.3
    8. # gevent==1.4.0
    9. # greenlet==0.4.15
    10. # pycparser==2.19
    11. # six==1.12.0
    12. # websocket==0.2.1
    13. # websocket-client==0.56.0
    14. # 合成小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
    15. # 错误码链接:https://www.xfyun.cn/document/error-code (code返回错误码时必看)
    16. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    17. import websocket
    18. import datetime
    19. import hashlib
    20. import base64
    21. import hmac
    22. import json
    23. from urllib.parse import urlencode
    24. import time
    25. import ssl
    26. from wsgiref.handlers import format_date_time
    27. from datetime import datetime
    28. from time import mktime
    29. import _thread as thread
    30. import os
    31. STATUS_FIRST_FRAME = 0 # 第一帧的标识
    32. STATUS_CONTINUE_FRAME = 1 # 中间帧标识
    33. STATUS_LAST_FRAME = 2 # 最后一帧的标识
    34. class Ws_Param(object):
    35. # 初始化
    36. def __init__(self, APPID, APIKey, APISecret, Text):
    37. self.APPID = APPID
    38. self.APIKey = APIKey
    39. self.APISecret = APISecret
    40. self.Text = Text
    41. # 公共参数(common)
    42. self.CommonArgs = {"app_id": self.APPID}
    43. # 业务参数(business),更多个性化参数可在官网查看
    44. self.BusinessArgs = {"aue": "raw", "auf": "audio/L16;rate=16000", "vcn": "xiaoyan", "tte": "utf8"}
    45. self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-8')), "UTF8")}
    46. #使用小语种须使用以下方式,此处的unicode指的是 utf16小端的编码方式,即"UTF-16LE"
    47. #self.Data = {"status": 2, "text": str(base64.b64encode(self.Text.encode('utf-16')), "UTF8")}
    48. # 生成url
    49. def create_url(self):
    50. url = 'wss://tts-api.xfyun.cn/v2/tts'
    51. # 生成RFC1123格式的时间戳
    52. now = datetime.now()
    53. date = format_date_time(mktime(now.timetuple()))
    54. # 拼接字符串
    55. signature_origin = "host: " + "ws-api.xfyun.cn" + "\n"
    56. signature_origin += "date: " + date + "\n"
    57. signature_origin += "GET " + "/v2/tts " + "HTTP/1.1"
    58. # 进行hmac-sha256进行加密
    59. signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
    60. digestmod=hashlib.sha256).digest()
    61. signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
    62. authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
    63. self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
    64. authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
    65. # 将请求的鉴权参数组合为字典
    66. v = {
    67. "authorization": authorization,
    68. "date": date,
    69. "host": "ws-api.xfyun.cn"
    70. }
    71. # 拼接鉴权参数,生成url
    72. url = url + '?' + urlencode(v)
    73. # print("date: ",date)
    74. # print("v: ",v)
    75. # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
    76. # print('websocket url :', url)
    77. return url
    78. def on_message(ws, message):
    79. try:
    80. message =json.loads(message)
    81. code = message["code"]
    82. sid = message["sid"]
    83. audio = message["data"]["audio"]
    84. audio = base64.b64decode(audio)
    85. status = message["data"]["status"]
    86. print(message)
    87. if status == 2:
    88. print("ws is closed")
    89. ws.close()
    90. if code != 0:
    91. errMsg = message["message"]
    92. print("sid:%s call error:%s code is:%s" % (sid, errMsg, code))
    93. else:
    94. with open('./demo.pcm', 'ab') as f:
    95. f.write(audio)
    96. except Exception as e:
    97. print("receive msg,but parse exception:", e)
    98. # 收到websocket错误的处理
    99. def on_error(ws, error):
    100. print("### error:", error)
    101. # 收到websocket关闭的处理
    102. def on_close(ws):
    103. print("### closed ###")
    104. # 收到websocket连接建立的处理
    105. def on_open(ws):
    106. def run(*args):
    107. d = {"common": wsParam.CommonArgs,
    108. "business": wsParam.BusinessArgs,
    109. "data": wsParam.Data,
    110. }
    111. d = json.dumps(d)
    112. print("------>开始发送文本数据")
    113. ws.send(d)
    114. if os.path.exists('./demo.pcm'):
    115. os.remove('./demo.pcm')
    116. thread.start_new_thread(run, ())
    117. if __name__ == "__main__":
    118. # 测试时候在此处正确填写相关信息即可运行
    119. wsParam = Ws_Param(APPID=' ', APISecret=' ',
    120. APIKey=' ',
    121. Text="这是一个语音合成示例")
    122. websocket.enableTrace(False)
    123. wsUrl = wsParam.create_url()
    124. ws = websocket.WebSocketApp(wsUrl, on_message=on_message, on_error=on_error, on_close=on_close)
    125. ws.on_open = on_open
    126. ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})

    7 播放声音

    7.1 播放wav

    linux命令:aplay -D plughw:0,0 xxx.wav

    plughw后面的0,0指的是声卡id和设备id(card0,device0),这个根据自己的设备决定

    7.2 播放pcm

    aplay -t raw -c 1 -f S16_LE -r 16000 test2.pcm

    -t: type raw表示是PCM

    -c: channel 1

    -f S16_LE: Signed 16bit-width Little-Endian

    -r: sample rate 16000

    PCM是最raw的音频数据,没有任何头信息。WAV文件就是PCM+头信息,头信息就是上述的声道数,sample rate这些。所以WAV文件可以直接播放,而PCM需要手动指定这些信息之后才能播放。

    参考:https://www.cnblogs.com/super119/archive/2011/01/03/1924431.html

  • 相关阅读:
    【毕业设计】基于RFID的门禁系统 - 单片机 物联网 嵌入式 stm32
    基于JAVA铝塑门窗的研制和生产管理计算机毕业设计源码+数据库+lw文档+系统+部署
    C语言——动态内存管理详解(内存结构、动态内存函数、易错题、柔性数组)
    隧道未来如何发展?路网全息感知,颠覆公路交通安全史
    《MySQL必知必会》知识汇总二
    入门数据库Days4
    离散数学考前小记
    计算机二级WPS 选择题(模拟和解析七)
    c#学习相关系列之构造函数
    图的存储-链式前向星
  • 原文地址:https://blog.csdn.net/zephyr_wang/article/details/131100530