拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ESP32-S3-N16R8打造麦金塔AI小机器人:从硬件选型到固件调试全攻略

ESP32-S3-N16R8打造麦金塔AI小机器人:从硬件选型到固件调试全攻略

1. 为什么我选了ESP32-S3-N16R8来做这台麦金塔小机器人

1.1 从一堆吃灰的开发板里翻出这块模组

去年年底收拾工作台,翻出来好几块开发板,ESP8266、ESP32-C3、STM32F103,还有一块买了好久一直没动的ESP32-S3-N16R8。当时买它纯粹是因为看到参数眼馋:16MB Flash加8MB PSRAM,双核LX7跑到240MHz,还带向量指令,支持Wi-Fi和蓝牙5.0。这个配置放在桌面级AI机器人项目里,属于"杀鸡用牛刀"的级别,但恰恰是这种冗余让我后面省了很多事。

很多人做桌面小机器人第一反应是树莓派,理由很直接:Linux环境、Python生态、跑大模型方便。我一开始也这么想,但实际搭起来发现几个问题。树莓派Zero 2W跑一个语音唤醒加云端API调用,功耗在1.5W到2.5W之间,需要持续供电,电池方案要么笨重要么续航短。而且Linux启动慢,从通电到能响应语音指令,快的话也要二十多秒。桌面摆件这种东西,用户的心理预期是"碰一下就有反应",不是等它开机。

ESP32-S3-N16R8的方案就完全不一样。上电到Wi-Fi连接完成、语音服务就绪,实测在3秒以内。深度睡眠模式下电流可以压到几十微安,用一块2000mAh的锂电池能撑好几天。最关键的是,8MB PSRAM让它可以缓存音频数据、跑轻量级的本地关键词识别模型,不需要所有东西都往云端扔。

1.2 麦金塔外壳不只是情怀,它解决了散热和结构问题

选麦金塔造型,一开始确实是情怀驱动。那个经典的方正造型、CRT屏幕的位置、软驱口的开口,放在桌面上辨识度极高。但真正动手之后发现,这个造型在工程上意外地合理。

麦金塔的机身厚度足够,内部空间能塞下一块3.7V锂电池、一个1.5英寸的圆形LCD、一个I2S数字麦克风、一个MAX98357功放模块和一个小尺寸扬声器。CRT屏幕的位置刚好用来放LCD,软驱口的开口尺寸正好可以引出USB-C充电口和几个按键。机身背面的散热孔原本是给CRT用的,现在用来给ESP32-S3散热刚刚好,连续跑几个小时语音交互,模组表面温度稳定在45度左右,不会触发降频。

外壳我一开始想3D打印,但后来发现用激光切割亚克力板加胶水拼接更省事。3D打印的层纹在麦金塔这种方正造型上特别明显,后期打磨很费时间。亚克力板切割边缘用砂纸过一遍,再用亚克力胶水粘合,出来的效果干净利落。屏幕窗口和按键开孔用激光切割精度足够,不需要二次加工。

1.3 这个项目适合谁动手

如果你有基本的焊接经验,能看懂简单的电路图,会用Arduino IDE或者PlatformIO烧录固件,这个项目完全可以自己搞定。不需要你会画PCB,所有模块都是现成的开发板加杜邦线连接。不需要你会写AI模型,语音识别和对话逻辑走的是现成的云端API,固件里已经封装好了调用逻辑。

整个项目的物料成本大概在150到200元之间,具体取决于你手头已经有哪些零件。ESP32-S3-N16R8模组大概40到50元,LCD屏幕30元左右,麦克风和功放模块加起来20元,锂电池和充电管理模块30元,外壳材料20元,剩下的就是杂七杂八的线材和螺丝。

我见过很多人卡在"选什么屏幕""用什么麦克风"这种问题上纠结好几天。这篇内容会把每个模块的选型理由和接线方式都讲清楚,你照着做就行。固件我会提供编译好的bin文件,也会说明怎么自己从源码编译。

