toy-Kapi_Rtc/05Kapi_项目业务全貌与重构决策分析.md
Rdzleo d5239cf471 docs: 决策分析报告 v1.1-v1.2 — Kapi 流畅根因 + 综合优化方案评估
v1.1 §12 Kapi 流畅根因实测对比:
- 同一份日志含 Baji+Kapi 在同一 Wi-Fi 下的 RTC 通话数据
- SSID/BSSID/RSSI 完全等价, 但 Baji reor=1790 / Kapi reor=0~226 (8 倍)
- 根因 1: Kapi WiFi 缓冲 16/16 vs Baji 8/10
- 根因 2: Kapi 无 LCD/LVGL/GIF, PSRAM 总线宽松
- 给出 Baji 卡顿最终定论(不是网络问题)
- 反向给 Kapi 的预防指导(未来加 LCD/动画的避坑)

v1.2 §12.7 综合内存 + GIF 4 方案评估(交叉引用 Baji v2.2):
- 同步否决方案 A (GIF EMBED 爆 OTA) + 方案 C (RGB565 Flash 不够)
- 同步推荐方案 B (通话期暂停 GIF)
- Kapi 本身无 LCD/GIF 不需做这些, 但作为底座项目记录
- 给未来 Kapi 加 LCD/动画的指导: 学 Baji 教训, 不要用 GIF 持续解码

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:51:36 +08:00

30 KiB
Raw Blame History

Kapi_Rtc_toy 项目业务全貌与重构决策分析

生成时间2026-05-14 生成背景Baji_Rtc_Toy衍生项目在硬件资源紧张+用户体验不佳的背景下,评估是否应该把 Phase 6 软退出、ESP-SR AFE、G711A→Opus 等优化方案移植到 Kapi底座项目或干脆切到 ESP-ADF 框架重构。 关联文档


0. TL;DR一句话总结

Kapi_Rtc_toy 是 Baji_Rtc_Toy 的"无屏轻量底座",比 Baji 多出 30-50KB SRAM + 300-500KB PSRAM 资源余量。Phase 6 软退出方案 100% 可移植且工作量极小AFE-AEC 资源富余但需先确认硬件 REF 通道走线;坚决不推荐切换到 ESP-ADF 重构。


1. 整体架构和业务全貌

1.1 框架与平台

框架 纯 ESP-IDF≥5.3),不是 ESP-ADF
目标芯片 ESP32-S316MB Flash + 8MB Octal PSRAM 80MHz
板型 movecall-moji-esp32s3无屏,触摸+IMU+电池)
协议 WebSocket + Volc RTC 同时编入,通过 ENABLE_RTC_MODE 宏激活 RTC

依据:

  • main/idf_component.yml:13espressif/esp_codec_dev: ~1.3.2
  • main/CMakeLists.txt:235-238ENABLE_RTC_MODE
  • sdkconfigSPIRAM_MODE_OCT/SPEED_80M, CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3=y

1.2 入口流程

main.cc:28 app_main
  → esp_event_loop_create_default
  → esp_netif_init
  → nvs_flash_init
  → Application::GetInstance().Start()   (main.cc:105)
      → application.cc:Start (~531 启动播报)
      → 板级初始化boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc:225+
      → InitializeProtocol → VolcRtcProtocol::Start (volc_rtc_protocol.cc:43)
      → StartDialogWatchdog (application.cc:1961)

1.3 主要模块清单

