当前位置:首页 > 工业控制 > 电路设计项目集锦
[导读]Alex是一款完全DIY的AI语音助手,基于普通的ESP32开发板构建而成——无需云音箱中转服务,无需专用硬件,也无需树莓派。它通过I2S麦克风进行声音采集,使用OpenAI的Whisper API转录语音,借助GPT生成真实回答,并通过有线放大器和扬声器将回复大声播放出来。整个过程实时完成,搭配一块小型OLED显示屏,可动态呈现生动的“小狗眼”,随着助手聆听、思考和说话时做出相应反应。

Alex:在裸露的ESP32上构建AI语音助手

Alex是一款完全DIY的AI语音助手,基于普通的ESP32开发板构建而成——无需云音箱中转服务,无需专用硬件,也无需树莓派。它通过I2S麦克风进行声音采集,使用OpenAI的Whisper API转录语音,借助GPT生成真实回答,并通过有线放大器和扬声器将回复大声播放出来。整个过程实时完成,搭配一块小型OLED显示屏,可动态呈现生动的“小狗眼”,随着助手聆听、思考和说话时做出相应反应。

本文详细介绍了该项目从零开始的构建过程,涵盖每个环节的实现细节,包括实际调试经历——因为项目的困难程度与最终成果同样重要。

所用硬件:

- ESP32开发板(ESP32-D0WD-V3,标准开发模块,无PSRAM)

- INMP441 — I2S MEMS麦克风

- MAX98357A — I2S 3W Class D功放扩展板

- 3W扬声器

- SSD1306 OLED显示屏 — 0.96英寸,128x64分辨率,I2C接口

接线配置

注意:为放大器专门提供5V电源(而非ESP32自身的5V供电)是刻意做出的选择——与WiFi当前的瞬时电流峰值共享电源会导致录音初期出现音频异常。

第一部分:核心语音处理流程

该项目的基础是一个五步循环:

•监听 — ESP32持续采样I2S麦克风,检测是否有持续超过阈值的音量突增。需要连续几段响亮的声音片段后才触发记录,因此避免了短暂的噪音尖峰(如门关闭、椅子吱呀声)误触发录音。

•录制 — 触发后,系统会持续记录直到检测到自然静止,然后停止。

•转录 — 捕获的音频片段被打包成WAV文件(手动构建,包含完整头部信息),并发送至OpenAI的Whisper API。

•思考 — 转录后的文本通过Chat Completions API发送给GPT,以获得真实且具有对话感的回答。

•语音输出 — 回答由OpenAI的TTS API生成,并将原始PCM音频直接通过I2S流式传输至MAX98357A放大器,无需先等待完整响应下载。

已过滤掉一些已知的Whisper“幻觉”短语(例如“感谢观看”),因为几乎无声或仅含噪音的片段往往会导致这些内容持续出现,而非真正不输出任何文字。

第二部分:添加OLED“面部”

为了让助手看起来更生动,而不仅仅是一个对音频做出反应的黑箱,我们加入了SSD1306 OLED显示屏,通过动态的狗狗眼睛动画来表现助手的状态:

•空闲/聆听——眼睛周期性眨眼,瞳孔轻微左右移动

•识别语音——实际转录的文字会短暂显示

•思考中——瞳孔更活跃地来回闪烁,等待AI响应

•说话中——AI的回答在被朗读时实时显示

•错误——API调用失败时显示简单的皱眉或错误表情

关键设计决策:OLED独立运行于自己的FreeRTOS任务中,并固定在ESP32的第二个核心上。这一点很重要,因为Whisper、GPT和TTS的调用都会阻塞HTTP请求,每个请求可能耗时1至3秒。如果屏幕更新与这些调用在同一任务中进行,动画就会在调用过程中卡住,无法流畅播放。在主管道运行于核心1的同时,将子程序独立运行于核心0,可确保整个周期内面部保持活跃,并通过一个受小范围互斥保护的共享状态进行通信。

遇到的问题及解决方法

1. 编译错误:setBufferSizes 不是 WiFiClientSecure 的成员