2. 物料清单与模块选型:每一分钱花在哪里

2.1 核心模组:ESP32-S3-N16R8的引脚分配策略

ESP32-S3-N16R8的引脚比普通ESP32多不少,但实际用起来还是会紧张。我先把整个项目的引脚需求列出来,再分配。

需要占用的引脚包括:I2S麦克风(3根:BCLK、WS、DATA)、I2S功放(3根:BCLK、WS、DATA)、LCD屏幕(SPI接口,5根:SCK、MOSI、CS、DC、RST,再加一根背光控制)、按键(2根)、电池电压检测(1根ADC)、状态LED(1根)。加起来一共15根。

ESP32-S3-N16R8可用的GPIO有三十多个,但要注意有些引脚有特殊功能。GPIO0是BOOT按键,GPIO19和GPIO20是USB D+/D-,GPIO43和GPIO44是UART0的TX/RX。这些引脚尽量避开,或者确认不影响功能后再用。

我的分配方案是这样的:麦克风I2S用GPIO4、5、6,功放I2S用GPIO7、8、9,LCD的SPI用GPIO10到14,按键用GPIO1和GPIO2,电池检测用GPIO3,状态LED用GPIO21。这样分配的好处是所有外设的引脚都连续,走线整齐,杜邦线不容易插错。

注意:ESP32-S3的I2S外设有两个,可以同时使用。麦克风和功放各用一个I2S控制器,互不干扰。如果你把两个设备挂在同一个I2S上,需要做时分复用,固件复杂度会上升很多。

2.2 显示模块:1.5英寸圆形LCD还是方形IPS

麦金塔的CRT屏幕区域是方形的,但实际显示区域可以做成圆形或者方形。我试过两种方案。

圆形LCD的好处是视觉上更像一个"眼睛"或者"脸",配合机器人的表情动画很自然。缺点是圆形屏幕的驱动IC通常是GC9A01,SPI接口,分辨率240x240,刷新率有限,做复杂动画会卡。而且圆形屏幕的可视角度一般,从侧面看颜色偏白。

方形IPS屏幕我选的是1.5英寸240x240的ST7789V驱动,SPI接口,刷新率比GC9A01高不少,颜色也更正。方形屏幕在麦金塔的方形窗口里刚好填满,看起来更像原版CRT的显示区域。表情动画用方形画布做,边缘留黑边,效果也不错。

最后我选了方形IPS。理由是固件里做表情动画需要频繁刷新屏幕,ST7789V的SPI时钟可以跑到80MHz,GC9A01通常只能到40MHz。刷新率差一倍,动画流畅度差别很明显。

2.3 音频输入输出:I2S麦克风与功放模块的搭配

麦克风我用的INMP441,I2S接口,24位ADC,信噪比61dB。这个麦克风在桌面环境下拾音效果很好,三米内正常说话音量就能触发唤醒。它的灵敏度可以通过固件里的增益参数调整,默认增益下,安静房间里连翻书的声音都能拾到,所以实际使用时我把增益调低了6dB,避免误触发。

功放用的MAX98357,I2S输入,D类功放,最大输出3.2W。搭配一个4欧姆3W的小扬声器,音量足够一个房间使用。MAX98357的好处是只需要I2S的三根线加电源,不需要额外的DAC或者滤波电路。它的增益可以通过SD引脚和GAIN引脚配置,我设的是9dB增益,再高会有底噪。

麦克风和功放的I2S时钟需要分开配置。麦克风是输入,ESP32-S3作为I2S主机产生时钟;功放是输出,ESP32-S3也作为主机产生时钟。两个I2S控制器独立工作,互不干扰。固件里我用的是ESP-IDF的I2S标准模式驱动,配置起来比老版的I2S驱动清晰很多。

2.4 电源方案:锂电池与充电管理的取舍

