【小白也能行】树莓派智能蓝牙音箱项目实践2.0
【小白也能行】树莓派智能蓝牙音箱项目实践2.0 USB麦克风硬件接入1. USB 麦克风硬件集成步骤内容结果设备识别lsusb发现Texas Instruments PCM2902 Audio Codec✅录音设备确认arecord -l确认为 card 2, device 0✅PyAudio 设备定位设备索引为 3max inputs: 1✅2. 解决录音兼容性问题问题原因解决方案Invalid number of channels设备索引写错用了 ALSA 的 card 号 2而非 PyAudio 的索引 3改为RECORD_DEVICE_INDEX 3Invalid sample ratePCM2902 芯片不支持 16000Hz只支持 44100/48000Hz用 44100Hz 录音再ffmpeg重采样到 16000Hzinput_device_name参数报错PyAudio 不支持该参数舍弃 plughw 字符串方案改用重采样方案问题一过程回放Cannot connect to server socket errNo suchfileor directory Cannot connect to server request channel jack server is not running or cannot be started JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock 可用录音设备:[3]USB PnP Sound Device: Audio(hw:2,0)(max inputs:1)[8]pulse(max inputs:32)[13]default(max inputs:32)Expressionparameters-channelCount maxChansfailedinsrc/hostapi/alsa/pa_linux_alsa.c, line:1514ExpressionValidateParameters( inputParameters, hostApi, StreamDirection_In )failedinsrc/hostapi/alsa/pa_linux_alsa.c, line:2818❌ 无法打开录音设备2:[Errno -9998]Invalid number of channels 请根据上面列表修改 RECORD_DEVICE_INDEX 为正确的编号关键错误信息 可用录音设备:[3] USB PnP Sound Device: Audio (hw:2,0) (max inputs: 1)修复方案# ❌ 原来的RECORD_DEVICE_INDEX2# ✅ 改成RECORD_DEVICE_INDEX3# 确认22行声道数CHANNELS1# max inputs: 1必须是 1问题二过程回放Cannot connect to server socket errNo suchfileor directory Cannot connect to server request channel jack server is not running or cannot be started JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock 可用录音设备:[3]USB PnP Sound Device: Audio(hw:2,0)(max inputs:1)[8]pulse(max inputs:32)[13]default(max inputs:32)ExpressionpaInvalidSampleRatefailedinsrc/hostapi/alsa/pa_linux_alsa.c, line:2048ExpressionPaAlsaStreamComponent_InitialConfigure( self-capture, inParams, self-primeBuffers, hwParamsCapture, realSr )failedinsrc/hostapi/alsa/pa_linux_alsa.c, line:2718ExpressionPaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerBuffer, inputLatency, outputLatency, hostBufferSizeMode )failedinsrc/hostapi/alsa/pa_linux_alsa.c, line:2842❌ 无法打开录音设备3:[Errno -9997]Invalid sample rate 请根据上面列表修改 RECORD_DEVICE_INDEX 为正确的编号关键错误信息❌ 无法打开录音设备 3: [Errno -9997] Invalid sample rate 请根据上面列表修改 RECORD_DEVICE_INDEX 为正确的编号这说明PCM2902 这款 USB 声卡不支持 16000Hz 采样率。PCM2902 是标准的 48kHz 芯片只支持 48000Hz 和 44100Hz。修复方案# 不用改采样率改了会让 ASR 和录音代码耦合直接用 ALSA 的 plughw 插件它自动把任意采样率转成硬件支持的格式。# 1、删除了 input_device_index3# 2、改用 input_device_nameplughw:2,0plughw 自动处理采样率转换16000 → 48000# 3、input_device_index 设为 Nonedefrecord_audio(output_file): 录音并保存为 WAV。 使用 plughw 自动适配硬件采样率。 papyaudio.PyAudio()# 打印可用录音设备print( 可用录音设备:)foriinrange(pa.get_device_count()):devpa.get_device_info_by_index(i)ifdev[maxInputChannels]0:print(f [{i}]{dev[name]}(max inputs:{dev[maxInputChannels]}))# 用 plughw 打开 —— 自动处理采样率转换device_nameplughw:2,0try:streampa.open(formatpyaudio.paInt16,channelsCHANNELS,rateSAMPLE_RATE,inputTrue,input_device_indexNone,# 不指定索引input_device_namedevice_name,# 用 plughw 设备名frames_per_buffer1024)exceptExceptionase:print(f❌ 无法打开录音设备{device_name}:{e})pa.terminate()returnFalseprint(f 开始录音... (最长{MAX_RECORD_SECONDS}秒检测到静音自动结束))frames[]has_speechFalsesilence_frames0frames_per_secondSAMPLE_RATE/1024foriinrange(int(frames_per_second*MAX_RECORD_SECONDS)):try:datastream.read(1024,exception_on_overflowFalse)exceptException:continueframes.append(data)# 简单音量检测max_valmax(abs(int.from_bytes(data[j:j2],little,signedTrue))forjinrange(0,len(data),2))ifmax_valSILENCE_THRESHOLD:has_speechTruesilence_frames0else:silence_frames1ifhas_speechandsilence_framesframes_per_second*SILENCE_SECONDS:print( 检测到静音结束录音)breakstream.stop_stream()stream.close()pa.terminate()durationlen(frames)/frames_per_secondprint(f✅ 录音完成时长约{duration:.1f}秒)# 保存 WAVwfwave.open(output_file,wb)wf.setnchannels(CHANNELS)wf.setsampwidth(pa.get_sample_size(pyaudio.paInt16))wf.setframerate(SAMPLE_RATE)wf.writeframes(b.join(frames))wf.close()returnTrue问题三过程回放# 报错信息Cannot connect to server socket errNo suchfileor directory Cannot connect to server request channel jack server is not running or cannot be started JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock JackShmReadWritePtr::~JackShmReadWritePtr - Init notdonefor-1, skipping unlock 可用录音设备:[3]USB PnP Sound Device: Audio(hw:2,0)(max inputs:1)[8]pulse(max inputs:32)[13]default(max inputs:32)❌ 无法打开录音设备 plughw:2,0: PyAudio.Stream.__init__()got an unexpected keyword argumentinput_device_name关键错误信息❌ 无法打开录音设备 plughw:2,0: PyAudio.Stream.init() got an unexpected keyword argument ‘input_device_name’原因PCM2902 硬件只支持 48000Hz 和 44100Hz。我们用 44100Hz 录音然后调用ffmpeg重采样成 ASR 模型需要的 16000Hz。修复方案# 修改USB麦克风相关设置# 录音参数RECORD_DEVICE_INDEX3# USB 声卡在 PyAudio 中的设备索引SAMPLE_RATE44100# PCM2902 硬件支持的采样率不用 16000ASR_SAMPLE_RATE16000# ASR 模型需要的采样率CHANNELS1MAX_RECORD_SECONDS10SILENCE_THRESHOLD500SILENCE_SECONDS1.5# 添加ffmpeg重采样defrecord_audio(output_file): 录音并保存为 ASR 需要的 16kHz WAV。 硬件用 44100Hz 录音然后 ffmpeg 重采样到 16000Hz。 importtempfile papyaudio.PyAudio()print( 可用录音设备:)foriinrange(pa.get_device_count()):devpa.get_device_info_by_index(i)ifdev[maxInputChannels]0:print(f [{i}]{dev[name]}(max inputs:{dev[maxInputChannels]}))try:streampa.open(formatpyaudio.paInt16,channelsCHANNELS,rateSAMPLE_RATE,# 44100硬件原生支持inputTrue,input_device_indexRECORD_DEVICE_INDEX,frames_per_buffer1024)exceptExceptionase:print(f❌ 无法打开录音设备{RECORD_DEVICE_INDEX}:{e})pa.terminate()returnFalseprint(f 开始录音 ({SAMPLE_RATE}Hz)... (最长{MAX_RECORD_SECONDS}秒检测到静音自动结束))frames[]has_speechFalsesilence_frames0frames_per_secondSAMPLE_RATE/1024foriinrange(int(frames_per_second*MAX_RECORD_SECONDS)):try:datastream.read(1024,exception_on_overflowFalse)exceptException:continueframes.append(data)# 简单音量检测max_valmax(abs(int.from_bytes(data[j:j2],little,signedTrue))forjinrange(0,len(data),2))ifmax_valSILENCE_THRESHOLD:has_speechTruesilence_frames0else:silence_frames1ifhas_speechandsilence_framesframes_per_second*SILENCE_SECONDS:print( 检测到静音结束录音)breakstream.stop_stream()stream.close()pa.terminate()durationlen(frames)/frames_per_secondprint(f✅ 录音完成时长约{duration:.1f}秒)# 先保存为临时文件44100Hz再用 ffmpeg 重采样到 16000Hzraw_filetempfile.mktemp(suffix.wav)wfwave.open(raw_file,wb)wf.setnchannels(CHANNELS)wf.setsampwidth(pa.get_sample_size(pyaudio.paInt16))wf.setframerate(SAMPLE_RATE)wf.writeframes(b.join(frames))wf.close()# ffmpeg 重采样print( 重采样到 16kHz...)retsubprocess.run([ffmpeg,-y,-loglevel,quiet,-i,raw_file,-ar,str(ASR_SAMPLE_RATE),-ac,1,output_file],stdoutsubprocess.DEVNULL,stderrsubprocess.DEVNULL)os.unlink(raw_file)# 删除临时文件ifret.returncode!0:print(❌ 重采样失败)returnFalseprint(f✅ 已保存{SAMPLE_RATE}Hz →{ASR_SAMPLE_RATE}Hz)returnTrue至此硬件主要的问题就解决完了另外附带一个‘欢迎词’的优化过程问题四过程回放# 问题现象欢迎语有明显的卡顿和内容丢失# 原因分析通常是 TTS 音频还没完全生成就开始播放或者是蓝牙音频缓冲区初始化不及时导致的。# 修复方案通过预加载延迟来解决欢迎词卡顿和丢失内容的现象# 主循环里播放欢迎语这段添加修改# 启动问候# 原代码逻辑print( 播放欢迎语...)try:play_audio(tts(你好我是小硅你的语音助手已上线。))exceptExceptionase:print(f❌ 欢迎语失败:{e})# 修改后代码逻辑# 启动问候print( 准备欢迎语...)try:audio_datatts(你好我是小硅你的语音助手已上线。)# 等待音频数据完全就绪importtime time.sleep(0.5)print( 播放中...)play_audio(audio_data)exceptExceptionase:print(f❌ 欢迎语失败:{e})关键改动先把 TTS 结果存到变量audio_data加time.sleep(0.5)让蓝牙音频缓冲区就绪再调用play_audio3. 完整语音链路跑通 USB麦克风 (44100Hz) → ffmpeg重采样 (16000Hz) → 硅基流动 ASR → 硅基流动 LLM → 硅基流动 TTS → 蓝牙音箱全链路验证通过 ✅4. 体验优化问题调整音量太低pactl set-sink-volume DEFAULT_SINK 10%语速太快speed参数从1.0改为0.8欢迎语卡顿/丢失TTS 生成后加time.sleep(0.5)预加载 当前项目状态模块状态备注蓝牙音箱播放✅A2DP 模式音量可调USB 麦克风录音✅44100Hz → ffmpeg 重采样 16000Hz静音检测自动结束✅1.5 秒静音触发停止ASR 语音识别✅硅基流动 SenseVoiceSmallLLM 对话✅Qwen2.5-7B-Instruct多轮上下文TTS 语音合成✅CosyVoice2-0.5Bopus 格式欢迎语播放✅预加载延迟修复卡顿语速调节✅speed0.8已交付文件文件用途keyboard_assistant.py键盘版调试用voice_assistant.py完整语音版日常使用test_keyboard.py系统能力测试脚本 后续优化方向和规划 高优先级核心体验提升序号优化项说明预计耗时1开机自启systemd 服务上电自动运行语音助手20 分钟2唤醒词Picovoice Porcupine小硅小硅免按回车直接唤醒30 分钟3异常重连蓝牙断开自动回连、API 超时重试、网络恢复后自愈30 分钟4对话提示音唤醒成功/开始录音/处理完成用短促提示音替代看屏幕15 分钟 中优先级功能增强序号优化项说明预计耗时5状态指示灯USB 声卡或 GPIO LED 显示待机(暗)/唤醒(蓝)/处理(绿闪烁)/播放(绿)/错误(红)30 分钟6打断功能播放回复时按回车或说唤醒词打断立刻进入下一轮录音20 分钟7对话历史管理限制上下文长度避免 token 爆炸支持忘记之前说的重置对话15 分钟8连续对话模式回复播放完后自动进入录音无需每次都按回车20 分钟 低优先级锦上添花序号优化项说明预计耗时9自定义唤醒词训练用 Picovoice 在线训练小硅小硅替代内置词10 分钟10TTS 音色优选测试全部 8 种 CosyVoice2 音色选定最适合的15 分钟11音量自适应根据环境噪音自动调节 TTS 音量30 分钟12外壳与按键3D 打印外壳 物理按键触发录音/静音视硬件而定13日志记录对话记录保存到文件方便回溯15 分钟14OTA 升级远程更新代码和模型配置1 小时 下次继续的起点等你再次开始时建议按这个顺序推进1. 开机自启最实用插电即用 2. 唤醒词体验质的飞跃真正解放双手 3. 提示音 状态灯脱离屏幕纯语音交互 4. 对话历史管理稳定运行的基础 项目进度核心功能 ██████████████████████████████████████████████████ 100% ✅ 语音交互链路 ██████████████████████████████████████████████████ 100% ✅ 体验优化 ████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░ 60% ⏳ 系统集成 ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 5% ⏳ ──────────────────────────────────────────────────── 总体进度 ████████████████████████████████░░░░░░░░░░░░░░░░ 66% 分项明细大类子项状态核心功能蓝牙音箱播放✅USB 麦克风录音✅ASR 语音识别✅LLM 对话生成✅TTS 语音合成✅完整链路跑通✅语音交互链路多轮对话上下文✅静音检测自动结束✅键盘版调试用✅语音版日常用✅体验优化音量调节✅语速调节✅欢迎语卡顿修复✅唤醒词⏳提示音⏳系统集成开机自启⏳异常重连⏳状态指示灯⏳外壳/按键⏳一句话总结能用了但还不够无感。核心链路 100% 打通差的都是让它更好用的活儿。下次从唤醒词和开机自启下手体验会有质的飞跃