复刻小智AI,ESP32-S3搭建Arduino+ESP-SR+ESP-TTS开发环境踩坑记录

最近 B 站上赛博小狗和小智 AI 都很火,我也想复刻一个,最好能把两者结合一下,研究了一下,发现作者们的开源方案有些地方不太符合我的习惯,准备改造一番,然后就走上了踩坑之旅。

缘由

赛博小狗这个开源项目,作者使用了现成的语音识别模块和文本转语音模块,这两个模拟贵且不说,在使用上也不太灵活,而且主控也是使用的 STM32,这个如果要想改造结合小智 AI,开发起来也比较麻烦。

然后就想到了乐鑫 ESP32 是已经自带了语音唤醒、语音识别、文本转语音能力的,那干嘛不直接用一个 MCU 来实现外部模块的功能,成本就要低上很多了,而且小智 AI 本身也是基于 ESP32 去做的,结合起来就更方便了。

再研究了一下小智 AI 的开源硬件端项目,发现是基于 ESP-IDF 开发的,这就叫人头大了,虽然原厂的 ESP-IDF 相比 Arduino 框架更强大,配置更灵活,但是从开发便捷性上来说,我还是更喜欢使用 Arduino 框架。

为了把这一堆东西都改成自己喜欢的样子,就开始了踩坑之旅。

开发环境

虽然在玩 ESP32 时一直用的 Arduino 框架,但是我并没有使用 Arduino IDE,而是使用的 Visual Studio Code + PlatformIO 插件。Ardunio IDE 的编辑器太古老了,缺少很多有用的功能,像代码提示、自动补全、符号跳转等功能都是缺失的。

而 Visual Studio Code 在这方面就强大很多了,而且得益于最近 AI 的流行,可以通过 Codium 或者 Cursor 等 AI 编程工具实现更高效的编码。另外 PlatformIO 还支持多个 MCU 平台的 SDK 管理和编译,可以实现一个 App 完成多种 MCU 固件的开发工作。

这次搭建 ESP32 + ESP-SR + ESP-TTS 开发环境也是基于 VSCode + PlatformIO 来完成的,也推荐电子 DIY 和 Arduino 爱好者尝试一下这套开发环境。

ESP-SR 库找不到?

第一步先尝试一下官方语音识别 ESP-SR 中的示例,找到 arduino-esp32 仓库中 ESP_SR 的示例,原样复制代码过来,一编译,直接找不到头文件 ESP_I2S.hESP_SR.h

esp32-arduino-esp-sr-tts-2

然后在 platformio 的 framework-arduinoespressif32 的目录里看了下,还真没有……

费了半天劲搜索了一下,才发现 PlatformIO 使用的 arduino-esp32 仓库,是另外一个项目中打包的,在打包的时候,删除了 ESP-SR 相关的库。

又花了点时间,找到一个 pioarduino/platform-espressif32 仓库,这个仓库会紧跟官方上游仓库来生成使用最新 ESP-IDF 打包的 Arduino 框架,只需要在 platformio.ini 中将 platform 字段指定他们的仓库就可以了。

esp32-arduino-esp-sr-tts-3

保存之后,等待 PlatformIO 下载依赖,编码终于可以编译通过了。

I2S 初始化失败?

拿上最新的编译、烧录,打开串口监视器,咋一直重启呢,暂停一下日志,发现了都挂在了下面这个错误:

esp32-arduino-esp-sr-tts-4

看着似乎是因为我用了 ESP32-S3-N16R8 带 PSRAM 的版本,导致在初始化 I2S 的时候,中断函数放在了 PSRAM 里面,而不是 SRAM 里。

但是我这测试代码也没改啥,按理分配到 PSRAM 需要额外指定的,示例代码里并没有这么做。

继续费劲地找,还真找到了在踩坑时几天前有人提的 issue I2S failed to set up tx callback,原来是一个官方 SDK 打包时参数错误的问题,好嘛……

esp32-arduino-esp-sr-tts-5

评论里提到了会在 3.2.0-RC2 中修复,那就等等修复后的 arduino-esp32 吧。

幸好是用了 16M Flash,分区才够用

等过了一周,终于有新版本的 arduino-esp32 可以用了,固件也可以正常在 ESP32 上跑起来了,但是运行的时候还是会出现错误说没有找到唤醒词。

搜了下,原来模型是得单独上传的,需要在 Flash 上单独创建一个分区,在某个教程中建议可以使用 esp_sr_16.csv 这个预置的分区配置。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,       0x9000,   0x5000,
otadata,  data, ota,       0xe000,   0x2000,
app0,     app,  ota_0,    0x10000, 0x300000,
app1,     app,  ota_1,   0x310000, 0x300000,
spiffs,   data, spiffs,  0x610000, 0x700000,
model,    data, spiffs,  0xD10000, 0x2E0000,
coredump, data, coredump,0xFF0000,  0x10000,