电源部分我一开始想用18650电池,容量大,放电曲线平稳。但18650电池加电池座太占空间,麦金塔机身内部放不下。后来换成一块2000mAh的软包锂电池,尺寸刚好塞在机身底部。

充电管理用的是TP4056模块,带保护板。TP4056的充电电流可以通过PROG电阻设置,我设的是1A,充2000mAh电池大概两个半小时充满。模块上自带蓝色和红色LED指示充电状态,我把LED用导线引到机身背面,透过亚克力板能看到充电指示。

电池电压检测用ESP32-S3的ADC引脚,通过一个100k和100k的电阻分压,把电池电压降到ADC量程范围内。固件里定期读取ADC值,换算成电池百分比,显示在屏幕角落。分压电阻用1%精度的金属膜电阻,普通碳膜电阻的误差太大,电量显示会跳。

提示:锂电池放电到3.3V以下时,ESP32-S3的Wi-Fi模块可能会工作不稳定。固件里我设了3.4V低压警告,3.2V自动关机。不要等到电池完全放空再充电,对电池寿命不好。

3. 电路连接与外壳组装:从散件到整机

3.1 接线图与焊接注意事项

整个项目的接线分三部分:电源部分、I2S音频部分、SPI显示部分。我建议先接电源部分,确认供电正常后再接其他模块。

电源部分的接线:锂电池的正负极接到TP4056模块的B+和B-,TP4056的OUT+和OUT-接到ESP32-S3的5V和GND。注意TP4056模块的输出电压是电池电压,不是稳定的5V。ESP32-S3的5V引脚内部有LDO稳压到3.3V,所以电池电压在3.4V到4.2V之间时,模组都能正常工作。

I2S麦克风的接线:INMP441的VDD接3.3V,GND接GND,SCK接GPIO4,WS接GPIO5,SD接GPIO6,L/R接GND(选择左声道)。麦克风的时钟线要尽量短,超过10厘米容易引入噪声。我用的是10厘米的杜邦线,实际测试底噪在可接受范围内。

I2S功放的接线:MAX98357的VIN接5V,GND接GND,BCLK接GPIO7,LRC接GPIO8,DIN接GPIO9。GAIN引脚悬空(默认9dB增益),SD引脚悬空(使能输出)。功放的电源线要粗一点,我用的是22AWG的线,细线在大音量时会有压降,导致声音失真。

SPI屏幕的接线:ST7789V的VCC接3.3V,GND接GND,SCL接GPIO12,SDA接GPIO11,RES接GPIO13,DC接GPIO14,CS接GPIO10,BLK接GPIO15(背光控制)。屏幕的背光LED需要限流电阻,我用的100欧姆,背光电流大概15mA,亮度足够。

焊接的时候注意,ESP32-S3-N16R8模组的引脚间距是2.54mm,普通烙铁头就能焊。但模组底部的散热焊盘需要焊到PCB上,如果你用的是模组而不是开发板,这个焊盘必须焊,否则大负载时模组会过热。我直接买的开发板,散热焊盘已经处理好了,省事。

3.2 麦金塔外壳的切割与组装

外壳我用的是3mm厚的黑色亚克力板,激光切割。图纸是我自己画的,参考了原版麦金塔的尺寸比例,整体缩小到大约三分之一。机身尺寸是120mm宽、140mm高、100mm深。

前面板需要开三个孔:屏幕窗口(40mm x 40mm)、软驱口(30mm x 10mm,用来引出USB-C)、按键孔(两个6mm圆孔)。屏幕窗口的位置要精确,偏1mm就会露出屏幕边框。我在图纸上标了定位孔,切割后用屏幕实际比对了一下,刚好卡住。

侧板和顶板用亚克力胶水粘合。胶水涂薄薄一层,压紧后等24小时完全固化。粘合的时候用直角夹具固定,保证机身方正。背板用螺丝固定,方便后期拆开维护。背板上开散热孔,用3mm钻头打一排孔,间距5mm。