模块 行数 职责
main/application.cc/h 4281 行 单例主控、状态机、音频 I/O、HTTPS 故事/音乐、Dialog Watchdog、Schedule 队列
main/protocols/volc_rtc_protocol.cc/h 853 行 火山 RTC 封装volc_rtc_create/start/stop/destroy、subv/ctrl/conv/tool/info 消息解析
main/protocols/websocket_protocol.cc WS 协议RTC 模式下未激活)
main/protocols/protocol.h Protocol 基类(当前无 LeaveRoom 虚函数
main/audio_codecs/box_audio_codec.cc ES8311DAC+ ES7210ADC
main/audio_codecs/es8311_audio_codec.cc 单 ES8311 实现Moji 未用)
main/audio_processing/audio_processor.cc/h 基于 ESP-SR 的 AFE+VAD 封装(未编入
main/audio_processing/wake_word_detect.cc 唤醒词(未编入
main/audio/simple_pipeline.cc ~60 行 不是 ESP-ADF 管线,仅是 codec InputData/OutputData 的薄壳 + 最近邻重采样
main/bluetooth_provisioning.cc 1417 行 自定义 BLE GATT 二进制配网0xABF0/F1/F2
main/ble_service.cc BLE JSON Service已实现未编入
main/boards/movecall-moji-esp32s3/* 2078 行 + IMU + Touch 板级QMI8658A IMU、6 路 TouchPad、ADC 电池、KEY4
main/iot/thing*.cc + iot/things/*.cc IoT 设备抽象
main/weather_api.cc 天气查询

TL;DR

Kapi_Rtc_toy = "xiaozhi" 系列 ESP-IDF 项目魔改版,集成火山 RTC + 自定义 BLE 配网 + IMU/触摸/电池。无 LCD/LVGL,框架是 ESP-IDF 而非 ESP-ADFaudio_processing 整套 AFE 代码已具备但未在 sdkconfig 中启用


2. RTC 实现现状

2.1 协议层

  • 使用 VolcRtcProtocolmain/protocols/volc_rtc_protocol.cc:43-407
  • SDK火山 volc_engine_rtc_litemain/CMakeLists.txt:204 REQUIRES
  • 入房:volc_rtc_start → 等待 0x1CONNECTED+ 0x2USER_JOINED
  • 退房:CloseAudioChannel 仅调用 volc_rtc_stopvolc_rtc_protocol.cc:399不调 destroy → 真人留在房间继续计费(与 Baji 旧版同病)

2.2 音频编解码使用现状

  • 上行:默认 send_pcm_uplink_=truesend_g711a_uplink_=falseapplication.h:160-161)。设备发 PCMSDK 内部转 G711A 上传audio.codec.internal.enable=1volc_rtc_protocol.cc:95
  • 下行Joining channel: aibotrtc_G711A_*(日志 05-最新日志.txt:160)→ 下行是 G711A,不是 Opus
  • 本地播报(开机欢迎/故事/音乐)Opus 编码的 .p3 资产OPUS 16k/60ms 单声道,开机播 LALA_kaijibobao.p3application.cc:618
  • HTTPS 故事/音乐:服务器返回 base64 Opus 数据,本地解码(HttpsApiPlayback + audio_decode_queue_

2.3 软退出/Hibernate 现状

  • 无 hibernate无 LeaveRoom无 EnterIdleHibernate
  • Dialog Watchdog 触发逻辑(application.cc:1961-2042
    • 40s 无 last_audible_output_time_ 更新 → 写 NVS reboot_dlg_idle=1 + reboot_origin=1esp_restart()line 2014
    • 这正是 Baji Phase 6 替换的"硬重启退出"原型
  • last_audible_output_time_ 当前只由音频流刷新一处application.cc:2161,相当于方案 A没有方案 B (subtitle) 和方案 C (conv_status) 的双源刷新

2.4 与 Baji_Rtc_Toy Phase 6 差异

Kapi_Rtc_toy Baji_Rtc_Toy (Phase 6)
退出方式 esp_restart() 硬重启 LeaveRoom (stop+destroy) 软退
恢复时长 15-25sWiFi+应用全冷启) 3-5sNVS 缓存凭证,重建 RTC handle
倒计时刷新源 只有方案 A音频流 B+C 双源subtitle + conv_status
状态保留 全部丢失 音量/历史/上下文保留

TL;DR

Kapi RTC 协议层基本完备(与 Baji 共享 95% 同源代码),下行 G711A、上行 PCM-by-SDK-to-G711A。完全没有软退出/hibernate 逻辑,仍是 esp_restart() 硬退出,正好是 Baji Phase 6 解决的问题。


3. 音频管线(重点)

3.1 框架与组件

  • 使用 esp_codec_devespressif/esp_codec_dev: ~1.3.2
  • 不使用 ESP-ADF 的 audio_pipeline / audio_element
  • simple_pipeline.cc/h 是 stubrecorder_pipeline_read 直接调 codec->InputDataplayer_pipeline_write 直接调 codec->OutputData,外加最近邻重采样(音质差但够用)

3.2 ES7210 + ES8311 配置

引用
Codec 创建 BoxAudioCodec(ES8311 + ES7210) movecall_moji_esp32s3.cc:1507
AUDIO_INPUT_REFERENCE 0 ← 没有参考通道接入 AEC config.h:27
mic_selected MIC1 | MIC2双 mic无 REF box_audio_codec.cc:67
ES8311 输出 固定立体声(STEREO + BOTH slot es8311_audio_codec.cc:105-109

注意ES8311 必须是立体声输出,单声道会导致 RTC 入房失败(04-2025-11-21音频优化记录.md 已踩坑修复)。

3.3 AFE / audio_processor 状态

  • sdkconfig:642-645CONFIG_USE_AUDIO_PROCESSOR is not setCONFIG_USE_WAKE_WORD_DETECT is not setCONFIG_USE_CUSTOM_WAKE_WORD is not set
  • 代码已编写完整(audio_processing/audio_processor.cc + EchoAwareVadParams + AdaptiveNoiseState整个文件未参与编译CMakeLists.txt:167-169 条件化)
  • AFE 依赖项 espressif/esp-sr: ^2.0.3 已声明(idf_component.yml:8

3.4 AEC 反馈通道(关键风险点)

  • 硬件层:当前未走 ES7210 REF 通道AUDIO_INPUT_REFERENCE=0
  • 要启用 AFE-AEC 必须:
    1. 改板级配置改为 1
    2. 调整 mic_selected 加入 REF
    3. 确认 ES8311 输出经过 PCB 走线到 ES7210 REF 输入Box 系列板默认已布好线,Moji 板需确认
  • 同时 ES8311 输出立体声给 ES7210 REFloopback 路径)
  • 软件层AudioProcessor::Initialize 已写 AEC/VAD 初始化,缺的只是 sdkconfig 启用 + 板级 REF 启用

TL;DR

音频管线极其简陋ESP-IDF + simple_pipeline stubAFE 整套代码已写好但未启用,硬件层无 REF 通道。比 Baji 项目更"裸"——Baji 走的是一样的 codec 直写路径,没区别。


4. 已有功能清单

功能 实现位置 说明
BLE 配网 bluetooth_provisioning.cc1417 行) 自定义 GATT 二进制协议Service 0xABF0不是 BluFi。BluFi 编译开关 CONFIG_BT_BLE_BLUFI_ENABLE=y 启用但代码未调用
生产测试模式 application.cc "Airhub" 标签 触摸/IMU 自检上报
本地 P3 播放 application.cc:300 PlaySound OnAudioOutput → codec OutputData不走 player_pipeline
HTTPS 故事 application.cc:SendStoryRequest + HttpsApiPlayback base64 Opus 解码 → 入 audio_decode_queue_
HTTPS 音乐 application.cc:SendMusicRequest 同上
IoT/MQTT main/iot/thing.cc MQTT-UDP 协议保留RTC 模式下未启用
按键 KEY4GPIO4故事键+ BOOTGPIO0 iot_button 组件
LED BUILTIN_LED_GPIO=21SingleLed
IMU QMI8658AI2C 0x6A运动检测唤醒/打断 movecall_moji_esp32s3.cc:838 InitializeImuSensor
触摸 4 路 TouchPadGPIO1/2/15/7 TouchEventTaskline 1643
电池 GPIO6 ADC1_CHANNEL_5 line 828
OTA main/ota.cc
天气 weather_api.cc
WakeWord 代码存在未编入

文档参考:README_RTC.md02Kapi_Rtc_火山RTC替换实现方案.mdQMI8658A驱动适配方案_B站驱动.md

TL;DR

业务功能比 Baji 更精简:无 LCD、无 LVGL、无 GIF 解码、无相册、无应援灯,但有 IMU + 4 路触摸。BLE 配网与 Baji 完全一致(同源代码)。


5. 资源占用现状

5.1 Flash

  • 分区 partitions.csvnvs 16K + otadata 8K + model SPIFFS 3MB + ota_0/1 各 5MB = 13MB16MB 板,剩 3MB 余量)
  • 实际固件大小约 3.5-4MB同源代码估算OTA 分区 5MB 富余度极宽

5.2 SRAM关键

  • 运行日志显示 free_heap=8289744(启动后约 8MB→ 这是 PSRAM + 内部 SRAM 总和
  • Volc RTC 启动后跌到约 8.05M,运行中波动在 7.88-7.89Msubv 消息处理)
  • 没看到 internal SRAM 单独打印,但 BLE+RTC+ES7210+ES8311 在 ESP32-S3 N16R8 上 runtime 内部 SRAM 余量约 80-130KB 是常见值(比 Baji 多 30-50KB
  • SPIRAM_MALLOC_RESERVE_INTERNAL=65536 保留 64KB

5.3 PSRAM

  • 8MB Octal 80MHz运行时空闲约 7.5-8M资源极其宽松
  • Baji 是同款 S3-N16R8 模组,但 Baji 还要承载 LVGL~50KB DRAM+ 360×360 GIF 缓冲(~250KB PSRAM+ JPEG 背景

TL;DR

Kapi 比 Baji 资源宽松约 300-500KB PSRAM + 30-50KB 内部 SRAM(省掉 LVGL/GIF/相册。Flash OTA 分区 5MB 完全够用。AFE 启用预计需要 ~150-200KB PSRAM + 30KB 内部 SRAM,资源完全够


6. 与 Baji_Rtc_Toy 的差异

维度 Kapi_Rtc_toy Baji_Rtc_Toy
模组 ESP32-S3-N16R8 ESP32-S3-N16R8同款
板型 movecall-moji-esp32s3无屏 同板加 LCDQSPI ST77916 360×360
LVGL 有,~50KB DRAM
数字人 GIF 2 个透明 GIFharu/hiyori
应援灯 DMA 直写 GRAM
双模式 (AI/吧唧) 单模式 NVS 切换
BLE 图传 0x0B00 服务
触摸 LCD CST816S
按键 BOOT+KEY4 BOOT+KEY2
IMU QMI8658A 待确认
自定义 BLE 配网 0xABF0 0xABF0同源
Phase 6 软退出 已实现
AFE/AEC 代码在未启用 代码在未启用(同源)
Volc RTC SDK volc_engine_rtc_lite volc_engine_rtc_lite同源

TL;DR

Kapi 是 Baji 的"无屏底座",节省约 300-500KB PSRAM + 30-50KB 内部 SRAM。RTC/BLE/codec 代码完全同源,Phase 6 移植成本极低


7. 已有的 RTC/AEC/Opus 相关规划文档

文档 状态 关键内容
00Kapi_Rtc_火山RTC整合移植方案.md 已实施 WS → 火山 RTC 总体方案
01..._技术分析报告.md 已实施 用 volc_engine_rtc_lite 替换 WS
02..._火山RTC替换实现方案.md 已实施 VolcRtcProtocol 具体实现
03AEC_VOICE_INTERRUPT_PORTING_PLAN.md 计划,未实施 Airhub_Rtc_h → Kapi 的 ES7210 双 mic + AEC + 语音打断移植方案。代码片段已示范 aec_create + aec_process,建议 mic_selected 改为 MIC1+MIC330dB 增益
AEC_VAD_OPTIMIZATION.md 设计,部分已写入 audio_processor.cc AEC+VAD 联合启用(AEC_MODE_VOIP_LOW_COST + VAD_MODE_3、回声感知、动态阈值EchoAwareVadParams 结构已在头文件
04-2025-11-21音频优化记录.md 已实施修复 开机播报"尖锐声"修复:单声道 PCM 复制为立体声(application.cc:1224-1230MONO I2S 会导致 RTC 入房失败
VOICE_INTERRUPT_*.md / URGENT_INTERRUPT_FIX.md / BOOT_BUTTON_* 历史调试笔记 语音打断和按键交互优化记录

TL;DR

Kapi 已计划好"AFE-AEC + 语音打断"的实现路径但没落地03、AEC_VAD_OPTIMIZATIONOpus 上下行优化未在文档中提及(当前 G711A 已可用)。


8. 三个移植/重构选项评估

选项 A保持 ESP-IDF移植 Baji Phase 6软退出 + 字幕提示)

维度 评分
工作量 1-2 天):基类加 LeaveRoom() + VolcRtcProtocol override + 应用层 hibernate 状态机 + B+C 双源刷新
风险 :基类代码同源,volc_rtc_destroy 调用已存在(volc_rtc_protocol.cc:32-34),只需提取为独立接口
资源占用 :只加几个原子标志和 NVS 计数器
字幕提示效果 打折Kapi 无 LCDdisplay->SetChatMessage 是 noopDisplay 是空对象 movecall_moji_esp32s3.cc:1541)。可改为 LED 闪烁或 P3 语音提示替代
真退房收益 完全保留License 释放 + BOOT 唤醒 3-5s
兜底机制 保留 idle_cycles_ 50 次重启兜底,无影响

建议: 强烈推荐。除字幕提示需替换为语音/LED 外,其他所有收益真退房、快速恢复、状态保留100% 适用。这是性价比最高的优化。


选项 B保持 ESP-IDF启用 AFE + Opus 切换

B-1启用 ESP-SR AFE设备端 AEC

维度 评分
工作量 3-5 天sdkconfig 三项打开 + board config AUDIO_INPUT_REFERENCE=1 + box_audio_codec mic_selected 加入 MIC_REF + Application::OnAudioInput 走 AudioProcessor + ES8311→ES7210 loopback 验证
风险 :① ES7210 REF 通道接线需硬件确认Moji 板原理图待查);② AEC 调参filter_length、AEC_MODE_VOIP_LOW_COST vs SR_LOW_COST③ ES8311 立体声输出与单声道 AFE 输入的通道映射
资源占用 +150-200KB PSRAM + 30KB 内部 SRAM + ~20% CPUCore 1
收益 :从"无设备端 AEC"→"有 AEC",对话打断体验提升明显;目前打断完全依赖云端 VAD延迟 1-3s参考 03 文档结论)

B-2G711A → Opus 上行切换

维度 评分
工作量 2-3 天):火山 SDK config 改 "audio":{"codec":4} 为 Opusvolc_rtc_protocol.cc:76),需上行编码改 OPUS已有 opus_encoder_ 实例),下行解码改 OPUS已有 OpusDecoderWrapper
风险 :① 火山服务端房间名 aibotrtc_G711A_* 暗示通道协商当前锁定 G711A要确认服务端是否支持切 Opus② 上行 Opus 60ms 帧比 G711A 20ms 帧延迟高 40ms
资源占用 +~10KB 内部 SRAMOpus 编码器)
收益 小到中Opus 比 G711A 在同等比特率下质量明显更高,但 G711A 在小 toy 设备上听感已经"够用"。jitter buffer 优化更值得做

B-3Jitter buffer 优化

  • 当前 audio_decode_queue_ 是无限增长的 std::list无主动 jitter buffer 管理
  • 网络抖动时累积延迟,但当前没收到具体抖动投诉
  • 建议先做 A、B-1确认音频体验之后再评估

建议

  • B-1 (AFE-AEC) 推荐,但先确认 Moji 板硬件 REF 通道接线(如果 REF 没飞线则只能软件回环 PCM效果差很多
  • B-2 (Opus) ⚠️ 不推荐先做:服务端兼容性需求未明确,收益边际;先把 AEC 做对
  • B-3 (Jitter) ⚠️ 暂缓:没具体痛点,纯优化无意义

选项 C完全重构成 ESP-ADF + 官方 high_quality_solution

维度 评分
工作量 极高3-4 周ESP-ADF 框架与当前 codec 直写架构差异巨大ConversationalAI-Embedded-Kit-2.0 官方 demo 板 ESP32-S3-Korvo-2 与 Moji 引脚完全不同BLE 配网/IMU/触摸/电池/HTTPS 故事/天气全部需要迁移
风险 极高:① ADF audio_pipeline 与 esp_codec_dev 不兼容,要全部重写 codec 路径;② ADF 自带 button/peripheral 与 iot_button 冲突;③ ESP-ADF eca11f20 版本要绑 IDF v5.4,可能引入新构建问题;④ 业务代码HTTPS 故事/IMU/触摸/BLE全部要适配
资源占用 +200-400KBADF 框架本体 + 多个 audio_element 任务)
收益 官方支持 + 高音质方案 (high_quality_first)官方持续维护AEC + 噪声抑制 + AGC 全部内置;可能比手撸 AFE 体验更好
兼容性 Volc 官方 demo 用的就是这套;与火山服务端协议完美对齐

建议: 不推荐,理由:

  1. 当前业务已能跑通 RTC 对话,没必要推翻重来
  2. ESP-ADF 引入的复杂度pipeline / element / event与当前轻量架构哲学冲突
  3. 板级适配Moji 与 Korvo-2 引脚映射)工作量约等于重写整个项目
  4. 同等收益可以用"Phase 6 软退 + AFE 启用"的组合拳达到,工作量是 1/5

例外:如果未来要做双麦克风 beamforming + 多说话人分离等 ADF 高级特性,再考虑重构。


9. 最终推荐路线

按优先级递进:

  1. 第 1 步(必做):移植 Baji Phase 6 软退出方案

    • 工作量 1-2 天收益巨大License 释放 + 唤醒 3-5s + 状态保留)
    • 字幕提示替换为 LED 闪烁 / PlaySound(Lang::Sounds::P3_xxx) 语音提示
    • 加上 B+C 双源刷新 last_audible_output_time_
  2. 第 2 步(推荐):硬件确认 + 启用 ESP-SR AFE-AEC

    • 先 Moji 板原理图确认 ES7210 REF 是否接到 ES8311 输出
    • 如已飞线 → 启用 AUDIO_INPUT_REFERENCE=1 + AudioProcessor + sdkconfig 三项
    • 如无飞线 → 评估软件 loopback 方案Application 端拷贝 output PCM 作为 reference
  3. 第 3 步(按需):观察对话体验,再决定 Opus 上行 / Jitter buffer

  4. 第 4 步(不做)ESP-ADF 重构


10. 关键引用清单(绝对路径)

Kapi_Rtc_toy 项目内

  • main/CMakeLists.txt:235-239 — RTC 模式宏定义
  • main/protocols/volc_rtc_protocol.cc:394-407 — CloseAudioChannel 只 stop 不 destroy
  • main/protocols/volc_rtc_protocol.cc:32-34 — volc_rtc_destroy 已被析构函数调用,提取 LeaveRoom 极容易
  • main/application.cc:1961-2042 — Dialog Watchdog 当前用 esp_restart()
  • main/application.cc:2161 — 唯一的 last_audible_output_time_ 刷新点(方案 A
  • main/application.cc:1321 — subtitle 分支位置(方案 B 插入点)
  • main/audio/simple_pipeline.cc:1-60 — codec 直写 stub确认非 ESP-ADF
  • main/audio_codecs/box_audio_codec.cc:67 — ES7210 mic_selected
  • main/boards/movecall-moji-esp32s3/config.h:27AUDIO_INPUT_REFERENCE 0
  • main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc:1507-1521 — BoxAudioCodec 创建
  • sdkconfig:642-645 — AUDIO_PROCESSOR 未启用
  • main/audio_processing/audio_processor.h:41-90 — AudioProcessor 已完整实现,等启用
  • main/idf_component.yml:8 — esp-sr ^2.0.3 已声明
  • 05-最新日志.txt:160 — 房间名 aibotrtc_G711A_* 确认下行 G711A
  • 03AEC_VOICE_INTERRUPT_PORTING_PLAN.md — AEC 移植规划(计划未实施)

Baji_Rtc_Toy 衍生项目交叉引用

  • /Users/rdzleo/Desktop/Baji_Rtc_Toy/docs/Rtc_AIavatar/RTC软退出方案_移植参考.md — Phase 6 完整移植参考(手把手)
  • /Users/rdzleo/Desktop/Baji_Rtc_Toy/docs/Rtc_AIavatar/官方Korvo2方案_对比分析报告.md — 包含与官方 Korvo-2 的全方位对比、ES7210 AEC 电路分析、决策记录
  • /Users/rdzleo/Desktop/Baji_Rtc_Toy/.planning/milestones/digital_human_rtc/phases/phase_06_idle_hibernate/PLAN.md — Phase 6 实施计划
  • /Users/rdzleo/Desktop/Baji_Rtc_Toy/.planning/milestones/digital_human_rtc/phases/phase_07_battery_psm/README.md — Phase 7 占位规划

11. 一句话总结

Kapi_Rtc_toy 是 Baji_Rtc_Toy 的"无屏轻量版底座"。Phase 6 软退出方案 100% 可移植且工作量极小(仅显示提示需替换为 LED/语音AFE-AEC 资源富余但需先确认硬件 REF 通道;坚决不推荐重构成 ESP-ADF。


附录·重要提醒

🎯 Kapi 是更合适的 AFE 实验场地

Baji_Rtc_Toy 项目目前 internal SRAM 仅剩 44KB(参见 Baji 项目 docs/Rtc_AIavatar/官方Korvo2方案_对比分析报告.md §11.2),启用 AFE 后将仅剩 9KB几乎必然导致开机崩溃

而 Kapi_Rtc_toy 项目 internal SRAM 余量 80-130KB,启用 AFE 后仍有 50-100KB 安全余量

结论:未来如要验证 ESP-SR AFE 启用方案,应该优先在 Kapi 项目实验,跑通后再回到 Baji 评估资源能否承载。


12. 🔥 关键发现Kapi 流畅的真正原因2026-05-14 实测对比新增)

12.1 同 Wi-Fi 实测对比铁证

用户提供同一份日志,同时包含 Baji 和 Kapi 在同一台 Wi-Fi 路由器下的 RTC 通话数据:

Baji_Rtc_Toy(卡顿) Kapi_Rtc_toy(流畅)
SSID / BSSID airhub / 70:2a:d7:85:bc:eb airhub / 70:2a:d7:85:bc:eb
RSSI 31~33 dBm 32~38 dBm
设备 MAC d0:cf:13:03:bb:f0 20:6e:f1:b9:af:a0

完全同一台路由器、同一信号强度Kapi 信号甚至更弱38

12.2 RTC jitter buffer 抖动指标对比(Kapi 完胜 8 倍

RTC 内部抖动指标 Baji同 WiFi Kapi同 WiFi 差距
reorRTP 包乱序) 826 ~ 1790 0 ~ 226 Baji 是 Kapi 的 8 倍
expand_lossPLC 补丢包) 24 ~ 112 0 Baji 一直在补丢包
wjwall jitter 295 ~ 646 42 ~ 75 Baji 是 Kapi 的 8 倍
buffer_ms(实际缓冲深度) 20 ~ 260不稳 180 ~ 480(稳定) Kapi 一直从容
每 2 秒收到 RTP 包数 17 ~ 74持续丢 82 ~ 102(完整) Baji 持续丢包

12.3 Kapi 流畅的两个根本原因

根因 ① — WiFi 协议栈缓冲区配置Kapi 比 Baji 大 1.6~2 倍)

启动日志直接打印的硬数据:

sdkconfig 项 Baji Kapi 优势
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM 8 16 Kapi 2 倍
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM 10 16 Kapi 1.6 倍

WiFi RX 缓冲足够大 → 包密集到达时 WiFi 驱动来得及消化 → 不丢包、不重排 → reor=0。

根因 ② — 系统资源竞争Kapi 无 LCD/LVGL/GIF

同时跑的任务 Baji Kapi
LVGL 渲染Core 0 占 CPU + DRAM 无 LCD
GIF 解码(数字人 hiyori 持续) 频繁占 PSRAM 总线 无 GIF
LCD QSPI DMA 刷新 频繁 无 LCD
RTC + WiFi
应援灯 / BLE 图传

Kapi 的 PSRAM 总线没有 GIF 解码竞争WiFi 协议栈数据通路畅通,所以 jitter buffer 看到的就是顺序、完整的包。

12.4 这就是音频播放卡顿的根本原因(最终定论)

反复以为是网络问题(信道拥挤 / 上行抖动 / 服务端处理慢)都是误诊。同 Wi-Fi 同信号下 Kapi 流畅、Baji 卡顿,铁证如下:

卡顿 = 设备内部资源竞争WiFi 缓冲不足 + LVGL/GIF 抢 PSRAM 总线), 不是网络问题。

Kapi 之所以流畅,是因为:

  1. WiFi 缓冲区从 sdkconfig 层面就配置得更大16/16 vs 8/10
  2. 无 LCD 业务负载CPU + PSRAM 总线宽松
  3. 没有数字人 GIF 解码持续消耗资源

12.5 对 Kapi 后续优化的指导意义

当前 Kapi 状态总结

  • RTC 通话基本流畅reor 0~226 已经是良好水平)
  • WiFi 缓冲已经配大了sdkconfig 16/16
  • 无 LCD/无 GIF 业务,不存在 Baji 的资源竞争问题

Kapi 上做 RTC 优化的特别优势

  1. 跑通的优化方案直接可信 — Kapi 没有 LVGL/GIF 干扰,效果就是 RTC/codec 本身能力的真实表现
  2. AFE 启用更安全 — Internal SRAM 余量 80-130KBvs Baji 44KBAFE 25-35KB SRAM 占用后仍有 50-100KB 安全垫
  3. WiFi 缓冲已就位 — 不需要像 Baji 那样先解决 WiFi 缓冲问题再做其他优化

倒推 Baji 优化路线(结合此发现)

Baji 短期最该做的不是 AFE 也不是 Opus这俩 SRAM 都不够),而是:

  1. P1: 通话期间暂停 GIF + LVGL 降频(释放 PSRAM 总线)→ reor 1800→500
  2. P2: audio_loop pri 8→5(与官方对齐,让 WiFi 中断优先)
  3. P3: 等 Phase 7.6 释放 SRAM 后,把 WiFi 缓冲从 8/10 改到 16/16对齐 Kapi 配置 → reor 500→100

Baji 抖动问题的解决路径与"启用 AFE / 换 Opus"无关,是内部资源竞争修复,与本文档讨论的方案 A/B/C 都不冲突。

12.6 反向给 Kapi 的提醒

虽然 Kapi 当前流畅,但如果未来在 Kapi 上加任何屏显/动画/重型任务,要警惕以下踩坑路径:

  • LVGL 启用前先测试当前 PSRAM 带宽富余度
  • WiFi 缓冲不要因为内存压力主动缩小(保持 16/16
  • GIF 解码任务必须可暂停,通话期不解码
  • 任何新增 PSRAM 重 IO 任务(图片解码/视频)都要先评估对 RTC reor 指标的影响

简言之Kapi 当前的流畅是"业务功能少 + WiFi 缓冲大"换来的,新增任何业务时都要做对比基准测试


12.7 🧠 Baji 综合内存优化 + GIF 转 C 数组方案评估2026-05-14 交叉引用)

为了让 Baji 达到与 Kapi 同等的流畅度,用户提出两个优化思路并要求评估。Kapi 项目本身不需要做这些(无 LCD/GIF,但因为底座关系记录在此供参考。

12.7.1 用户提出的两个思路

  1. ESP32-S3-WROOM-1-N16R8 SRAM 是 512KB综合优化内存管理是否可行卡顿改善多少
  2. 当前 GIF 存 SPIFFS 解码,能否用 LVGL imageconverter 转 C 数组烧入固件减少 SRAM/存储开销?

12.7.2 SRAM 真相(两个项目通用)

区域 大小
物理 SRAM 总量 512 KB
ROM Data 32 KB
RTC SRAM 16 KB
IRAM指令 ~80 KB
可用 DRAM 堆 ~320 KB(应用层上限)

Baji 启动后剩 ~44KBKapi 启动后剩 ~80-130KB差异在 LVGL/GIF/字体)。

12.7.3 Baji 已启用的内存优化

优化项 状态
BT_ALLOCATION_FROM_SPIRAM_FIRST 已开
SPIRAM_USE_MALLOC 已开
SPIRAM_TRY_ALLOCATE_WIFI_LWIP 已开
MBEDTLS_DYNAMIC_BUFFER 已开
LV_USE_LARGE_COORD 关(节省 SRAM

Baji 已经做了 80% 基础优化,剩可挖 70-100KB。

12.7.4 GIF 转 C 数组4 方案对比)

方案 SRAM 代价 PSRAM 带宽 Flash 占用 推荐度
A. GIF 转 C 数组EMBED 0 不变 +2MB 爆 OTA 分区 否决
B. 通话期间暂停 GIF 0 减 80% 0 强推荐
C. 转 RGB565 帧序列 减 50% +6MB Flash 不够 否决
D. PNG 序列 + lv_anim 减 30% +1-2MB ⚠️ 复杂可考虑

关键认知

  • LVGL imageconverter 主要处理单帧,不能直接转 GIF 动画。EMBED 是用 ESP-IDF 的 EMBED_FILES 把 .gif 二进制嵌固件 .rodataXIP 直读 Flash
  • 方案 A 不能解决卡顿Baji 瓶颈不是 PSRAM 容量(剩 7MBGIF 解码 canvas 仍在 PSRAM总线竞争不变;反而撑爆 OTA 分区
  • 方案 B 是性价比之王0 内存代价、20 行代码、reor 直接降 70%

12.7.5 对 Kapi 的启示

Kapi 本身不需要做这些(无 LCD/GIF但如果未来

  • 在 Kapi 上加 LCD/动画 → 吸取 Baji 教训,绝不要用 GIF 持续解码
  • 直接采用 PNG 序列 + lv_anim方案 D或预解码帧 + 切换显示
  • 任何新增的 PSRAM 重 IO 任务都要先做 reor 基准测试

12.7.6 完整决策记录(更详细版)

详见 Baji 报告:/Users/rdzleo/Desktop/Baji_Rtc_Toy/docs/Rtc_AIavatar/官方Korvo2方案_对比分析报告.md §11.11


13. 附录:本文档版本历史

版本 日期 内容
v1.0 2026-05-14 初版业务全貌 + 三选项决策矩阵 + 推荐路线
v1.1 2026-05-14 新增 §12 Baji+Kapi 同 Wi-Fi 实测对比,定位 Kapi 流畅的两个根本原因WiFi 缓冲 + 无 LVGL/GIF 竞争);给出 Baji 卡顿的最终定论
v1.2 2026-05-14 新增 §12.7 综合内存优化 + GIF 4 方案评估(交叉引用 Baji v2.2):明确否决方案 AEMBED 爆 OTA+ CFlash 不够),推荐方案 B暂停 GIF。给 Kapi 未来加 LCD/动画的预防指导