这个分区配置中创建了一个标签为 model,大小为 2944KB 的分区,用于存放 ESP-SR 需要的模型,包括语音唤醒和命令识别两种用途的模型。

在使用了这个分区表之后,下一步配置唤醒模型是可以了,但是更后面的 TTS 模型又需要额外分区,而且这里的分区大小也不太够用,因此最终还是改了一下分区表,使用自定义配置。

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,       0x9000,   0x5000,
otadata,  data, ota,       0xe000,   0x2000,
app0,     app,  ota_0,    0x10000, 0x300000,
app1,     app,  ota_1,   0x310000, 0x300000,
spiffs,   data, spiffs,  0x610000, 0x100000,
model,    data, spiffs,  0x710000, 0x5E0000,
voice_data,data,fat,     0xCF0000, 0x300000,
coredump, data, coredump,0xFF0000,  0x10000,

额外的两个分区配置:

  • model:用于存放语音唤醒模型,大小 6016KB
  • voice_data:用于存放 TTS 模型,大小 3072KB

为了使用 MultiNet6,直接支持用拼音配置中文指令,所以这里把 model 分区大小调整到了 6MB 多,要不然就放不下 WakeNet 和 MultiNet 模型了。

唤醒模型该怎么配置啊?

很好,终于可以跑起来了,一运行,继续提示缺少提示词,明白,上面创建了分区,但是还没有上传模型数据。

esp-sr 项目中找到 WakeNet 和 MultiNet 的模型数据,在项目主页也可以看到目前已经有的唤醒词。

esp32-arduino-esp-sr-tts-6

我在这里选择了 小鸭小鸭 作为唤醒词。

另外还需要 MultiNet 的指令识别模型,为了使用中文,我选择了 mn6_cn 这个,同样可以在 esp-sr 项目中的 model 目录中找到。

esp32-arduino-esp-sr-tts-7

然后把它们放在一个目录中,通过自带的 Python 脚本来生成目标分区数据:

python3 pack_model.py -m mytarget -o srmodels.bin

再使用 esp-tool 上传到 model 分区中:

esptool.py --baud 2000000 --before default_reset --after hard_reset write_flash 0x710000 data/srmodels.bin

完成,终于可以用“小鸭小鸭”来唤醒 ESP32,并且使用“打开空调”来操作了。

TTS 也要上传模型哦

弄完 ESP-SR 的语音唤醒和命令识别,继续折腾 TTS。

根据 esp-skainet 中的示例,使用 TTS 也很简单,参考 https://github.com/espressif/esp-skainet/blob/master/examples/chinese_tts/main/main.c 示例中的代码就可以了。

不过在这里不得不吐槽一下,官方这些示例、文档、模型文件都是东一块西一块的……

TTS 的模型又在 esp-sr 项目中,在 esp-tts/esp_tts_chinese/esp_tts_voice_data_xiaole.dat 目录中。

esp32-arduino-esp-sr-tts-8

几种音色的区别可以在 samples 中找到示例试听一下。

我这里使用了 xiaoxin_small 这个,使用以下命令刷入 ESP32 的 Flash:

esptool.py --baud 2000000 --before default_reset --after hard_reset write_flash 0xCF0000 ./data/esp_tts_voice_data_xiaoxin_small.dat

再使用官方示例代码测试就可以了。

这语音咋声音不对呢

我搭建测试环境的硬件,是使用的 MAX98357 I2S 音频放大器模块,在参照官方示例代码初始化 I2S 并播放 TTS 音频时,总是单调不太对,调整了采样率和位宽都对不上,最终发现 ESP-TTS 输出的数据是单声道的,将输出模式从立体声修改为单声道就没问题了。

esp32-arduino-esp-sr-tts-9

这里用了 ESP_I2S 库,在 begin 时将第 4 个参数从 I2S_SLOT_MODE_STEREO 改为 I2S_SLOT_MODE_MONO 就可以正常工作了。

好了终于跑通了

断断续续几个星期,终于把 ESP32-S3 在 Arduino 开发框架下运行 ESP-SR 语音识别、语音指令、文本转语音这些功能搞定了,再有点时间就可以继续折腾那个赛博小狗和小智 AI 了 😂。

希望此文对想用 Arduino 框架开发 ESP-SR 和 ESP-TTS 的朋友们有所帮助。

参考资料

发表评论?

0 条评论。

发表评论


注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>