屏幕用热熔胶固定在前面板内侧。热熔胶的好处是固化快,后期要拆的话用酒精就能软化。麦克风用双面胶贴在机身顶部内侧,靠近前面板的位置,拾音效果最好。扬声器用螺丝固定在机身底部,出声孔朝下,利用桌面反射,声音更饱满。

3.3 上电前的检查清单

接线完成后不要急着上电,先做一遍检查。我列了一个清单,每次装新机器都过一遍。

  • 用万用表测电池电压,确认在3.7V到4.2V之间
  • 测TP4056输出端电压,应该和电池电压一致
  • 测ESP32-S3的3.3V引脚对GND的电阻,正常应该在几百欧姆以上,如果接近0说明有短路
  • 检查所有I2S和SPI线是否插紧,杜邦线有时候会松动
  • 确认麦克风的L/R引脚接GND,如果悬空会导致声道错乱
  • 确认屏幕的背光限流电阻已接,直接接3.3V会烧背光

上电后先看状态LED是否亮起,然后听扬声器有没有底噪。如果底噪很大,检查功放的电源线和地线是否接触良好。如果屏幕不亮,先测背光电压,再测SPI信号。ESP32-S3的SPI引脚在启动时会有短暂的电平变化,这是正常的。

4. 固件烧录与配置:让机器人开口说话

4.1 固件下载与烧录工具的选择

固件我提供了两个版本:一个是编译好的bin文件,直接用esptool烧录;另一个是完整的源码工程,用PlatformIO编译。

bin文件的烧录地址是0x0,包含bootloader、分区表和应用程序。烧录命令是这样的:

esptool.py --chip esp32s3 --port /dev/ttyUSB0 --baud 921600 write_flash 0x0 firmware.bin

Windows下端口号可能是COM3或者COM4,在设备管理器里看。Linux下通常是/dev/ttyUSB0或者/dev/ttyACM0。烧录前需要按住BOOT键,按一下RST键,进入下载模式。烧录完成后按RST键重启。

如果你要自己编译源码,需要安装PlatformIO。在VSCode里装PlatformIO插件,打开工程目录,它会自动下载ESP-IDF和依赖库。编译命令是pio run,烧录命令是pio run --target upload。第一次编译会下载几百MB的工具链,耐心等。

注意:ESP32-S3-N16R8的Flash是16MB,分区表要选对。我用的分区表是"16MB Flash (3MB APP/9.9MB FATFS)",应用程序分区3MB,足够放语音识别模型和表情动画资源。如果你自己改分区表,注意APP分区不要小于2MB,否则固件放不下。

4.2 Wi-Fi与云端API配置

固件第一次启动时会进入配网模式,屏幕上显示一个二维码,用手机扫码连接机器人的热点,然后输入你家Wi-Fi的SSID和密码。配网信息保存在NVS里,下次启动自动连接。

云端API我用的是一个语音对话服务,支持语音识别、对话生成和语音合成。固件里封装了API调用逻辑,你只需要在配置文件中填入API Key和API Secret。配置文件在源码的main/config.h里,bin文件版本的话,配网时在手机页面上填入。

API的调用流程是这样的:麦克风采集音频,经过降噪和VAD(语音活动检测)后,把音频数据通过WebSocket传到云端,云端返回识别文本和对话回复,固件再把回复文本通过TTS合成语音,用功放播放出来。整个流程的延迟在1.5秒到2.5秒之间,取决于网络状况。

如果你不想用云端API,固件也支持本地关键词识别。ESP-SR是乐鑫的离线语音识别库,支持自定义唤醒词和命令词。本地识别只能处理预设的命令,比如"打开灯""播放音乐",不能做自由对话。本地识别的延迟在200ms以内,响应很快。

4.3 表情动画与交互逻辑的调整

