马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
树莓派上做一个会听会说的语音助手
思路其实很朴素:麦克风录音 → 语音识别成文字 → 调大模型 API 得到回答 → TTS 合成语音 → 播放器播出。四段各用一个库搞定,串起来就是一个能对话的树莓派音箱。整个方案在树莓派 4B 上就能跑,成本不到两百块。
一、准备 API 密钥
先在对应平台申请密钥并保存好:
- 大模型侧:进 OpenAI 的 API Keys 页面创建一个 key(模型可以换成国内可用的替代,代码差异很小)。
- TTS 侧:阿里云百炼 dashscope 申请 api key,它有 SamBERT 系列中文语音合成,延迟低、听感自然,国内网络下速度优势明显。
两个 key 都要写进环境变量或配置文件,别硬编码进脚本里提交到 git。免费额度有限(大模型侧常见 5 美元起步额度),日常自测基本用不完。
二、装依赖
pip3 install openai
pip3 install SpeechRecognition
pip3 install dashscope
pip3 install pygame
几个包各司其职:SpeechRecognition 做 ASR,dashscope 提供 SamBERT 的 TTS,pygame 的 mixer 是实测里延迟最低、最稳的 Python 音频播放方式。
三、先验证大模型 API
先用一段最小代码确认 key 和模型都通:
# -*- coding: utf-8 -*-
import openai
openai.api_key = 'sk-xxxxxxxx' # 换成你自己的 key
conversation_history = [
{'role': 'system', 'content': 'You are a helpful assistant.'}
]
while True:
user_input = input("User: ")
conversation_history.append({'role': 'user', 'content': user_input})
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=conversation_history,
max_tokens=100,
n=1,
stop=None,
temperature=0.7
)
assistant_reply = response['choices'][0]['message']['content']
print("Assistant:", assistant_reply)
conversation_history.append({'role': 'assistant', 'content': assistant_reply})
if user_input.lower() == 'exit':
break
能问答就说明 API 段通了。把 conversation_history 带着一起传是关键——多轮对话靠这个历史列表,不传就只能一问一答、模型没有记忆。
四、语音识别(ASR)
硬件上一台免驱 USB 麦克风就够(树莓派原生的 3.5mm 口录不了,别走那个坑)。插上先确认系统认得:
arecord -l
# 能看到 card 就说明音频输入设备就绪
import speech_recognition as sr
r = sr.Recognizer()
with sr.Microphone() as source:
print("请说话:")
audio = r.listen(source)
try:
text = r.recognize_google(audio, language='zh-CN')
print("你说的是:" + text)
except sr.UnknownValueError:
print("无法识别你的语音")
except sr.RequestError as e:
print("无法连接识别服务,错误原因:" + str(e))
几点实战体会:
- 安静环境识别率高,嘈杂环境基本废——先用
r.adjust_for_ambient_noise(source) 做一次环境噪声校准。
recognize_google 需要能访问 Google 的服务,网络不通就换国内 ASR(百度、讯飞、阿里 Paraformer),接口换成对应 SDK 即可,形状一样:audio → text。
- 识别结果存在
text 变量里,下一步直接塞给大模型就行。
五、TTS 合成语音
# coding=utf-8
import dashscope
from dashscope.audio.tts import SpeechSynthesizer
dashscope.api_key = 'your-dashscope-api-key'
result = SpeechSynthesizer.call(
model='sambert-zhichu-v1', # 可换成 sambert-zhimiao-emo-v1 等带情绪的版本
text='今天天气怎么样',
sample_rate=48000,
format='wav'
)
if result.get_audio_data() is not None:
with open('output.wav', 'wb') as f:
f.write(result.get_audio_data())
print('get response: %s' % (result.get_response()))
跑完目录里会多一个 output.wav(这里存的是模型回答的音频)。
六、播放音频
pip3 install pygame
import pygame
from pygame import mixer
mixer.init()
mixer.music.load('output.wav')
mixer.music.play()
while mixer.music.get_busy(): # 播完再往下走,别急着退出
pass
七、整合成一个语音对话循环
# -*- coding: utf-8 -*-
import openai
import time
import pygame
from pygame import mixer
import dashscope
from dashscope.audio.tts import SpeechSynthesizer
import speech_recognition as sr
r = sr.Recognizer()
mixer.init()
openai.api_key = 'sk-xxxx'
dashscope.api_key = 'sk-xxxx'
conversation_history = [
{'role': 'system', 'content': 'You are a helpful assistant.'}
]
while True:
with sr.Microphone() as source:
print("请开始说话...")
audio = r.listen(source)
try:
text = r.recognize_google(audio, language='zh-CN')
print("识别结果:", text)
except sr.UnknownValueError:
print("无法识别音频")
continue
except sr.RequestError as e:
print("请求出错:", e)
continue
conversation_history.append({'role': 'user', 'content': text})
response = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=conversation_history,
max_tokens=100,
n=1,
stop=None,
temperature=0.7
)
assistant_reply = response['choices'][0]['message']['content']
result = SpeechSynthesizer.call(
model='sambert-zhimiao-emo-v1',
text=assistant_reply,
sample_rate=48000,
format='wav'
)
print("Assistant:", assistant_reply)
if result.get_audio_data() is not None:
with open('output.wav', 'wb') as f:
f.write(result.get_audio_data())
mixer.music.load('output.wav')
mixer.music.play()
conversation_history.append({'role': 'assistant', 'content': assistant_reply})
time.sleep(1)
while pygame.mixer.music.get_busy():
pass
两处原代码的小 bug 顺手修了:一是 recognize_google 异常后 text 可能未定义就往下走,会抛 UnboundLocalError,这里加了 continue;二是 pygame 用了 pygame.mixer 但只 import 了 mixer,get_busy 调用要统一。
八、性能与排错
- 延时主要花在两端:ASR 和 TTS 都是联网请求,本地只做编解码。整个来回几秒是正常的,别指望秒回。
- 识别率差:先
r.adjust_for_ambient_noise(source) 校准,再把麦克风离嘴 10–20 cm,噪音大就换指向性麦克风。
- 没声音:先
paplay output.wav 或 aplay output.wav 用命令行播一次,确认功放/耳机接对了再怪 pygame。
- 树莓派 CPU 吃紧:ARM 上跑 Python 事件循环没压力,但 TTS 的 48 kHz wav 下载占带宽,
sample_rate 降到 16000 明显更轻。
- 想换模型:ASR 换国内引擎、大模型换任何 OpenAI 兼容接口,都是替换
xxx.create(...) 那一处,整体结构不用动。
小结
整套方案拆开就四步:Recognizer 拿音频转文字 → 带 history 调大模型 → SamBERT 把回答合成 wav → pygame mixer 播完再进下一轮。跑通之后把它挂成 systemd 服务、开机自启,就是一个摆在家里随时能用的语音助手。真正耗时间的地方从来不是代码,而是麦克风硬件选型和网络连通性——这两样搞定,剩下都是拼装。
qidao123.com企服评测-应用市场 编辑提示 |