LilaWakeup_App/CLAUDE.md
Rdzleo 128f7ca02e init: KWS 软件唤醒 APK 工程骨架(基于 sherpa-onnx 改造)
工程结构:
- com.lila.wakeup 包名,基于 sherpa-onnx v1.13.0 SherpaOnnxKws demo 改造
- 9 个 Kotlin 模块: WakeupForegroundService / KwsStateMachine / AudioCapture
  / KwsEngine / BootReceiver / ProtocolReceiver / BroadcastSender / Config
  / MainActivity
- 5 个 Lila 中文唤醒词(你好Lila/hello Lila/Lila同学/Lila你好/小Lila)
- 仅 arm64-v8a(OrangePi CM5 / RK3588)
- 协议 v2.1: 双向 setPackage / silence_ms 3000 / 2min 兜底
- 数字人 APP 包名: com.qy.lila

构建通过项:
- Gradle Sync OK(腾讯云 Gradle 镜像 + 阿里云 Maven 镜像)
- APK 编译成功 55MB,含 25MB onnxruntime + 24MB 模型 + 9 个 Kotlin .dex
- adb install OK,Service onCreate 被调用,sherpa-onnx 加载模型

已知问题(下一 commit 修复):
- KwsEngine.kt 的 OnlineModelConfig modelType 设为 'zipformer'
  实际应为 'zipformer2',native 加载在 InitEncoder 后崩溃
- 修复方法: 用 sherpa-onnx 官方 getKwsModelConfig(0) 工厂函数

参考文档:
- 协议: ~/OrangePi_CM5_Project/docs/.../KWS唤醒协议-V2.md (v2.1)
- 设计: ~/OrangePi_CM5_Project/docs/.../KWS服务设计.md
- 评估: ~/OrangePi_CM5_Project/docs/.../KWS唤醒方案适配评估_Unity.md
2026-04-30 09:55:49 +08:00