屏幕上的表情动画是用LVGL做的。LVGL是一个轻量级图形库,在ESP32-S3上跑240x240的界面很流畅。我做了几组表情:待机时是眨眼的圆形眼睛,识别到语音时变成放大的瞳孔,思考时是转圈的眼睛,说话时是跳动的波形。

表情切换的逻辑在main/face.c里。待机状态每3秒随机眨眼一次,识别到唤醒词后切换到"聆听"表情,云端返回结果后切换到"思考"表情,TTS播放时切换到"说话"表情。这些状态之间的切换用了一个简单的状态机,代码不复杂,你可以根据自己的喜好调整。

按键的功能是这样的:短按第一个键切换表情主题,短按第二个键切换对话模式(云端/本地),长按第一个键进入配网模式,长按第二个键重启。按键消抖用的是ESP-IDF的GPIO中断加软件定时器,消抖时间20ms。

5. 调试过程中遇到的几个坑

5.1 I2S时钟冲突导致音频断断续续

第一次跑通的时候,麦克风和功放单独测试都正常,但一起工作的时候,扬声器里的声音会每隔几秒卡一下。用逻辑分析仪抓I2S波形,发现麦克风的BCLK和功放的BCLK有相位差,偶尔会冲突。

原因是两个I2S控制器的时钟源虽然独立,但ESP32-S3的I2S时钟树有共享的PLL。当两个I2S的采样率不一致时,PLL会重新锁定,导致短暂的时钟丢失。解决办法是把麦克风和功放的采样率设成一样的,都用16kHz。麦克风采集16kHz,功放播放16kHz,PLL不需要切换,冲突就消失了。

如果你需要用不同的采样率,比如麦克风16kHz、功放48kHz,那就需要把其中一个I2S设成从模式,用外部时钟。但这样需要额外的时钟芯片,电路复杂度上升。对于桌面机器人这种场景,16kHz的语音质量已经足够,没必要折腾。

5.2 锂电池供电时Wi-Fi频繁掉线

用USB供电调试的时候一切正常,换成锂电池供电后,Wi-Fi每隔几分钟就掉一次。一开始以为是电池电量不足,但测电压还有3.8V。后来用示波器看电源纹波,发现Wi-Fi发射瞬间,电池电压会瞬间跌到3.2V以下,导致模组复位。

原因是TP4056模块的输出端没有大电容,电池的内阻在瞬间大电流下产生压降。解决办法是在TP4056的输出端并一个470uF的电解电容加一个100nF的陶瓷电容。电解电容提供瞬态电流,陶瓷电容滤高频噪声。加上之后,Wi-Fi发射时的电压跌落从600mV降到了100mV以内,掉线问题解决。

提示:如果你用的电池内阻比较大(比如拆机电池),电容还要加大。我试过用1000uF的电容,效果更好。但电容体积也大,麦金塔机身里要找个位置放。

5.3 屏幕SPI干扰麦克风导致识别率下降

屏幕刷新的时候,麦克风的底噪会明显增大,语音识别率从90%掉到60%。用频谱分析仪看,屏幕SPI的时钟频率是40MHz,谐波刚好落在麦克风的音频频段内,通过电源线和空间辐射耦合到了麦克风。

解决办法有两个:一是降低SPI时钟到20MHz,刷新率降一半,但表情动画还能接受;二是在麦克风的电源引脚上加一个LC滤波,10uH电感和100nF电容组成低通,滤掉高频干扰。我两个都做了,SPI降到20MHz,麦克风电源加LC滤波,识别率恢复到92%以上。

屏幕的排线也要尽量远离麦克风。我一开始把屏幕排线和麦克风线捆在一起,干扰特别严重。后来把屏幕排线走机身左侧,麦克风线走右侧,中间隔了电池,干扰小了很多。

5.4 云端API的音频格式匹配问题