早期尝试修复内存问题(见下文)时使用了 client.setBufferSizes(1024, 512) — 这是一个在旧版 Arduino-ESP32 核心版本中存在但自核心 3.x 起被移除的方法。该版本用新的 mbedtls 基于实现(NetworkClientSecure)取代了原有的 BearSSL 基于的 WiFiClientSecure,而后者不再提供运行时 TLS 缓冲区控制功能。修复方法:完全移除了相关调用,并以另一种方式解决了底层的内存问题(见下文)。

2. “直接连接失败”——TLS握手偶尔失败

当天首次进行的 HTTPS 调用(启动时的独立 TTS 测试)会失败连接,尽管 WiFi 本身已正常连接。诊断结果显示,ESP32 的总可用堆内存充足,但连续可用堆内存不足——TLS 握手需要一个大而连续的内存块,而其他分配(如麦克风采样缓冲区、JSON 解析、WiFi 栈缓冲区)导致的碎片化使得连续可用内存不足 40KB,正好处于 mbedtls 所需内存的边缘。修复方法:将 MAX_RECORD_SECONDS 从 3 减少到 2,缩小了音频上传缓冲区,并释放了约 32KB 的连续堆内存。仅此一项就足以使 TLS 连接变得可靠。

3. OLED完全无显示

这是项目中调试过程最长的一次,经历了多个误判后才找到真正原因:

首先怀疑线路问题——但当心跳打印确认显示任务正常运行,且display.begin()返回成功(即真实的I2C握手)后,该问题被排除。

接着怀疑是错误的OLED控制器(SH1106与SSD1306混淆,廉价模块中常见匹配错误)——但经确认模块为真正的0.96英寸SSD1306后,问题依然存在。

然后怀疑是GPIO引脚损坏——通过物理连通性检查确认线路本身端到端连接良好。

最终发现真正原因是:OLED模块需要使用SSD1306_EXTERNALVCC供电模式,而不是display.begin()函数中默认传递的SSD1306_SWITCHCAPVCC电源模式。由于模式设置错误,I2C握手和所有初始化命令均能成功执行,但显示屏始终无法接收到正确的内部驱动电压以实现点亮。解决方法:将电源模式常量更改为SSD1306_EXTERNALVCC后,问题彻底解决。

4. 在测试OLED接线时,尝试将GPIO 22用作I2C引脚,但该引脚已在组装好的PCB上物理焊接至放大器的DIN线路,导致两个外设会争夺同一根线路。解决方法:将OLED的I2C时钟线移至一个真正空闲的引脚(GPIO 19)上。

5. TTS播放声音显得过快

当管道端到端完全正常运行后,AI的语音回复听起来像是以正常音调播放得过快——这排除了采样率不匹配的可能性,反而指向了TTS语音的默认语速问题。解决方法:在TTS API请求中添加了“speed”参数0.85,使语音速度放缓,更符合自然语速。

6. 必须非常靠近麦克风说话

INMP441 麦克风灵敏度较高,但原始样本值在处理时未经过放大,而扬声器路径已预先设置了增益。问题修复:在触发检测和保存/转录音频之前,为每个麦克风样本添加了软件增益(MIC_GAIN),从而提升灵敏度,确保正常说话距离下的声音能够可靠采集。

本项目展示的内容

除了最终成果之外,这个构建还涉及多个嵌入式系统的基本原理,这些内容在高级教程中容易被忽略:

- 手动配置 I2S 接口,分别用于录音(麦克风)和流媒体播放(扬声器),包括原始 WAV 头部的构建

- 流式处理 HTTP 响应(分块传输编码),并实时播放音频,而非缓冲整个文件

- 区分堆碎片化与总可用堆空间,明确它们是两个不同问题

- 双核 FreeRTOS 任务设计,使用户界面保持响应,同时主任务因慢速网络调用而阻塞

系统化的硬件调试——逐个隔离变量(线路、软件、电源或元件缺陷),而不是盲目猜测

本文编译自hackster.io

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