225 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Lila Wakeup APK 工程上下文com.lila.wakeup
> **本文件用于 Claude Code 插件加载,自动获取项目背景。**
> 本工程是 OrangePi CM5 数字人产品的 KWS 软件唤醒服务(独立 APK与第三方 Unity 数字人 APP包名 `com.qy.lila`)通过 Android Broadcast 协议联动。
---
## 一、项目本质(一句话)
基于 **sherpa-onnx** 引擎的 Android 13 KWSKeyword Spotting / 关键词唤醒APK监听用户喊"你好 Lila"等唤醒词后通过 Broadcast 通知数字人 APP 显示数字人 + 接通火山 RTC。
替代原方案的"ESP32 + 离线语音芯片 + USB 串口 SO_WAKEUP1"硬件方案,省 BOM 成本 + 更灵活。
---
## 二、核心元信息(写代码前必读)
| 项 | 值 |
|---|---|
| 本 APK 包名 | `com.lila.wakeup` |
| 数字人 APP 包名 | `com.qy.lila`(已确认,来源 LTY_Project ProjectSettings.asset |
| 目标系统 | Android 13 (API 33) |
| 目标架构 | arm64-v8a仅打包此架构节省 APK 体积) |
| 目标设备 | OrangePi CM5Rockchip RK3588rk3588s_s |
| 协议版本 | **v2.1**(详见上游 `docs/OrangePi_CM5/MD_Document/KWS唤醒协议-V2.md` |
| KWS 引擎 | sherpa-onnx Zipformer wenetspeech 3.3M(中文,已预编译 native|
| 集成方式 | **基于 sherpa-onnx 官方 SherpaOnnxKws demo 改造**(不是 Maven 依赖,因 Android arm64-v8a .so + Kotlin Wrapper 在 Maven 上没发布完整包) |
| 当前阶段 | **方案 A普通 APK**(量产前升级到方案 B系统签名 + /system/priv-app/|
---
## 三、协议规约v2.1 核心摘要)
### 三个 Broadcast Action双向 setPackage 强制要求)
| 方向 | Action | 字段 |
|---|---|---|
| **WAKEUP**(本 → APP| `com.lila.intent.action.WAKEUP` | `keyword: String` / `timestamp: long` / `confidence: float` |
| **KWS_PAUSE**APP → 本)| `com.lila.intent.action.KWS_PAUSE` | `silence_ms: long`(默认 **3000**Networking 场景 60000/ `reason: String` |
| **KWS_RESUME**APP → 本)| `com.lila.intent.action.KWS_RESUME` | `reason: String` |
### 关键设计约束(不要破坏)
1. **silence_ms 二次保险**:收到 PAUSE 后即使收到 RESUME 也要等到 silence 满才真正 resume —— 防 AI 自我唤醒
2. **2 分钟兜底超时**APP 漏发 RESUME 时 KWS 自动恢复(防永久哑火)
3. **后验平滑**:连续 N 帧(默认 2置信度过阈值才发 WAKEUP —— 降误唤醒
4. **AudioRecord 暂停时完全 release**:把麦克风让给 RTC SDK避免资源冲突
5. **WAKEUP 命中后不自动 PAUSE 自己**:必须由 APP 主动发 PAUSE —— 防漏命中导致的死锁
---
## 四、工程结构(基于 sherpa-onnx demo 改造后)
```
工程根/(原 sherpa-onnx/android/SherpaOnnxKws
├── CLAUDE.md ← 本文件
├── app/
│ ├── build.gradle.kts ← 已改:包名 + arm64-v8a only + targetSdk 33
│ └── src/main/
│ ├── AndroidManifest.xml ← 已改:完整权限 + 4 个组件声明
│ ├── assets/
│ │ └── sherpa-onnx-kws-zipformer-wenetspeech-3.3M-2024-01-01/
│ │ ├── *.onnx ← 原 demo 自带(不动)
│ │ ├── tokens.txt ← 原 demo 自带(不动)
│ │ └── keywords.txt ← 已改5 个 Lila 唤醒词
│ └── java/com/lila/wakeup/ ← 已改:包名 + 9 个新文件
│ ├── Config.kt ← 协议常量、阈值、模型路径
│ ├── MainActivity.kt ← 状态查看 UI + 权限申请(开发期辅助)
│ ├── BootReceiver.kt ← 开机自启
│ ├── WakeupForegroundService.kt ← 前台 Service 主体
│ ├── kws/
│ │ ├── AudioCapture.kt ← AudioRecord 16kHz mono PCM16
│ │ ├── KwsEngine.kt ← sherpa-onnx KWS 封装
│ │ └── KwsStateMachine.kt ← LISTENING/PAUSED 状态机 + silence_ms
│ ├── protocol/
│ │ ├── BroadcastSender.kt ← 发 WAKEUP 给 com.qy.lila
│ │ └── ProtocolReceiver.kt ← 收 PAUSE/RESUME
│ └── sherpa-onnx 自带的 Wrapper 类保留,包名 com.k2fsa.sherpa.onnx 不动)
└── build.gradle.kts (项目级) ← 不动
```
### 9 个我们写的 Kotlin 文件代码量
| 文件 | 行数 | 职责 |
|---|---|---|
| Config.kt | 108 | 全局常量 |
| MainActivity.kt | 117 | 状态 UI + 权限申请 |
| BootReceiver.kt | 50 | 开机自启 |
| WakeupForegroundService.kt | 142 | Service 主体 + 通知栏 |
| AudioCapture.kt | 112 | AudioRecord 封装 |
| KwsEngine.kt | 119 | sherpa-onnx 封装 |
| KwsStateMachine.kt | 163 | 状态机 + silence_ms + 兜底 |
| BroadcastSender.kt | 36 | 发 WAKEUP |
| ProtocolReceiver.kt | 63 | 收 PAUSE/RESUME |
---
## 五、关键参考文档(在主仓库 OrangePi_CM5_Project 中)
> **这些文档是工程的"上游契约"**,本工程实现必须与之一致。改协议要先去主仓库改文档。
| 文档 | 路径 | 作用 |
|---|---|---|
| 协议规约 v2.1 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS唤醒协议-V2.md` | 双方对接契约(最权威)|
| 服务设计 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS服务设计.md` | 我侧实现指南 |
| 第三方评估 | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS唤醒方案适配评估_Unity.md` | Unity APP 团队的对接评估(含 v2 微调建议)|
| 工程骨架 README | `/Volumes/LinuxDev/OrangePi_CM5_Project/docs/OrangePi_CM5/MD_Document/KWS-APK-工程骨架/README.md` | 9 步实施指南(含路线图、坑提示)|
---
## 六、用户偏好与代码风格(继承自主项目)
- **语言**:所有沟通用中文,代码注释用中文
- **代码风格**snake_case 变量、UPPER_SNAKE_CASE 常量、_t 后缀类型Kotlin 这边按 Kotlin 标准CamelCase 类名 / camelCase 变量),但**注释一律中文**
- **嵌入式原则**:节省 RAM/Flash > 时序优化 > 模块化(虽然这是 Android 但 OrangePi 资源仍宝贵)
- **不主动加冗余校验**CLAUDE.md 全局规则)
- **破坏性操作先确认**git push --force / rm 等)
- **优先静态分配**:避免频繁 malloc/free字符串处理用固定缓冲区
---
## 七、当前任务路线图
| 阶段 | 内容 | 状态 |
|---|---|---|
| 0. 算法验证 | sherpa-onnx 预编译 KWS APK 装到板子,测识别率 | ✅ 已完成5/8 命中)|
| 1. 工程骨架 | 9 个 Kotlin 类 + Manifest + gradle | ✅ 已完成(在主仓库 KWS-APK-工程骨架/|
| 2. AS 装齐组件 | API 33 / Build-Tools 33 / NDK 25 / CMake 3.22 | 🔄 进行中 |
| 3. 克隆 sherpa-onnx + 改造 | git clone + Refactor 包名 + 拷骨架文件 | 📋 待做 |
| 4. Build → adb install 到 OrangePi CM5 | 验证唤醒效果 | 📋 待做 |
| 5. 协议联调(与 Unity APP| 按协议 v2.1 §八 联调清单 | 📋 待 APP 团队 |
| 6. 方案 B 升级 | 系统签名 + 集成进 update.img | 📋 联调通过后 |
| 7. 长稳测试 | 72h 连续运行 | 📋 量产前 |
---
## 八、关键技术决策记录(不要重新讨论)
| 决策 | 结论 | 理由(一句话)|
|---|---|---|
| 用 Maven 依赖还是克隆 demo | **克隆 demo** | Maven 上 sherpa-onnx 的 Android arm64-v8a .so + Kotlin Wrapper 不全 |
| 方案 A / B / C 怎么走? | **A 跑通 → B 升级**(不走 C| C 改 AOSP system_server 收益不抵成本 |
| silence_ms 默认值 | **3000ms** | 第三方实测 RTC 远端音频回灌 800-1500ms + AI TTS 触发2000ms 不够 |
| Networking 场景是否唤醒? | **不允许**APP 进入发 PAUSE| 配网中被唤醒会要求 APP 新增响应逻辑 + 决策跳哪里 |
| BroadcastReceiver 与 Service 通信方式 | **Locator 模式** | 比 startService / AIDL 简单,性能好 |
| AudioRecord 暂停时是 stop 还是 release | **完全 release** | 释放麦克风给 RTC SDK避免资源冲突 |
---
## 九、绝对不要做的事
1.**不要改包名 `com.qy.lila`**APP 团队已确认,改了协议失效
2.**不要改 Action 名 `com.lila.intent.action.*`**:双方协议契约
3.**不要把 sherpa-onnx 源码包名 `com.k2fsa.sherpa.onnx` 改掉**JNI native 库 binding 的硬编码包名
4.**不要在前台 Service 里调耗时操作vTaskDelay 类)阻塞 main looper**StateMachine 的 timeout 用 Handler.postDelayed
5.**不要在 AudioCapture 线程里同步调网络 / 文件 IO**:会丢音频帧
6.**不要禁用 START_STICKY**Service 异常重启依赖此标志
7.**不要把 RECORD_AUDIO 申请放在 Service**:必须在 Activity 里申请Android 限制)
---
## 十、与主项目OrangePi_CM5_Project的协作
主项目的 Claude Code 会话负责:
- 协议规约文档KWS唤醒协议-V2.md演进
- 服务设计文档KWS服务设计.md演进
- 与第三方 APP 团队对接
- 系统侧kernel / dts / Audio HAL相关修改
- 整机集成(方案 B 升级)
本工程sherpa-onnx 改造)的 Claude Code 会话负责:
- 9 个 Kotlin 文件的具体实现
- Gradle / Manifest 配置
- Android Studio Build / Run 调试
- KWS 引擎参数调优threshold、N 帧平滑)
- 真机测试(与 OrangePi CM5 联调)
**协议改动必须先在主项目改文档,再同步到本工程。**
---
## 十一、典型工作请求示例(让 Claude Code 上手)
新来 Claude Code 插件可以处理的典型任务:
- "帮我改 [`KwsStateMachine.kt`](app/src/main/java/com/lila/wakeup/kws/KwsStateMachine.kt) 的后验平滑窗口从 2 帧改为 3 帧"
- "把 [`build.gradle.kts`](app/build.gradle.kts) 的 minSdk 从 26 改为 24"
- "新增一个 keywords 重载接口,让 [`KwsEngine.kt`](app/src/main/java/com/lila/wakeup/kws/KwsEngine.kt) 支持热更新唤醒词"
- "[`MainActivity.kt`](app/src/main/java/com/lila/wakeup/MainActivity.kt) 状态展示太丑,帮我用 Compose 重写"
- "Build 报错 `Unresolved reference: KeywordSpotter`,怎么改?"
- "帮我加一个 logcat 过滤器,只看 KWS 命中日志"
需要主项目协调的请求(**不要自己改本工程**
- "改协议字段名" → 让用户回主项目讨论
- "升级方案 B" → 涉及 SDK 集成,回主项目
- "对接第三方新需求" → 协议要先改
---
## 十二、运行时验证
```bash
# 装到板子
adb install -r -g ./app/build/outputs/apk/debug/app-debug.apk
# 看日志
adb logcat -s KwsService.Svc:* KwsService.Engine:* KwsService.State:* KwsService.Audio:* KwsService.Send:* KwsService.Proto:* KwsService.Boot:*
# 关键日志关键字
[Engine] init done ← 引擎加载成功
[State] -> LISTENING ← 进入监听
[KWS] HIT confirmed ← 命中
[Broadcast] WAKEUP -> com.qy.lila ← 发广播给 APP
# 测试 APP 端 PAUSE/RESUME不依赖 Unity APP用 adb 模拟)
adb shell am broadcast -a com.lila.intent.action.KWS_PAUSE \
-n com.lila.wakeup/.protocol.ProtocolReceiver \
--el silence_ms 3000 --es reason "test_pause"
adb shell am broadcast -a com.lila.intent.action.KWS_RESUME \
-n com.lila.wakeup/.protocol.ProtocolReceiver \
--es reason "test_resume"
```