云端API要求的音频格式是16kHz、16bit、单声道PCM,但INMP441输出的是24bit数据。固件里需要做位深转换,把24bit截断成16bit。我一开始直接取高16位,结果音量很小,因为低8位被丢弃了。正确的做法是取高16位后,根据低8位的值做舍入,或者直接右移8位再加抖动。

TTS返回的音频是MP3格式,需要解码成PCM才能用I2S播放。固件里集成了一个轻量级的MP3解码器,解码后的PCM数据缓存到PSRAM里,再通过I2S输出。PSRAM的8MB容量在这里派上了用场,可以缓存好几秒的音频,避免网络抖动导致播放卡顿。

如果你用的云端API返回的是其他格式,比如Opus或者AAC,需要换对应的解码器。Opus的解码器比MP3复杂一些,但压缩率更高,适合网络状况不好的场景。

6. 几个可以继续折腾的方向

6.1 加一个舵机做头部转动

现在的机器人是固定不动的,只能通过屏幕表情和声音交互。如果想让它更生动,可以加一个小型舵机,让头部能左右转动。SG90舵机体积小,扭矩够用,用ESP32-S3的LEDC外设驱动,占一个GPIO就行。

舵机的电源要从电池直接取,不要从3.3V取,舵机启动瞬间的电流会拉低3.3V,导致模组复位。控制信号用GPIO16,PWM频率50Hz,脉宽500us到2500us对应0到180度。固件里可以根据对话内容控制头部转动,比如识别到声音从左边来,头就转向左边。

6.2 本地唤醒词的自定义训练

ESP-SR支持自定义唤醒词,但需要用乐鑫的工具链训练模型。训练过程不复杂,在乐鑫的语音识别平台上上传几段唤醒词的录音,等几分钟就能生成模型文件。把模型文件放到固件的分区里,改一下配置就能用。

我训练了一个"小智小智"的唤醒词,识别率在安静环境下95%以上,有背景音乐的时候会降到80%左右。如果你想要更高的识别率,可以多录几组不同口音和语速的样本。训练模型的大小在200KB左右,放在3MB的APP分区里绰绰有余。

6.3 用MQTT接入智能家居

ESP32-S3的Wi-Fi可以同时维持WebSocket和MQTT两个连接。固件里加一个MQTT客户端,把语音识别的结果发布到MQTT主题,就可以控制家里的智能设备了。比如你说"打开客厅的灯",固件识别到"灯"和"开"两个关键词,就往MQTT发一条消息,Home Assistant收到后执行动作。

MQTT的配置在main/mqtt.c里,填上Broker地址、端口、用户名和密码就行。我用的Mosquitto Broker跑在本地服务器上,延迟在100ms以内。如果你没有本地服务器,也可以用公共的MQTT Broker,但要注意数据安全,不要发敏感信息。

6.4 电池续航的进一步优化

现在的续航在连续对话模式下大概4小时,待机模式下2天。如果想更长,可以做几个优化。一是把屏幕背光亮度调低,从15mA降到5mA,续航能多出几个小时。二是把Wi-Fi的省电模式打开,ESP32-S3支持Modem-sleep,在不需要通信的时候自动降低Wi-Fi功耗。三是把CPU频率从240MHz降到160MHz,语音识别和LVGL刷新都够用,功耗降低30%左右。

我实测过,背光降到5mA、Wi-Fi开省电、CPU降到160MHz,连续对话续航能到6小时,待机续航4天。代价是屏幕在强光下看起来有点暗,语音识别的响应速度稍微慢一点点,但都在可接受范围内。

最后分享一个我在装配时的小技巧:所有杜邦线连接好后,用热熔胶在接插件根部点一下,固定住。桌面机器人会经常被碰到,杜邦线松动是最高频的故障。热熔胶固定后,即使用力晃动机身,线也不会掉。后期要拆的话,用酒精棉签擦一下热熔胶就软化了,不会损坏接插件。

返回列表