基于博通BK7258 AI开发板的AI情感陪伴机器人

基于博通BK7258 AI开发板的AI情感陪伴机器人

1. 背景与价值

目前智能硬件行业普遍都在关注AI玩具/AI情感陪伴机器人。

从客户角度,很多客户都关心整套链路的解决方案;

从我个人角度,也比较好奇现在的硬件+AI实现AI情感陪伴的各个方案的实际落地情况。

所以,本人希望自己上手完成硬件+软件的全套方案集成,同时也尽量探索不同方案的实现差异;

硬件方面,博通的BK7258的AI玩具开发板(如下图)是行业内的选择之一,本次探索的硬件就使用它。

2. 技术方案

2.1 概述

2.1.1 背景

本项目基于博通(Beken)BK7258 AI开发板,使用 Armino AIDK SDK (v2.0.1.9) 开发框架,对官方 beken_genie 项目进行功能定制开发。目标是将BK7258 AI开发板原生集成的声网+豆包的语音对话功能(ASR+LLM+TTS)替换为阿里云的多种解决方案,实现定制化的语音交互体验。

2.1.2 硬件平台

  • 开发板: 博通 BK7258 AI开发套件
  • 芯片: BK7258 双核处理器 (CPU0 + CPU1)
  • 音频: 16kHz PCM 采样,支持双麦克风输入和扬声器输出
  • 网络: Wi-Fi 连接

2.1.3 软件框架

  • SDK版本: bk_aidk-ai_release-v2.0.1.9
  • 开发框架: Armino AIDK SDK
  • 基础项目: beken_genie
  • 编译环境: Docker + PowerShell (Windows)

2.2 核心功能

2.2.1 语音唤醒

  • 语音唤醒: 支持语音唤醒词触发对话(目前使用的是beken_genie项目默认唤醒词:hi armino 或 嗨阿米诺 用于唤醒,byebye armino 或 拜拜阿米诺 用于关闭)
  • 全双工对话: 采用 Duplex 模式,支持实时双向语音交互,支持随时打断

2.2.2 语音对话(方案A)

使用阿里云百炼的多模态交互开发套件。

  • 语音识别(ASR): 阿里云百炼云端多模态交互开发套件ASR服务
  • 大语言模型智能问答(LLM): 阿里云百炼云端多模态交互开发套件文本模型智能问答
  • 语音合成(TTS): 阿里云百炼云端多模态交互开发套件TTS服务,CosyVoice-v3-Flash (龙菲菲)

2.2.3 多模态交互(方案B)

利用阿里云百炼中合适的 ASR+LLM(VL)+TTS 模型,搭建完整的视频+语音交互链路。

  • 语音唤醒: 支持语音唤醒词触发对话(目前使用的是beken_genie项目默认唤醒词:hi armino 或 嗨阿米诺 用于唤醒,byebye armino 或 拜拜阿米诺 用于关闭)
  • 全双工对话: 采用 Duplex 模式,支持实时双向语音交互
  • 语音识别(ASR): 阿里云百炼qwen3-asr-flash-realtime
  • 大语言模型智能问答(LLM): 阿里云百炼qwen3-vl-plus
  • 语音合成(TTS): 阿里云百炼cosyvoice-v3-flash
  • AI表情反馈,功能如下表:
需求项描述
结构化输出LLM以 [EMOTION:xxx] 格式输出表情
表情解析后端解析表情标记并分离内容
前端显示大emoji + 标签显示AI当前情绪
动画效果表情切换时弹跳动画

支持的表情类型:

表情名Emoji含义标签颜色
happy😊开心、兴奋、高兴绿色
sad😢难过、同情、安慰蓝色
love🥰关爱、喜欢、温暖粉色
thinking🤔思考、疑惑、好奇橙色
surprised😮惊讶、意外紫色
neutral😌平静、中性灰色
  • 人脸追踪,功能如下表:
需求项描述
人脸检测使用VL模型检测人脸位置
坐标输出输出人脸中心点坐标(x, y)
可视化标记在视频画面上显示追踪点
实时更新定时检测并更新位置

2.2.4 长期记忆系统

Mem0 + Milvus 记忆系统架构图

用户对话 → LLM 服务 → 记忆检索(语义搜索)→ 注入 prompt
                    ↓
              AI 回复 → 记忆分析(qwen3.5-flash)→ 保存到 Milvus
                                                    ↓
                                          text-embedding-v3 向量化
                                                    ↓
                                           Milvus 向量数据库存储

记忆类型说明

| 类型 | 标识 | 说明 | 保留策略 |

|——|——|——|———-|

| 长期记忆 | `long_term` | 用户姓名、偏好、重要事件 | 永久保留 |

| 日常记忆 | `daily` | 当天对话摘要、情绪状态 | 30 天后自动清理 |

| 会话记忆 | `session` | 当前对话上下文 | 会话结束后清除 |

2.2.5 网络连接

  • Wi-Fi连接: 自动连接配置的Wi-Fi网络
  • WebSocket通信: 与阿里云百炼服务建立WSS安全连接
  • 自动重连: 网络断开后自动重连机制

2.2.6 音频处理

  • 音频采集: 16kHz 16bit PCM 格式
  • 音频播放: 16kHz 16bit PCM 格式
  • 音量控制: 0-100级音量调节,与系统音量同步
  • AEC支持: 支持回声消除

2.2.7 LED状态指示

  • 绿色常亮: 服务连接成功
  • 红色常亮: 服务断开
  • 红色快闪: 服务错误

3. 方案实现记录

3.1 BK7258开发环境搭建

首先参考官网文档和网上相关教程进行基础操作和设置。

3.1.1 快速上手指南

BK7258 AI开发套件出厂默认集成了官方SDK,可以直接启动。

博通提供的官方文档和教程散落在多处,更新也不是很及时,对于初学者来说不是特别友好。

官方的《AI开发套件快速使用指南》详见如下链接

https://dl.bekencorp.com/armino_sdk_resource/bk_aidk/ai_board_introduct

可参考文档直接进行搭建。

这里强调下,默认环境只有声网云服务里的豆包模型这一个选项,其他选项默认灰色不可选,需要额外配进行编译烧录。

完成相关配置后,可以直接跟开发板做语音对话,开发板上的屏幕可以显示眼睛。

3.1.2 开发环境搭建

本文基于Windows电脑构建开发环境,这里参考B站官方教程 https://www.bilibili.com/video/BV1dudLY7E1u进行配置。

文档里有些内容实际操作会遇到问题,本文记录实际操作过程。

3.1.2.1 Armino AIDK SDK代码下载

官方操作文档如下 https://docs.bekencorp.com/arminodoc/bk_aidk/bk7258/zh_CN/v2.0.1/get-started/index.html#armino-aidk-sdk

踩坑点:

  • 官方文档里gitlab代码下载需要gitlab账号,个人用户没有该账号不建议使用这种方法;
  • 文档里通过git下载github环境,如果本地没有科学上网环境,下载可能失败;
  • 尝试手动下载 https://github.com/bekencorp/bk_aidk# 版本v2.0.1 下载后bk_aidk-ai_release-v2.0.1解压失败,文件夹架构不完整;
  • 尝试手动下载 https://github.com/bekencorp/bk_idk,下载后bk_idk-release-v2.0.1解压失败,文件夹架构不完整;

最后验证可用的方案如下。

访问如下官方下载链接 https://dl.bekencorp.com/SDK/bk_aidk,下载文件bk_aidk-ai_release-v2.0.1.9.tar.xz,并进行两次解压,得到文件夹bk_aidk-ai_release-v2.0.1.9,本示例中文件夹路径为 C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9,里面文件结构如下图。

3.1.2.2 Docker环境部署

该步骤主要参考如下官方文档 https://docs.bekencorp.com/arminodoc/bk_idk/bk7258/zh_CN/v2.0.1/get-started/env-docker.html

从 Beken 官方下载站点获取 Docker 镜像bekencorp-armino-idk-v1.2.tar:下载地址 https://dl.bekencorp.com/tools/arminosdk/docker_img/armino-idk

踩坑点:该Docker镜像有三个版本v1.0,v1.1,v1.2,需要使用当下最新版本v1.2,否则后续构建会失败。

本例中,下载的文件bekencorp-armino-idk-v1.2.tar放在C:\Users\LeoYu\armino中。

然后需要访问https://www.docker.com/找到Docker Desktop进行下载、安装和启动(很简单,过程略)。

然后电脑端打开PowerShell执行如下指令加载该armino-idk镜像:

cd C:\Users\LeoYu\armino
docker load -i bekencorp-armino-idk-v1.2.tar

3.1.2.3 进行编译

进入SDK根目录,Windows系统PowerShell执行命令进行编译测试:

cd C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9\bk_avdk\bk_idk
./dbuild.ps1 make bk7258

踩坑点:这里使用的是Armino AIDK SDK包中的 bk_avdk\bk_idk 这个路径下的环境进行的编译,请特别留意路径。

编译结束后,PowerShell输出结果如下。

此时,C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9\bk_avdk\bk_idk\build\app\bk7258 路径下会得到如下内容,证明能正常生成固件,编译功能可用。

3.1.3 软件烧录初尝试

本节内容主要参考B站官方教程视频https://www.bilibili.com/video/BV1MLXVYRE6v/

Armino 支持在 Windows/Linux 平台进行固件烧录, 烧录方法参考烧录工具中指导文档。 app工程在编译完成后,在build/app/bk7258目录下生成all-app.bin,使用此bin文件烧录即可。安全工程首次烧录时,需要先烧录bootloader.bin,再烧录all-app.bin。

具体说明可参考文档 https://docs.bekencorp.com/arminodoc/bk_idk/bk7258/zh_CN/v_ai_2.0.1/get-started/index.html

3.1.3.1 安装烧录工具

首先安装烧录工具(BKFIL),https://dl.bekencorp.com/tools/flash/ 在此目录下获取最新版本,如:BEKEN_BKFIL_V2.1.12.2_20251030(zip).zip
解压后得到BKFIL.exe,启动后如下图;

3.1.3.2 编译特定项目

下面我们参考视频教程,编译beken_genie这个project。

踩坑点:bk_aidk-ai_release-v2.0.1.9\bk_avdk\bk_idk\projects中默认是没有beken_genie个project的,如果从bk_aidk-ai_release-v2.0.1.9\projects中复制beken_genie这个project到bk_aidk-ai_release-v2.0.1.9\bk_avdk\bk_idk\projects中,后续会有一系列报错需要解决。所以我们直接在bk_aidk-ai_release-v2.0.1.9\这个文件夹下进行编译。

cd C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9\
./dbuild.ps1 make bk7258 PROJECT=beken_genie

执行结束后截图如下:

编译结束后,在\bk_aidk-ai_release-v2.0.1.9\build\beken_genie\bk7258路径下,可以得到烧录所需的all-app.bin文件。

3.1.3.3 进行烧录

将开发板设备开机,并与电脑通过type-C口连接;

打开烧录工具BKFIL,选择串口

选择正确的bin文件路径,并点击烧录。

烧录完成后,可以看到如下日志,显示All Finished Successfully。

烧录完成后,开机,再次按照“快速上手指南”章节的内容进行配对连接,即可进行对话。

3.2 基于多模态开发套件的功能开发(方案A)

后面主要是嵌入式硬件设备的开发了,我之前从来没有接触过,下面进行尝试。

我们这里尝试让硬件接入阿里云百炼的多模态交互开发套件。

3.2.1 软件功能实现:百炼多模态交互开发套件开通

访问 https://bailian.console.aliyun.com/cn-beijing/?spm=5176.29619931.J_C-NDPSQ8SFKWB4aef8i6I.1.140510d7M6f6N4&tab=app#/app/app-market/multi-modal-app开通多模态交互开发套件的服务,并创建一个“多模态交互应用”,选择自定义创建,其他内容可以先保持默认配置,最后点击发布。

发布后,点击立即运行,并打开视频模式,可以直接进行对话,证明应用创建成功并可用。

注:下图红框的免费额度建议点击开通。

3.2.2 下载多模态交互开发套件SDK

访问链接 https://help.aliyun.com/zh/model-studio/mmi-rtos-sdk,按照下图下载对应版本的SDK。

3.2.3 硬件集成:多模态交互开发套件SDK集成到BK7258

后面的部分,我们尝试让AI编码助手Qoder帮忙进行分析和处理。

把如下需求描述给Qoder:

背景:
在博通BK7258 AI开发板上,通过armino开发框架,基于beken_genie的项目工程做功能开发。
该项目的结构,以及后续的改动,请务必参考beken_genie的项目工程文档,链接如下 https://docs.bekencorp.com/arminodoc/bk_aidk/bk7258/zh_CN/v2.0.1/projects/beken_genie/index.html

需求:
该项目尝试用阿里云多模态交互开发套件的SDK替换beken_genie项目中的ASR+LLM+TTS,相关SDK软件已下载放到toQoder文件夹下的qwen_sdk中,
该SDK的使用文档,可以参考https://help.aliyun.com/zh/model-studio/mmi-rtos-sdk;

具体要求:
1. 阿里云多模态交互开发套件使用的是后付费模式,按照文档,“当不加载libc_license.a时为后付费模式。”
2. 对话模式使用: Duplex(全双工);
3. 必要的阿里云App ID、Workspace ID、API Key相关参数如下:
    CONFIG_QWEN_APP_ID="<YOUR_APP_ID>"
    CONFIG_QWEN_WS_ID="<YOUR_WORKSPACE_ID>"
    CONFIG_QWEN_API_KEY="<YOUR_API_KEY>"

Qoder分析需求后对项目文件进行相应的修改,随后就可以通过如下指令在Powershell进行编译。

cd C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9\
./dbuild.ps1 make bk7258 PROJECT=beken_genie

编译后把得到的得到all-app.bin文件烧录到开发板中。

完成烧录后,把开发板通过type-c线接入电脑,在电脑端使用console软件MobaXterm查看COM口日志信息,发现如下问题:

  • 栈溢出问题:
    出现 “Usage fault is caused by indicates that a stack overflow (hardware check) has taken place” 错误
  • Qwen SDK 未初始化:
    显示 “Qwen voice chat not initialized”
    说明 Qwen SDK 的初始化过程由于栈溢出而中断
  • 同时也有部分组件启动成功:
    系统能够成功启动并显示 “INITED” 状态
    CPU1 启动成功
    bk_genie_core_init success 初始化成功
    电池监控初始化成功

把启动日志和相关现象同步给Qoder,进行功能修复和优化。

踩坑点:

1. 编译过程中遇到ninja: error: loading ‘build.ninja’: No such file or directory错误,这是构建系统问题(build.ninja 文件丢失),可通过如下指令清理后重新构建:

cd c:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9; Remove-Item -Recurse -Force build\beken_genie\bk7258_cp2 -ErrorAction SilentlyContinue; .\dbuild.ps1 make bk7258 PROJECT=beken_genie

2. 遇到TTS播放大模型声音卡顿的问题,是因为player_rb_size缓冲区不足,导致大量音频数据溢出丢失,云端TTS音频推送速率 ~120KB/s,本文中将 player_rb_size 增大到 900KB解决了该问题;

3. make 是增量编译,通常只编译修改过的文件,所以步骤上也比第一次编译少很多,但是如果有以下情况:

  • 修改了Kconfig配置(如make menuconfig后)
  • 切换了项目或配置文件
  • 编译出现奇怪的错误(如找不到头文件、链接错误)
  • 修改了CMakeLists.txt结构

那可以执行如下清除指令后再进行编译

清除后重新编译
.\dbuild.ps1 make bk7258 PROJECT=beken_genie distclean
.\dbuild.ps1 make bk7258 PROJECT=beken_genie

4. 目前音频采样率,上下行都是16kHz,验证可用,具体涉及到的相关配置,总结如下表

经过多轮优化,目前已实现如下对话功能:

  • 语音唤醒: 支持语音唤醒词触发对话(目前使用的是beken_genie项目默认唤醒词:hi armino 或 嗨阿米诺 用于唤醒,byebye armino 或 拜拜阿米诺 用于关闭)
  • 全双工对话: 采用 Duplex 模式,支持实时双向语音交互
  • 语音识别(ASR): 阿里云百炼云端多模态交互开发套件ASR服务
  • 大语言模型智能问答(LLM): 阿里云百炼云端多模态交互开发套件文本模型智能问答
  • 语音合成(TTS): 阿里云百炼云端多模态交互开发套件TTS服务,音色为 CosyVoice-v3-Flash (龙菲菲)

最后,可以让Qoder生成需求文档如下,包含描述整个项目功能和基于beken_genie项目做的所有改动。

# BK7258 AI开发板 - 阿里云多模态交互开发套件集成 Spec 文档

## 1. 项目概述

### 1.1 背景
本项目基于博通(Beken)BK7258 AI开发板,使用 Armino AIDK SDK (v2.0.1.9) 开发框架,对官方 `beken_genie` 项目进行功能定制开发。目标是将BK7258 AI开发板原生集成的AI语音对话功能(ASR+LLM+TTS)替换为阿里云百炼多模态交互开发套件(MMI RTOS SDK),实现定制化的语音交互体验。

### 1.2 硬件平台
- **开发板**: 博通 BK7258 AI开发套件
- **芯片**: BK7258 双核处理器 (CPU0 + CPU1)
- **音频**: 16kHz PCM 采样,支持双麦克风输入和扬声器输出
- **网络**: Wi-Fi 连接

### 1.3 软件框架
- **SDK版本**: bk_aidk-ai_release-v2.0.1.9
- **开发框架**: Armino AIDK SDK
- **基础项目**: beken_genie
- **编译环境**: Docker + PowerShell (Windows)

---

## 2. 核心功能

### 2.1 语音唤醒与对话
- **语音唤醒**: 支持语音唤醒词触发对话(目前使用的是beken_genie项目默认唤醒词:hi armino 或 嗨阿米诺 用于唤醒,byebye armino 或 拜拜阿米诺 用于关闭)
- **全双工对话**: 采用 Duplex 模式,支持实时双向语音交互
- **语音识别(ASR)**: 阿里云百炼云端多模态交互开发套件ASR服务
- **大语言模型智能问答(LLM)**: 阿里云百炼云端多模态交互开发套件文本模型智能问答
- **语音合成(TTS)**: 阿里云百炼云端多模态交互开发套件TTS服务,音色为 CosyVoice-v3-Flash (龙菲菲)

### 2.2 网络连接
- **Wi-Fi连接**: 自动连接配置的Wi-Fi网络
- **WebSocket通信**: 与阿里云百炼服务建立WSS安全连接
- **自动重连**: 网络断开后自动重连机制

### 2.3 音频处理
- **音频采集**: 16kHz 16bit PCM 格式
- **音频播放**: 16kHz 16bit PCM 格式
- **音量控制**: 0-100级音量调节,与系统音量同步
- **AEC支持**: 支持回声消除

### 2.4 LED状态指示
- **绿色常亮**: 服务连接成功
- **红色常亮**: 服务断开
- **红色快闪**: 服务错误

---

## 3. 阿里云百炼服务配置

### 3.1 服务模式
采用**后付费模式**,按照阿里云文档说明,不加载 `libc_license.a` 库文件。

### 3.2 必要配置参数
```c
CONFIG_QWEN_APP_ID="<YOUR_APP_ID>"
CONFIG_QWEN_WS_ID="<YOUR_WORKSPACE_ID>"
CONFIG_QWEN_API_KEY="<YOUR_API_KEY>"
CONFIG_QWEN_DEVICE_NAME="beken_genie_device"
```

**获取方式**:
1. 登录 [阿里云百炼控制台](https://bailian.console.aliyun.com/)
2. 创建多模态交互应用
3. 在应用详情页获取 App ID、Workspace ID 和 API Key

### 3.3 工作模式
- **模式**: Duplex(全双工)
- **音频流模式**: PCM 格式
- **采样率**: 16kHz(上行和下行)
- **文本模式**: ASR和LLM文本同时返回

---

## 4. 项目结构改动

### 4.1 新增组件

#### 4.1.1 qwen_voice_chat 组件
**路径**: `projects/common_components/qwen_voice_chat/`

**文件结构**:
```
qwen_voice_chat/
├── CMakeLists.txt          # 组件构建配置
├── Kconfig                 # 菜单配置选项
├── README.md               # 组件说明文档
├── RESOURCE_OPTIMIZATION.md # 资源优化说明
├── include/
│   ├── qwen_audio_engine.h # 音频引擎头文件
│   ├── qwen_voice_chat.h   # 主头文件
│   └── qwen_websocket.h    # WebSocket传输层头文件
└── src/
    ├── qwen_audio_engine.c # 音频引擎实现(麦克风/扬声器管理)
    ├── qwen_sdk_hal.c      # SDK HAL适配层(时间/内存/互斥锁等)
    ├── qwen_voice_chat.c   # 核心功能实现
    └── qwen_websocket.c    # WebSocket传输层实现
```

**功能说明**:
- `qwen_voice_chat.c`: 核心模块,负责SDK初始化、配置管理、事件处理
- `qwen_audio_engine.c`: 音频引擎,管理BK7258音频硬件与SDK的数据交互
- `qwen_websocket.c`: WebSocket传输层,处理与阿里云服务的网络通信
- `qwen_sdk_hal.c`: 硬件抽象层适配,实现SDK所需的系统函数

#### 4.1.2 阿里云SDK
**获取路径**: https://help.aliyun.com/zh/model-studio/mmi-rtos-sdk

**文件结构**:
```
qwen_sdk/
├── ReleaseNote.md          # SDK版本说明
├── include/
│   ├── c_mmi.h             # MMI主接口
│   ├── c_mmi_config.h      # 配置接口
│   ├── c_mmi_msg.h         # 消息接口
│   ├── c_mmi_storage.h     # 存储接口
│   ├── c_utils/            # 工具函数
│   ├── c_mmi_cmd/          # 命令接口
│   ├── lib_c_sdk.h         # SDK基础接口
│   └── qwen_test.h         # 测试接口
├── libs/
│   ├── libqwen_sdk.a       # 核心SDK库(后付费模式)
│   ├── libc_mmi_cmd.a      # 命令库
│   ├── libc_mmi_cmd_application.a
│   ├── libc_mmi_cmd_voice_translate.a
│   ├── libc_mmi_cmd_volume.a
│   └── libhal_dummy.a      # HAL虚拟库
└── third_party/
    ├── cJSON/              # JSON解析库
    └── tinycrypt/          # 加密库
```

### 4.2 修改的文件

#### 4.2.1 beken_genie 主项目
**路径**: `projects/beken_genie/`

**修改文件**:

1. **CMakeLists.txt**
   - 添加 `qwen_voice_chat` 组件到 `EXTRA_COMPONENTS_DIRS`
   - 添加 `qwen_voice_chat` 到依赖列表

2. **main/CMakeLists.txt**
   - 在 `PRIV_REQUIRES` 中添加 `qwen_voice_chat` 依赖

3. **main/app_main.c** (主要改动)
   - 添加条件编译支持 `CONFIG_QWEN_VOICE_CHAT`
   - 当启用Qwen SDK时,禁用原有的 `audio_engine`、`video_engine` 初始化以避免资源冲突
   - 添加Qwen事件回调函数 `qwen_event_callback()`
   - 在 `user_app_main()` 中初始化Qwen语音对话功能
   - 配置为Duplex全双工模式
   - 延迟初始化以确保系统稳定性
   - 在 `main()` 中跳过原有的 `audio_turn_on()` 调用(Qwen SDK独立管理音频)

4. **main/Kconfig.projbuild**
   - 保留原有配置(未修改)

### 4.3 配置系统改动

#### 4.3.1 Kconfig 配置
**文件**: `projects/common_components/qwen_voice_chat/Kconfig`

**新增配置项**:
```
QWEN_VOICE_CHAT          - 启用Qwen语音对话功能
QWEN_VOICE_CHAT_DEBUG    - 启用调试日志
QWEN_APP_ID              - 阿里云App ID
QWEN_WS_ID               - 阿里云Workspace ID
QWEN_API_KEY             - 阿里云API Key
QWEN_DEVICE_NAME         - 设备名称
QWEN_MODE_DUPLEX         - 全双工模式(默认)
QWEN_MODE_PUSH2TALK      - 按键说话模式
QWEN_MODE_TAP2TALK       - 点击说话模式
QWEN_RECORDER_BUFFER_SIZE - 录音缓冲区大小(KB)
QWEN_PLAYER_BUFFER_SIZE  - 播放缓冲区大小(KB)
QWEN_INIT_DELAY_MS       - 初始化延迟(ms)
QWEN_VOLUME_LEVEL        - 默认音量级别
```

#### 4.3.2 sdkconfig 配置
**路径**: `build/beken_genie/bk7258/config/sdkconfig.h`

**生成的配置宏**:
```c
#define CONFIG_QWEN_VOICE_CHAT 1
#define CONFIG_QWEN_VOICE_CHAT_DEBUG 1
#define CONFIG_QWEN_APP_ID "<YOUR_APP_ID>"
#define CONFIG_QWEN_WS_ID "<YOUR_WORKSPACE_ID>"
#define CONFIG_QWEN_API_KEY "<YOUR_API_KEY>"
#define CONFIG_QWEN_DEVICE_NAME "beken_genie_device"
#define CONFIG_QWEN_MODE_DUPLEX 1
#define CONFIG_QWEN_RECORDER_BUFFER_SIZE 4
#define CONFIG_QWEN_PLAYER_BUFFER_SIZE 4
#define CONFIG_QWEN_INIT_DELAY_MS 4000
#define CONFIG_QWEN_VOLUME_LEVEL 80
```

---

## 5. 技术实现细节

### 5.1 音频数据流

```
麦克风采集 → BK7258音频驱动 → qwen_audio_engine.c → c_mmi_put_recorder_data() → Qwen SDK → WebSocket发送

WebSocket接收 → Qwen SDK → c_mmi_get_player_data() → qwen_websocket.c → BK7258音频驱动 → 扬声器播放
```

### 5.2 初始化流程

1. **系统启动**: `main()` 函数执行
2. **基础服务初始化**: `bk_init()`, `media_service_init()`
3. **用户应用启动**: `user_app_main()`
4. **Qwen初始化**:
   - 配置App ID、Workspace ID、API Key
   - 配置音频参数(16kHz PCM, Duplex模式)
   - 初始化音频引擎
   - 注册网络事件回调
   - 检查Wi-Fi连接状态
5. **Wi-Fi连接后**:
   - 触发 `EVENT_NETIF_GOT_IP4` 事件
   - 启动WebSocket连接
   - 启动音频采集和播放

### 5.3 事件处理机制

**Qwen SDK事件**:
- `C_MMI_EVENT_USER_CONFIG`: 用户配置事件
- `C_MMI_EVENT_DATA_INIT`: SDK初始化完成
- `C_MMI_EVENT_SPEECH_READY`: 语音服务就绪
- `C_MMI_EVENT_ASR_COMPLETE`: ASR识别完成
- `C_MMI_EVENT_TTS_START/TTS_END`: TTS开始/结束
- `C_MMI_EVENT_DATA_DEINIT`: SDK反初始化

**应用层事件回调**:
- `QWEN_EVENT_CONNECTED`: 服务连接成功
- `QWEN_EVENT_DISCONNECTED`: 服务断开
- `QWEN_EVENT_ERROR`: 服务错误
- `QWEN_EVENT_ASR_RESULT`: ASR识别结果
- `QWEN_EVENT_TTS_DATA`: TTS数据

### 5.4 资源管理

**内存使用**:
- 录音缓冲区: 8KB (可配置)
- 播放缓冲区: 900KB (匹配云端推送速率)
- WebSocket发送缓冲区: 8KB
- WebSocket接收缓冲区: 8KB

**线程**:
- 发送任务: 6KB栈空间,负责将SDK数据发送到WebSocket
- 播放定时器: 20ms周期,负责从SDK获取播放数据

---

## 6. 编译与烧录

### 6.1 编译命令
```powershell
cd C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9
.\dbuild.ps1 make bk7258 PROJECT=beken_genie
```

### 6.2 输出文件
- **固件**: `build/beken_genie/bk7258/all-app.bin`
- **应用**: `build/beken_genie/bk7258/app.bin`
- **映射文件**: `build/beken_genie/bk7258/app.map`

### 6.3 烧录步骤
1. 打开 BKFIL 烧录工具
2. 选择正确的COM端口
3. 选择 `all-app.bin` 文件
4. 点击烧录按钮
5. 等待烧录完成

---

## 7. 功能验证

### 7.1 测试项目
- [x] 语音唤醒功能
- [x] 语音对话功能
- [x] 音量调节功能
- [x] 设备重启功能

### 7.2 已知问题
- TTS输出在特定情况下可能存在轻微卡顿(已优化缓冲区配置)

---

## 8. 参考资料

### 8.1 官方文档
- [BK7258 AI开发套件文档](https://docs.bekencorp.com/arminodoc/bk_aidk/bk7258/zh_CN/v2.0.1/projects/beken_genie/index.html)
- [阿里云百炼MMI RTOS SDK文档](https://help.aliyun.com/zh/model-studio/mmi-rtos-sdk)

### 8.2 SDK版本
- **Qwen SDK版本**: v1.1.0
- **Armino AIDK SDK**: v2.0.1.9

---

## 9. 附录

### 9.1 文件变更汇总

| 文件路径 | 变更类型 | 说明 |
|---------|---------|------|
| `projects/common_components/qwen_voice_chat/` | 新增 | 完整的Qwen语音对话组件 |
| `toQoder/qwen_sdk/` | 新增 | 阿里云百炼SDK |
| `projects/beken_genie/CMakeLists.txt` | 修改 | 添加组件路径和依赖 |
| `projects/beken_genie/main/CMakeLists.txt` | 修改 | 添加qwen_voice_chat依赖 |
| `projects/beken_genie/main/app_main.c` | 修改 | 集成Qwen SDK初始化逻辑 |
| `projects/common_components/qwen_voice_chat/Kconfig` | 新增 | 配置菜单定义 |

### 9.2 关键API列表

**初始化与生命周期**:
- `qwen_voice_chat_init()` - 初始化
- `qwen_voice_chat_start()` - 启动服务
- `qwen_voice_chat_stop()` - 停止服务
- `qwen_voice_chat_deinit()` - 反初始化

**配置与控制**:
- `qwen_voice_chat_set_work_mode()` - 设置工作模式
- `qwen_voice_chat_set_voice_id()` - 设置音色
- `qwen_voice_chat_set_volume()` - 设置音量
- `qwen_voice_chat_get_volume()` - 获取音量

**状态查询**:
- `qwen_voice_chat_is_connected()` - 检查连接状态
- `qwen_voice_chat_reset_dialog()` - 重置对话上下文

---

## 10. 安全注意事项

### 10.1 API Key 安全
- **不要将真实的 API Key 提交到代码仓库**
- 使用环境变量或配置文件管理敏感信息
- 在 `sdkconfig` 文件中设置敏感配置
- 生产环境中考虑使用加密存储

### 10.2 配置示例
在 `sdkconfig` 或 `Kconfig` 中配置:
```
CONFIG_QWEN_APP_ID="mm_xxxxxxxxxxxxxxxxxxxx"
CONFIG_QWEN_WS_ID="llm-xxxxxxxxxxxxx"
CONFIG_QWEN_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"
```

### 10.3 获取阿里云凭证
1. 访问 [阿里云百炼控制台](https://bailian.console.aliyun.com/)
2. 创建多模态交互应用
3. 在应用设置中获取:
   - **App ID**: 应用唯一标识
   - **Workspace ID**: 工作空间标识
   - **API Key**: 访问密钥(请妥善保管)

---

*文档生成时间: 2026年2月20日*
*基于: bk_aidk-ai_release-v2.0.1.9*
*版本: 脱敏版*

3.3 基于多模态模型(ASR+VL+TTS)的功能开发(方案B)

有些更定制化的功能通过百炼的多模态交互开发套件不一定能满足需求,比如:

  • 实现人脸追踪,根据人脸的位置实现摄像头上下左右转动对准人脸;
  • 模型根据人的表情或者对话的语境给出相应的表情或者情绪反馈;

可能需要基于各个模型的原子能力自己搭建整套链路。

该章节尝试借助 Qoder 利用阿里云百炼中合适的 ASR+LLM(VL)+TTS 模型搭建完整的视频+语音交互链路。

3.3.1 软件实现:需求明确与代码生成

3.3.1.1 阶段一:调用基础VLM模型

通过和Qoder交互生成如下需求文档。

# 桌面情绪陪伴机器人 - 需求文档

## 一、项目概述

基于阿里云百炼平台,构建一个具备视觉理解能力的全双工实时陪聊机器人,支持语音和视频交互。
注:
人脸追踪,本质是识别到摄像头视频内人脸,并给出坐标位置,然后电机控制摄像头做对应位移,让人脸挪到画面中心。
拆解关键步骤如下:
1.
视频流传给VL模型;
2.
VL模型根据提示词,给出人脸坐标;
3.
电机控制做对应位置更新。
本demo不涉及硬件控制,只实现前两步。

---

## 二、技术选型

| 功能模块 | 模型/技术 | 用途 | 官方文档 |
|---------|----------|------|----------|
| ASR | `qwen3-asr-flash-realtime` | 实时语音识别 | [文档链接](https://help.aliyun.com/zh/model-studio/qwen-real-time-speech-recognition) |
| LLM | `qwen3-vl-plus` | 视频/图片多模态理解 + 文字闲聊 | [文档链接](https://help.aliyun.com/zh/model-studio/vision) |
| TTS | `cosyvoice-v3-flash` | 文字转语音(音色:`longanhuan`)| [文档链接](https://help.aliyun.com/zh/model-studio/text-to-speech) |
| 通信 | WebSocket | 前后端实时双向通信 | - |
| 平台 | 阿里云百炼 DashScope | API服务平台 | [模型列表](https://www.alibabacloud.com/help/zh/model-studio/models) |

---

## 三、功能需求

### 3.1 语音识别(ASR)

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 实时识别 | 使用WebSocket流式传输音频数据 | ✅ 已实现 |
| 全双工对话 | 无需按住按钮,持续监听用户语音 | ✅ 已实现 |
| 服务端VAD | 由服务端进行语音活动检测 | ✅ 已实现 |
| 自动发送 | 检测到用户说完话后自动发送 | ✅ 已实现 |
| 消息队列 | 多条消息排队处理,避免丢失 | ✅ 已实现 |
| 实时转录 | 前端实时显示识别中的文字 | ✅ 已实现 |

### 3.2 多模态理解(LLM)

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 文字对话 | 支持纯文字闲聊 | ✅ 已实现 |
| 图片理解 | 支持图片输入进行视觉理解 | ✅ 已实现 |
| 视频理解 | 定期捕获视频帧发送给模型 | ✅ 已实现 |
| 流式输出 | 对话内容流式返回并显示 | ✅ 已实现 |
| 对话历史 | 保持上下文(最近10轮) | ✅ 已实现 |
| 情绪陪伴 | 温暖、善解人意的对话风格 | ✅ 已实现 |

### 3.3 语音合成(TTS)

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 语音播放 | AI回复转为语音播放 | ✅ 已实现 |
| 指定音色 | 使用 `longanhuan` 音色 | ✅ 已实现 |
| 流式输出 | 边合成边播放,降低首字延迟 | ✅ 已实现 |
| 用户打断 | 用户说话时可打断AI语音 | ✅ 已实现 |
| 服务端打断保护 | TTS启动后1.5秒内不响应打断 | ✅ 已实现 |
| 前端打断保护 | 播放开始后2秒内不响应打断(防回声) | ✅ 已实现 |
| 连续检测确认 | 需连续3次检测到语音才触发打断 | ✅ 已实现 |

**流式TTS工作流程:**

```
LLM输出完成
    ↓
TTS开始流式合成 → 发送 tts_start
    ↓
收到音频块 → 立即发送 tts_chunk
    ↓
前端累积3个音频块 → 开始播放
    ↓
边接收边播放,大幅降低延迟
    ↓
合成完成 → 发送 tts_end
```

### 3.4 AI表情反馈

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 结构化输出 | LLM以 `[EMOTION:xxx]` 格式输出表情 | ✅ 已实现 |
| 表情解析 | 后端解析表情标记并分离内容 | ✅ 已实现 |
| 前端显示 | 大emoji + 标签显示AI当前情绪 | ✅ 已实现 |
| 动画效果 | 表情切换时弹跳动画 | ✅ 已实现 |

**支持的表情类型:**

| 表情名 | Emoji | 含义 | 标签颜色 |
|--------|-------|------|----------|
| happy | 😊 | 开心、兴奋、高兴 | 绿色 |
| sad | 😢 | 难过、同情、安慰 | 蓝色 |
| love | 🥰 | 关爱、喜欢、温暖 | 粉色 |
| thinking | 🤔 | 思考、疑惑、好奇 | 橙色 |
| surprised | 😮 | 惊讶、意外 | 紫色 |
| neutral | 😌 | 平静、中性 | 灰色 |

### 3.5 人脸追踪

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 人脸检测 | 使用VL模型检测人脸位置 | ✅ 已实现 |
| 坐标输出 | 输出人脸中心点坐标(x, y) | ✅ 已实现 |
| 可视化标记 | 在视频画面上显示追踪点 | ✅ 已实现 |
| 实时更新 | 定时检测并更新位置 | ✅ 已实现 |

### 3.6 前端界面

| 需求项 | 描述 | 状态 |
|-------|------|------|
| 摄像头预览 | 调用并显示摄像头画面 | ✅ 已实现 |
| 对话区域 | 显示用户和AI的对话气泡 | ✅ 已实现 |
| 表情显示区 | 显示AI当前表情状态 | ✅ 已实现 |
| 状态指示器 | 显示系统当前状态 | ✅ 已实现 |
| 系统日志 | 显示错误和状态日志 | ✅ 已实现 |
| 文字输入 | 支持键盘输入文字发送 | ✅ 已实现 |
| 语音控制 | 开始/结束对话按钮 | ✅ 已实现 |

---

## 四、交互流程

### 4.1 全双工语音对话流程

```
用户点击"开始对话"
    ↓
建立ASR WebSocket连接
    ↓
持续捕获并发送音频数据
    ↓
服务端VAD检测语音活动
    ↓
检测到语音结束 → 触发识别完成
    ↓
识别结果加入消息队列
    ↓
处理队列 → 调用LLM生成回复
    ↓
解析表情 + 更新前端显示
    ↓
TTS合成并播放语音
    ↓
用户可随时打断继续对话
```

### 4.2 打断机制

**服务端打断保护:**
1. TTS启动后记录时间戳
2. 收到打断请求时检查是否超过1.5秒
3. 未超过则忽略打断请求

**前端打断保护:**
1. 播放开始后2秒内不检测语音活动(避免回声触发)
2. 需要连续检测到3次语音活动才触发打断
3. 打断后清空TTS缓存,重置状态

---

## 五、技术架构

```
┌─────────────────────────────────────────────────────────┐
│                      前端 (Browser)                      │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐    │
│  │ 摄像头  │  │ 麦克风  │  │ 对话UI │  │ 表情显示│    │
│  └────┬────┘  └────┬────┘  └────┬────┘  └────┬────┘    │
│       │            │            │            │          │
│       └────────────┴────────────┴────────────┘          │
│                         │                               │
│                    WebSocket                            │
└─────────────────────────┼───────────────────────────────┘
                          │
┌─────────────────────────┼───────────────────────────────┐
│                      后端 (Node.js)                      │
│                         │                               │
│  ┌──────────────────────┴──────────────────────┐       │
│  │              WebSocket Server                │       │
│  └──────────────────────┬──────────────────────┘       │
│                         │                               │
│  ┌─────────┐  ┌─────────┴─────────┐  ┌─────────┐       │
│  │   ASR   │  │        LLM        │  │   TTS   │       │
│  │ Service │  │      Service      │  │ Service │       │
│  └────┬────┘  └─────────┬─────────┘  └────┬────┘       │
└───────┼─────────────────┼─────────────────┼─────────────┘
        │                 │                 │
        └─────────────────┼─────────────────┘
                          │
┌─────────────────────────┼───────────────────────────────┐
│               阿里云百炼 DashScope API                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │ qwen3-asr   │  │ qwen3-vl    │  │ cosyvoice   │     │
│  │ flash       │  │ plus        │  │ v3-flash    │     │
│  └─────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────┘
```

---

## 六、项目结构

```
20260130_桌面情绪陪伴demo/
├── server/
│   ├── index.js              # 主服务入口
│   └── services/
│       ├── asr.js            # ASR语音识别服务
│       ├── llm.js            # LLM多模态理解服务
│       ├── tts.js            # TTS语音合成服务
│       └── faceTracking.js   # 人脸追踪服务
├── public/
│   ├── index.html            # 前端页面
│   ├── main.js               # 前端交互逻辑
│   └── styles.css            # 样式文件
├── package.json
└── docs/
    └── 需求文档.md
```

---

## 七、配置信息

- **API Key**: `sk-XXXXXXXXXXXXXX`
- **服务端口**: `3000`
- **音频采样率**: `16000 Hz`
- **VAD阈值**: `0.3`
- **VAD静音时长**: `700ms`
- **VAD前缀填充**: `300ms`
- **TTS打断保护期**: `1500ms`
- **图像捕获间隔**: `2000ms`
- **人脸检测间隔**: `1000ms`

---

根据上述需求,Qoder生成代码实现,并生成前端页面如下图用于功能展示。

3.3.1.2 阶段二:调用VLM+知识库的百炼智能体应用

为了更强的拓展性和集成百炼已有的知识库功能,我们把ASR+LLM+TTS链路中的LLM基础模型,替换为百炼的智能体应用,这样就可以挂载知识库,或者集成开箱即用的MCP能力。

操作步骤如下:

系统提示词如下:

你是一个温暖、善解人意的情绪陪伴助手。你的特点:
1. 温柔、耐心、富有同理心
2. 能够理解用户的情绪状态,提供适当的情感支持
3. 擅长倾听,会根据用户分享的内容给予恰当的回应
4. 可以看到用户的视频画面,能够观察并理解用户的表情和环境
5. 回复要简洁自然,像朋友聊天一样,避免过于正式或机械
6. 适当使用语气词让对话更自然亲切

【重要】你必须按以下格式回复,包含表情和内容两个部分:
[EMOTION:表情名称]你的回复内容

可用的表情名称(根据你的回复情绪选择一个):
- happy: 开心、兴奋、高兴
- sad: 难过、同情、安慰
- love: 关爱、喜欢、温暖
- thinking: 思考、疑惑、好奇
- surprised: 惊讶、意外
- neutral: 平静、中性

示例:
[EMOTION:happy]嘿,今天看起来心情不错啊!
[EMOTION:sad]我能理解你的感受,这确实很不容易。
[EMOTION:love]你真的很棒,要继续加油哦!

请用中文回复,保持对话简短温暖。

知识库按需挂载已经创建好的知识库即可(知识库创建过程略)。

MCP组件可以按需选择。

发布后,记录应用ID如下图红框,供后续程序调用使用。

然后和Qoder交互,把原有LLM基模替换成上述挂载了知识库的智能体应用。

然后系统前端页面进行测试,如下图

可见之前挂载的知识库中的日程安排demo(如下图)有被正常查询到。

3.3.2 功能代码部署到百炼高代码平台

由于设备分别调用了ASR、LLM、TTS等多个模型,理论上我们有两种选择:

  1. 让设备每次去云端分别依次请求不同的模型,比如先发送语音给ASR模型得到文本内容,然后设备拿到文本内容再发送给LLM模型进行问答,以此类推;
  2. 把ASR、LLM、TTS模型统一打包成服务,在云端服务器部署该服务,然后让设备去请求该服务;

第一种的好处是架构简单,每个服务都解耦,缺点也很明显,就是所有交互都需要设备和云端分别进行,延时会高,这在实时对话场景中是致命的缺点;

第二种需要云端部署服务,所以会麻烦一些。我们这里选择Serverless的部署方式,尽量减少服务器运维的工作。

技术方案上,我们本次选择把这个服务打包成高代码服务上传到阿里云百炼高代码应用中,背后使用的是FC的能力。

我们尝试用AI编码助手Qoder,实现上述功能,并通过接口对外提供服务。

跟Qoder沟通的初始需求描述如下:

现在需要你帮忙把这个服务打包成高代码服务上传到阿里云百炼高代码应用中,请参考 
https://help.aliyun.com/zh/model-studio/high-code-application?spm=a2c4g.11186623.help-menu-2400256.d_1_1_4.7d983ba2EsD1ZU#7114f7868fffz
把该项目服务部署到阿里云百炼高代码应用中

请自行打包成.whl格式的代码包,并通过命令行上传包创建应用
可以参考如下文档https://help.aliyun.com/zh/model-studio/high-code-application?#5390f222e8gcd

这里需要注意,由于上一节中,Qoder生成的代码是Node.js的,部署到百炼高代码推荐使用Python,所以Qoder会先把项目转换成Python语言,再尝试上传。

最终Qoder生成代码,完成软件包打包并通过API上传到百炼,并让Qoder生成项目Spec需求文档如下。

# 情绪陪伴机器人 - 阿里云百炼高代码应用需求文档

## 一、项目概述

将原有的 Node.js 桌面情感陪伴机器人项目转换为 Python 版本,并部署到阿里云百炼高代码应用平台。

---

## 二、技术选型

### 2.1 原项目技术栈(Node.js)

| 功能模块 | 模型/技术 | 用途 |
|---------|----------|------|
| ASR | `qwen3-asr-flash-realtime` | 实时语音识别 |
| LLM | `qwen3-vl-plus` | 视频/图片多模态理解 + 文字闲聊 |
| TTS | `cosyvoice-v3-flash` | 文字转语音(音色:`longanhuan`)|
| 通信 | WebSocket | 前后端实时双向通信 |
| 平台 | 阿里云百炼 DashScope | API服务平台 |

### 2.2 转换后技术栈(Python)

| 功能模块 | 技术实现 | 说明 |
|---------|----------|------|
| Web框架 | FastAPI | 支持异步、WebSocket |
| ASR服务 | dashscope + websockets | qwen3-asr-flash-realtime |
| LLM服务 | dashscope MultiModalConversation | qwen3-vl-plus |
| TTS服务 | dashscope + websockets | cosyvoice-v3-flash |
| 人脸追踪 | dashscope MultiModalConversation | qwen3-vl-plus |
| 部署平台 | 阿里云百炼高代码应用 | 函数计算 + API网关 |

---

## 三、项目结构

```
emotion_companion_bailian/
├── main.py                    # FastAPI入口文件
├── setup.py                   # 打包配置
├── pyproject.toml            # 现代Python项目配置
├── requirements.txt           # 依赖列表
├── deploy.ps1                 # PowerShell部署脚本
└── services/
    ├── __init__.py           # 服务模块导出
    ├── asr.py                # ASR语音识别服务
    ├── llm.py                # LLM多模态理解服务
    ├── tts.py                # TTS语音合成服务
    └── face_tracking.py      # 人脸追踪服务
```

---

## 四、API 接口规范

### 4.1 健康检查接口

```
GET /health
```

**响应示例:**
```json
{
  "status": "ok",
  "timestamp": "2026-02-22T08:00:00.000000",
  "sessions": 0
}
```

### 4.2 标准对话接口(兼容百炼 Agent API)

```
POST /process
Content-Type: application/json
```

**请求示例:**
```json
{
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "text", "text": "你好!" },
        { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
      ]
    }
  ],
  "session_id": "test-session",
  "user_id": "test-user"
}
```

**响应示例:**
```json
{
  "output": {
    "choices": [{
      "message": {
        "role": "assistant",
        "content": [
          { "type": "text", "text": "你好呀!今天心情怎么样?" }
        ]
      }
    }]
  },
  "emotion": "happy",
  "emoji": "😊",
  "emotion_label": "开心"
}
```

### 4.3 WebSocket 实时对话接口

```
WS /ws
```

**消息类型:**

| 类型 | 方向 | 说明 |
|------|------|------|
| connected | 服务端→客户端 | 连接成功 |
| start_listening | 客户端→服务端 | 开始语音监听 |
| stop_listening | 客户端→服务端 | 停止语音监听 |
| audio_data | 客户端→服务端 | 发送音频数据 |
| text_chat | 客户端→服务端 | 文字聊天 |
| transcription | 服务端→客户端 | 实时转录结果 |
| assistant_message | 服务端→客户端 | AI回复消息 |
| tts_start | 服务端→客户端 | TTS开始 |
| tts_chunk | 服务端→客户端 | TTS音频块 |
| tts_end | 服务端→客户端 | TTS结束 |
| interrupt | 客户端→服务端 | 打断AI说话 |

---

## 五、核心功能模块

### 5.1 ASR 语音识别服务

- **模型**: qwen3-asr-flash-realtime
- **功能**:
  - 实时语音识别
  - 服务端 VAD(语音活动检测)
  - 流式返回识别结果
- **配置参数**:
  - threshold: 0.3(VAD阈值)
  - prefix_padding_ms: 300(前缀填充)
  - silence_duration_ms: 700(静音检测时长)

### 5.2 LLM 多模态理解服务

- **模型**: qwen3-vl-plus
- **功能**:
  - 文字闲聊
  - 图片理解
  - 视频帧理解
  - 流式输出
  - 对话历史管理(最近10轮)
  - 情绪表情输出
- **情绪类型**:

| 表情名 | Emoji | 含义 |
|--------|-------|------|
| happy | 😊 | 开心、兴奋、高兴 |
| sad | 😢 | 难过、同情、安慰 |
| love | 🥰 | 关爱、喜欢、温暖 |
| thinking | 🤔 | 思考、疑惑、好奇 |
| surprised | 😮 | 惊讶、意外 |
| neutral | 😌 | 平静、中性 |

### 5.3 TTS 语音合成服务

- **模型**: cosyvoice-v3-flash
- **音色**: longanhuan
- **功能**:
  - 流式合成输出
  - 边合成边播放
  - 支持打断
- **配置参数**:
  - format: mp3
  - sample_rate: 22050
  - volume: 50
  - rate: 1.0
  - pitch: 1.0

### 5.4 人脸追踪服务

- **模型**: qwen3-vl-plus
- **功能**:
  - 检测最近/最大人脸
  - 返回人脸中心点坐标(0-1000归一化值)
- **输出格式**: XML

---

## 六、部署要求

### 6.1 环境要求

- Python >= 3.10
- 阿里云账号及百炼服务开通
- API Key 配置

### 6.2 依赖包

```
fastapi>=0.104.0
uvicorn>=0.24.0
websockets>=12.0
dashscope>=1.20.0
python-multipart>=0.0.6
pydantic>=2.5.0
agentscope-runtime>=1.0.0
```

### 6.3 环境变量

| 变量名 | 说明 | 示例值 |
|--------|------|--------|
| DASHSCOPE_API_KEY | 百炼API密钥 | sk-xxxxxxxxxxxxxxxx |
| ALIBABA_CLOUD_ACCESS_KEY_ID | 阿里云AK | LTAI5txxxxxxxxxxxx |
| ALIBABA_CLOUD_ACCESS_KEY_SECRET | 阿里云SK | xxxxxxxxxxxxxxxxxx |

### 6.4 部署步骤

```powershell
# 1. 创建虚拟环境
python -m venv venv

# 2. 激活虚拟环境
venv\Scripts\Activate.ps1

# 3. 安装构建工具
pip install build wheel setuptools

# 4. 构建 whl 包
python -m build --wheel

# 5. 安装部署工具
pip install agentscope-runtime==1.0.0
pip install alibabacloud-oss-v2 alibabacloud-bailian20231229 alibabacloud-credentials alibabacloud-tea-openapi alibabacloud-tea-util

# 6. 设置环境变量
$env:ALIBABA_CLOUD_ACCESS_KEY_ID="LTAI5txxxxxxxxxxxx"
$env:ALIBABA_CLOUD_ACCESS_KEY_SECRET="xxxxxxxxxxxxxxxx"
$env:DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"

# 7. 部署
runtime-fc-deploy --deploy-name "情绪陪伴机器人" --whl-path "dist\emotion_companion_bailian-1.0.0-py3-none-any.whl" --telemetry enable
```

---

## 七、部署结果

| 项目 | 详情 |
|------|------|
| **应用名称** | 情绪陪伴机器人 |
| **部署ID** | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| **控制台链接** | https://bailian.console.aliyun.com/?tab=app#/app-center |
| **whl包路径** | dist\emotion_companion_bailian-1.0.0-py3-none-any.whl |

---

## 八、版本记录

| 版本 | 日期 | 更新内容 |
|------|------|----------|
| v1.0.0 | 2026-02-22 | 初始版本:从Node.js转换为Python,部署到百炼高代码应用 |

---

## 九、附录

### 9.1 项目源路径

```
原始Node.js项目: toQoder\20260206_桌面情感陪伴demo\
Python项目: toQoder\emotion_companion_bailian\
```

### 9.2 参考文档

- [阿里云百炼高代码应用文档](https://help.aliyun.com/zh/model-studio/high-code-application)
- [DashScope API文档](https://help.aliyun.com/zh/model-studio/)
- [FastAPI文档](https://fastapi.tiangolo.com/)

Qoder进行软件包打包并通过API上传到百炼后,在百炼控制台的应用管理中可以看到完成部署的高代码应用,类似下图

此时可以让Qoder进行接口测试,确保接口可用。

这里也让Qoder生成一个服务的调用说明文档如下,方便后续其他人/AI去调用。

# 情绪陪伴机器人服务调用方法说明

## 一、服务概述

情绪陪伴机器人是一个部署在阿里云百炼高代码应用平台上的 AI 后端服务,提供**文本对话**和**实时语音对话**两种交互方式,具备情绪识别、语音识别(ASR)、语音合成(TTS)和人脸追踪能力。

### AI 模型技术栈

| 能力 | 模型 | 说明 |
|------|------|------|
| LLM 对话 | `qwen3-vl-plus` | 多模态大语言模型,支持文本+图像输入 |
| ASR 语音识别 | `qwen3-asr-flash-realtime` | 实时流式语音识别 |
| TTS 语音合成 | `cosyvoice-v3-flash` | 流式语音合成,音色:longanhuan |
| 人脸追踪 | `qwen3-vl-plus` | 基于视觉模型的人脸位置检测 |

---

## 二、服务地址与鉴权

- **服务基础地址**:`https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run`
- **鉴权方式**:Bearer Token
- **鉴权 Token**:`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`

所有 HTTP 请求需在 Header 中携带:

```
Authorization: Bearer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```

---

## 三、HTTP 接口

### 3.1 健康检查

用于检测服务是否正常运行。

- **URL**:`GET /health`

**请求示例:**

```bash
curl -X GET "https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run/health" \
  -H "Authorization: Bearer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```

**响应示例:**

```json
{
  "status": "ok",
  "timestamp": "2026-02-22T17:22:43.276976",
  "sessions": 0
}
```

| 字段 | 类型 | 说明 |
|------|------|------|
| status | string | 服务状态,`ok` 表示正常 |
| timestamp | string | 当前服务器时间(ISO 8601) |
| sessions | number | 当前活跃会话数 |

---

### 3.2 文本对话(核心接口)

发送文本消息并获取 AI 回复,支持图像输入(多模态)。

- **URL**:`POST /process`
- **Content-Type**:`application/json`

**请求参数:**

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| input | array | 是 | 消息数组,每条消息包含 `role` 和 `content` |
| session_id | string | 否 | 会话ID,用于保持多轮对话上下文(同一 session_id 共享对话历史) |
| user_id | string | 否 | 用户ID |

**`input` 数组中的消息格式:**

```json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "用户输入的文本" }
  ]
}
```

**如需发送图像(多模态输入):**

```json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "这张图片里的人是什么表情?" },
    { "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQ..." } }
  ]
}
```

#### 纯文本对话请求示例

```bash
curl -X POST "https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run/process" \
  -H "Authorization: Bearer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "input": [
      {
        "role": "user",
        "content": [
          { "type": "text", "text": "你好!今天天气真好" }
        ]
      }
    ],
    "session_id": "my-session-001",
    "user_id": "user-001"
  }'
```

**响应示例:**

```json
{
  "output": {
    "choices": [
      {
        "message": {
          "role": "assistant",
          "content": [
            {
              "type": "text",
              "text": "是呀~阳光暖暖的,连风都带着甜味呢!你今天有出门走走吗?😊"
            }
          ]
        }
      }
    ]
  },
  "emotion": "happy",
  "emoji": "😊",
  "emotion_label": "开心"
}
```

**响应字段说明:**

| 字段 | 类型 | 说明 |
|------|------|------|
| output.choices[0].message.content[0].text | string | AI 回复的文本内容 |
| emotion | string | 情绪英文标签(如 happy, sad, neutral, surprised 等) |
| emoji | string | 对应的表情符号 |
| emotion_label | string | 情绪中文标签(如 开心、难过、平静、惊讶 等) |

---

### 3.3 服务信息

- **URL**:`GET /`

```bash
curl -X GET "https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run/" \
  -H "Authorization: Bearer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```

**响应:**

```json
{
  "name": "情绪陪伴机器人",
  "version": "1.0.0",
  "status": "running"
}
```

---

## 四、WebSocket 实时语音对话

支持实时语音输入、流式语音合成输出的全双工对话。

- **URL**:`wss://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run/ws`

### 4.1 连接建立

连接成功后服务端会发送:

```json
{
  "type": "connected",
  "sessionId": "session_xxx",
  "message": "连接成功,准备就绪"
}
```

### 4.2 客户端发送的消息类型

| type | 说明 | 附加字段 |
|------|------|----------|
| `start_listening` | 开始语音监听(启动 ASR) | 无 |
| `stop_listening` | 停止语音监听 | 无 |
| `audio_data` | 发送音频数据 | `audio`(base64 编码音频)、`hasVoice`(布尔值,是否检测到人声) |
| `text_chat` | 发送文本消息 | `text`(文本内容)、`image`(可选,base64 图像) |
| `image_frame` | 发送图像帧(用于多模态) | `image`(base64 图像) |
| `start_face_tracking` | 开始人脸追踪 | 无 |
| `stop_face_tracking` | 停止人脸追踪 | 无 |
| `detect_face` | 执行一次人脸检测 | `image`(base64 图像) |
| `interrupt` | 打断 AI 说话 | 无 |
| `reset_conversation` | 重置对话历史 | 无 |

### 4.3 服务端返回的消息类型

| type | 说明 | 关键字段 |
|------|------|----------|
| `connected` | 连接成功 | `sessionId` |
| `status` | 状态更新 | `status`(listening/thinking/speaking/idle)、`message` |
| `listening_started` | ASR 监听已开始 | 无 |
| `listening_stopped` | ASR 监听已停止 | 无 |
| `transcription` | 语音识别中间结果 | `text`、`isFinal` |
| `user_message` | 用户消息确认 | `text` |
| `assistant_chunk` | AI 回复流式片段 | `chunk`(增量文本)、`fullText`(完整文本) |
| `assistant_message` | AI 回复完整消息 | `text`、`emotion`、`emoji`、`emotionLabel` |
| `tts_start` | TTS 开始合成 | 无 |
| `tts_chunk` | TTS 音频片段 | `audio`(base64 编码的 PCM 音频) |
| `tts_end` | TTS 合成完成 | 无 |
| `interrupt_audio` | AI 语音已被打断 | 无 |
| `voice_activity` | 检测到语音活动 | `active`(布尔值) |
| `face_detection_result` | 人脸检测结果 | `success`、`coords`、`elapsed` |
| `conversation_reset` | 对话已重置 | `message` |
| `error` | 错误信息 | `source`(asr/tts/llm)、`message` |

### 4.4 WebSocket 典型交互流程

```
客户端                          服务端
  |--- 建立 WebSocket 连接 ------>|
  |<--- connected ----------------|
  |--- start_listening ---------->|
  |<--- listening_started --------|
  |--- audio_data (循环发送) ---->|
  |<--- transcription (实时) -----|
  |<--- user_message -------------|
  |<--- status: thinking ---------|
  |<--- assistant_chunk (流式) ---|
  |<--- assistant_message --------|
  |<--- tts_start ----------------|
  |<--- tts_chunk (流式音频) -----|
  |<--- tts_end ------------------|
  |<--- status: listening --------|
  |--- stop_listening ----------->|
  |<--- listening_stopped --------|
```

---

## 五、多轮对话

服务支持多轮对话上下文管理(最近 10 轮):

- **HTTP 接口**:通过 `session_id` 参数维持会话。相同 `session_id` 的请求共享对话历史。
- **WebSocket**:每个 WebSocket 连接自动维持独立会话,连接断开后会话清除。
- **重置对话**:WebSocket 发送 `{"type": "reset_conversation"}` 可清除当前会话历史。

---

## 六、调用代码示例

### Python 示例

```python
import requests
import json

BASE_URL = "https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run"
TOKEN = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HEADERS = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json"
}

# 文本对话
def chat(text, session_id="default"):
    body = {
        "input": [
            {
                "role": "user",
                "content": [{"type": "text", "text": text}]
            }
        ],
        "session_id": session_id,
        "user_id": "api-caller"
    }
    response = requests.post(f"{BASE_URL}/process", headers=HEADERS, json=body)
    result = response.json()
    
    ai_text = result["output"]["choices"][0]["message"]["content"][0]["text"]
    emotion = result["emotion"]
    emoji = result["emoji"]
    label = result["emotion_label"]
    
    print(f"AI: {ai_text}")
    print(f"情绪: {emoji} {label} ({emotion})")
    return result

# 测试
chat("你好!今天过得怎么样?")
chat("我今天有点难过", session_id="session-001")
```

### JavaScript 示例

```javascript
const BASE_URL = "https://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run";
const TOKEN = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

// HTTP 文本对话
async function chat(text, sessionId = "default") {
  const response = await fetch(`${BASE_URL}/process`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${TOKEN}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      input: [{ role: "user", content: [{ type: "text", text }] }],
      session_id: sessionId,
      user_id: "api-caller"
    })
  });
  return await response.json();
}

// WebSocket 实时对话
const ws = new WebSocket(`wss://xxxxxxxxxxxx-xxxxxxxxxx.cn-beijing.fcapp.run/ws`);

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  switch (msg.type) {
    case "connected":
      console.log("已连接:", msg.sessionId);
      break;
    case "assistant_message":
      console.log(`AI: ${msg.text} ${msg.emoji}`);
      break;
    case "tts_chunk":
      // 播放 base64 编码的 PCM 音频
      break;
  }
};
```

---

# 七、项目打包与部署

### 7.1 项目结构

```
emotion_companion_bailian/
├── main.py                  # FastAPI 应用主入口(必须为 main.py)
├── services/                # AI 服务模块
│   ├── __init__.py
│   ├── asr.py               # ASR 语音识别服务(qwen3-asr-flash-realtime)
│   ├── llm.py               # LLM 多模态对话服务(qwen3-vl-plus)
│   ├── tts.py               # TTS 语音合成服务(cosyvoice-v3-flash)
│   └── face_tracking.py     # 人脸追踪服务(qwen3-vl-plus)
├── setup.py                 # 包构建配置
├── pyproject.toml           # 构建系统配置
├── requirements.txt         # Python 依赖列表
└── deploy.ps1               # 自动化部署脚本(PowerShell)
```

### 7.2 环境准备

```bash
# 要求 Python >= 3.10
python --version

# 安装部署工具
pip install "agentscope-runtime==1.0.0"

# 安装阿里云 SDK 依赖(部署上传需要)
pip install alibabacloud-oss-v2 alibabacloud-bailian20231229 \
  alibabacloud-credentials alibabacloud-tea-openapi alibabacloud-tea-util rich
```

### 7.3 配置环境变量

```powershell
# PowerShell(Windows)
$env:ALIBABA_CLOUD_ACCESS_KEY_ID="<你的阿里云 AccessKey ID>"
$env:ALIBABA_CLOUD_ACCESS_KEY_SECRET="<你的阿里云 AccessKey Secret>"
$env:DASHSCOPE_API_KEY="<你的百炼 API Key>"
```

```bash
# Linux / macOS
export ALIBABA_CLOUD_ACCESS_KEY_ID="<你的阿里云 AccessKey ID>"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="<你的阿里云 AccessKey Secret>"
export DASHSCOPE_API_KEY="<你的百炼 API Key>"
```

### 7.4 打包与部署(首次)

使用 `runtime-fc-deploy` 的 **wrapper 模式**将项目打包为包含 `deploy_starter` 入口的 whl 并部署:

```bash
# 进入项目目录
cd emotion_companion_bailian

# 方式一:一步完成构建 + 部署(新建应用)
runtime-fc-deploy --mode wrapper \
  --dir "." \
  --cmd "python3 main.py" \
  --deploy-name "情绪陪伴机器人" \
  --telemetry enable
```

部署成功后会输出:
```
Console URL: https://bailian.console.aliyun.com/?tab=app#/app-center
Deploy ID: <应用部署ID>
```

### 7.5 更新部署

如果需要更新已部署的应用代码,分两步操作:

```bash
# 第一步:构建 wrapper whl(仅构建,不上传)
runtime-fc-deploy --mode wrapper \
  --dir "." \
  --cmd "python3 main.py" \
  --skip-upload \
  --telemetry enable \
  --deploy-name "情绪陪伴机器人"

# 构建产物位于:.agentscope_runtime_builds/build-xxx/dist/ 目录下

# 第二步:上传 wrapper whl 到已有应用
runtime-fc-deploy --update <应用部署ID> \
  --whl-path "<wrapper whl 文件路径>" \
  --telemetry enable
```

### 7.6 部署后控制台配置

部署完成后,需要在[阿里云百炼控制台](https://bailian.console.aliyun.com/?tab=app#/app-center)完成以下配置:

1. **等待构建部署完成**:进入应用详情页,等待应用函数状态从"未部署"变为"已部署"
2. **配置环境变量**:在"环境变量"区域添加 `DASHSCOPE_API_KEY = <你的百炼 API Key>`
3. **配置路由鉴权**:在"路由 > 鉴权Token"中选择对应的 API Key

### 7.7 关键注意事项

| 事项 | 说明 |
|------|------|
| **必须使用 wrapper 模式** | 直接使用 `--whl-path` 上传用户自建的 whl 会缺少 `deploy_starter/main.py` 入口文件,导致 FC 函数启动失败 |
| **启动命令使用 python3 main.py** | FC 环境中 `uvicorn` CLI 不在 PATH 中,需通过 `python3 main.py` 启动(main.py 内部调用 `uvicorn.run()`) |
| **默认端口必须为 8080** | FC 健康检查固定检测 8080 端口,`main.py` 中 `PORT` 环境变量默认值需设为 8080 |
| **排除虚拟环境目录** | wrapper 模式仅忽略 `.venv`(带点号),如使用 `venv` 目录需重命名为 `.venv`,否则会被打包导致 whl 过大 |
| **环境变量必须配置** | FC 函数运行时必须配置 `DASHSCOPE_API_KEY`,否则所有 AI 模型调用(LLM/ASR/TTS)将失败 |

---

## 八、注意事项

1. **鉴权必填**:所有请求必须携带 `Authorization: Bearer <token>` 头,否则将被拒绝。
2. **会话管理**:HTTP 接口需通过 `session_id` 维持多轮对话上下文;不传则每次创建新会话。
3. **超时设置**:建议 HTTP 请求超时设置为 60 秒,因为首次调用可能触发冷启动。
4. **WebSocket 音频格式**:发送的音频数据为 base64 编码,TTS 返回的音频片段同样为 base64 编码的 PCM 数据。
5. **情绪识别**:每次对话回复都会附带情绪分析结果(emotion/emoji/emotion_label),可用于驱动前端表情动画等场景。

完成部署后,尝试生成一个前端页面建立websocket调用服务测试可用性。

给Qoder描述需求如下:

请基于 ​情绪陪伴机器人服务调用方法说明2.0.md​ 生成一个前端页面,用来测试**实时语音对话**和**人脸追踪**的功能;
前端代码实现和页面效果,可以参考 ​20260206_桌面情感陪伴demo​ 文件夹,这个文件夹已经实现相关功能,只不过服务是单独调用,不是打包上传到阿里云百炼高代码应用中的。
目前部署在百炼FC平台的服务是在 ​emotion_companion_bailian​ 进行打包和部署的,
请参考官方文档 https://help.aliyun.com/zh/functioncompute/fc-2-0/user-guide/configure-an-http-trigger-for-a-function-that-is-triggered-by-websocket-requests
检查目前配置是否符合文档规范。

生成前端测试页面如下。

根本原因:

FC 网关对 WebSocket 升级请求返回了 HTTP 400(不是 1006,1006 是浏览器对握手失败的包装)。

根据 FC 官方文档的错误表:

FcCommonError 400 — 调用需要身份认证的函数的 Request 没有传入 Date 信息或 Authorization 信息

结论:FC HTTP 触发器配置了”签名认证”,WebSocket 升级请求缺少 FC 级别的 HMAC 签名(Date + Authorization),所以被网关直接拒绝。

解决方案:

需要在 百炼/FC 控制台 将 HTTP 触发器的认证方式改为”无需认证“:

  1. 登录 阿里云百炼控制台
  2. 找到”情绪陪伴机器人”应用 → 进入详情
  3. 找到关联的 FC 函数 → HTTP 触发器
  4. 将认证方式从”签名认证”改为”无需认证
  5. 保存

安全说明:改为”无需认证”后,FC 网关不再拦截请求,但你的应用代码仍然有 Authorization: Bearer token 的应用级别鉴权。

修改完成后,前端页面测试服务可用。

3.3.3 硬件集成:多模态交互服务接口集成到BK7258

我们还是尝试跟Qoder配合完成这部分工作。

首先把初始需求给到Qoder如下。

背景:
我们前面基于​1.3-目前需求描述.md​ 完成了阿里云多模态交互开发套件的SDK替换beken_genie项目中的ASR+LLM+TTS,经过编译和测试,已经在博通BK7258上实现了语音唤醒和语音对话。
我所有的改动和编译都是在 ​bk_aidk-ai_release-v2.0.1.9​ 这个文件夹下进行的;


另外,在
C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9-自建ASR+LLM+TTS\toQoder\20260206_桌面情感陪伴demo\文件夹下,部署了一个情绪陪伴机器人,它是一个部署在阿里云百炼高代码应用平台上的 AI 后端服务,提供**文本对话**和**实时语音对话**两种交互方式,具备情绪识别、语音识别(ASR)、语音合成(TTS)和人脸追踪能力,具体的调用方式,可以参考。:
C:\Users\LeoYu\armino\bk_aidk-ai_release-v2.0.1.9-自建ASR+LLM+TTS\toQoder\emotion_companion_bailian\情绪陪伴机器人服务调用方法说明.md

需求:
1. 用“20260206_桌面情感陪伴demo”文件夹下实现的情绪陪伴机器人,替换目前的语音对话功能,并集成情绪陪伴机器人具有的情绪识别功能,在设备COM口输出对应情绪和表情;
2. 用“20260206_桌面情感陪伴demo”文件夹下实现的情绪陪伴机器人,调用博通BK7258上的摄像头,实现人脸追踪服务;


本次新需求的整体结构,以及后续的改动,请务必参考博通BK7258中beken_genie的项目工程文档,链接如下 https://docs.bekencorp.com/arminodoc/bk_aidk/bk7258/zh_CN/v2.0.1/projects/beken_genie/index.html

需要经过多轮调优,软件编译和硬件烧录,最终可以实现:

  • 进行实时对话;
  • 输出人脸坐标(如下图);
  • 输出表情(如下图);

生成该项目spec需求文档如下。

# 情绪陪伴机器人 - BK7258 端侧实现需求文档 v7.1

## 一、项目概述

将阿里云百炼高代码应用平台的情绪陪伴机器人服务集成到博通 BK7258 AI 开发板,实现全双工流水线语音交互、情绪识别和人脸追踪功能。v7.1 版本在 v7.0 基础上重点优化了 TTS 播放末尾杂音问题,引入三重防护机制彻底消除音频结尾噪声。

---

## 二、系统架构

### 2.1 整体架构

```
┌─────────────────────────────────────────────────────────────────┐
│                        BK7258 开发板 (端侧)                        │
├─────────────────────────────────────────────────────────────────┤
│  CPU0 (应用核心)                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐  │
│  │ WebSocket   │  │ Audio Engine│  │ Face Tracker            │  │
│  │ Client      │  │ (300KB RB)  │  │ (DVP Camera)            │  │
│  └──────┬──────┘  └──────┬──────┘  └───────────┬─────────────┘  │
│         │                │                     │                 │
│         └────────────────┼─────────────────────┘                 │
│                          │                                       │
│  ┌───────────────────────┴───────────────────────┐               │
│  │           Emotion Companion Main              │               │
│  │  (事件分发、自动重连、网络事件处理)              │               │
│  └───────────────────────┬───────────────────────┘               │
├──────────────────────────┼──────────────────────────────────────┤
│  CPU1 (媒体核心)          │                                       │
│  ┌───────────────────────┴───────────────────────┐               │
│  │  Audio Mailbox + Video Engine                  │               │
│  │  (音频 DMA、摄像头驱动、硬件编解码)             │               │
│  └───────────────────────────────────────────────┘               │
├─────────────────────────────────────────────────────────────────┤
│  CPU2 (ASR 核心)                                                 │
│  │  (本次未使用,云端 ASR)                                        │
└─────────────────────────────────────────────────────────────────┘
                          │
                          │ WebSocket (wss://)
                          ▼
┌─────────────────────────────────────────────────────────────────┐
│              阿里云百炼高代码应用 (云端 v6.0)                       │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────────────────┐ │
│  │ ASR     │  │ LLM     │  │ TTS     │  │ Face Detection      │ │
│  │ qwen3   │  │ qwen3.5 │  │ cosyvoice│  │ qwen3.5-plus        │ │
│  │ -asr    │  │ -plus   │  │ -v3     │  │                     │ │
│  └─────────┘  └─────────┘  └─────────┘  └─────────────────────┘ │
│                          │                                       │
│  ┌───────────────────────┴───────────────────────┐               │
│  │  Mem0 + Milvus 长期记忆系统                    │               │
│  └───────────────────────────────────────────────┘               │
└─────────────────────────────────────────────────────────────────┘
```

### 2.2 技术选型

| 功能模块 | 端侧实现 | 云端服务 |
|---------|----------|----------|
| 通信协议 | WebSocket (mbedTLS + LWIP) | FastAPI WebSocket |
| 音频采集 | 16kHz PCM, 60ms 帧 | qwen3-asr-flash-realtime |
| 音频播放 | 16kHz PCM, 20ms 定时器 | cosyvoice-v3-flash |
| 人脸追踪 | DVP 摄像头 + JPEG 编码 | qwen3.5-plus VLM |
| 情绪识别 | JSON 解析 | LLM 输出 EMOTION 标签 |
| 认证方式 | Bearer Token | 阿里云百炼 |

---

## 三、项目结构

### 3.1 端侧代码结构

```
projects/common_components/emotion_companion/
├── CMakeLists.txt              # 编译配置
├── Kconfig                     # 配置菜单
├── include/
│   ├── emotion_companion.h     # 主模块 API
│   ├── emotion_audio_engine.h  # 音频引擎 API
│   └── emotion_ws_client.h     # WebSocket 客户端 API
└── src/
    ├── emotion_companion.c     # 主控制逻辑
    ├── emotion_audio_engine.c  # 音频引擎实现 (v7.1 重点优化)
    ├── emotion_ws_client.c     # WebSocket 客户端实现
    ├── emotion_face_tracker.c  # 人脸追踪模块
    ├── emotion_serial_output.c # 串口输出模块
    └── base64_util.c           # Base64 编码工具
```

### 3.2 配置文件

```
projects/beken_genie/config/bk7258/config       # CPU0 配置
projects/beken_genie/config/bk7258_cp1/config   # CPU1 配置
```

---

## 四、核心功能模块

### 4.1 WebSocket 客户端 (emotion_ws_client.c)

**功能描述**:
- 建立与云端服务的 WebSocket 连接
- 发送麦克风音频数据(base64 编码)
- 接收 TTS 音频数据和解码后的 PCM
- 处理各类文本消息(ASR 结果、AI 回复、情绪、人脸坐标等)

**消息类型**:

| 类型 | 方向 | 说明 |
|------|------|------|
| connected | 服务端→端侧 | 连接成功 |
| start_listening | 端侧→服务端 | 开始语音监听 |
| stop_listening | 端侧→服务端 | 停止语音监听 |
| audio_data | 端侧→服务端 | 发送音频数据 |
| transcription | 服务端→端侧 | ASR 转录结果 |
| assistant_message | 服务端→端侧 | AI 完整回复 |
| tts_start | 服务端→端侧 | TTS 播放开始 |
| tts_chunk | 服务端→端侧 | TTS 音频块 |
| tts_end | 服务端→端侧 | TTS 播放结束 |
| detect_face | 端侧→服务端 | 发送人脸检测请求 |
| face_detection_result | 服务端→端侧 | 人脸检测结果 |
| interrupt | 端侧→服务端 | 打断 AI 说话 |

**关键参数**:
- SSL 出站缓冲区:16384 字节(解决大数据发送失败)
- 音频编码:PCM(非 OPUS)

### 4.2 音频引擎 (emotion_audio_engine.c) [v7.1 重点优化]

**功能描述**:
- 管理 BK7258 音频硬件(麦克风 + 扬声器)
- 实现平滑的 TTS 音频播放
- 提供流控机制防止缓冲区溢出
- **[v7.1 新增] 三重防护消除 TTS 结尾杂音**

**核心设计**:

```
云端 TTS → WebSocket → 解码 PCM → 环形缓冲区 (300KB) → 播放定时器 (20ms) → DAC
                                          ↑
                                    流控检查 (80%)
```

**关键参数**:

| 参数 | 值 | 说明 |
|------|-----|------|
| 采样率 | 16000 Hz | 16kHz,匹配云端 |
| 帧大小 | 960 字节 | 60ms @ 16kHz/16bit/mono |
| 环形缓冲区 | 300 KB | 约 9 秒 PCM 数据 |
| 播放定时器 | 20 ms | 平滑播放间隔 |
| 流控阈值 | 80% | 缓冲区 >= 80% 时等待 |
| 流控超时 | 200 ms | 等待空间超时则丢弃 |
| **渐进淡出区间** | **16000 字节 (500ms)** | **[v7.1] 线性淡出区间** |
| **静音填充时长** | **1000 ms (50 cycles)** | **[v7.1] 结尾静音填充** |

**流控机制**:
```c
// 当缓冲区 >= 80% 满时,等待播放消费
if (emo_audio_rb_is_nearly_full()) {
    if (!emo_audio_wait_for_space(pcm_len, 200)) {
        // 超时则丢弃当前块,避免溢出
        return;
    }
}
```

#### 4.2.1 [v7.1 新增] TTS 结尾三重防护机制

**问题背景**:

VOICE 模式下麦克风和扬声器持续运行,PA 不会关闭。即使 PCM 数据全为零值,DAC/编解码器/功放硬件层面仍会输出 thermal noise(底噪),导致每段 TTS 结束时出现可感知的杂音。

**三重防护设计**:

```
TTS 结束
  │
  ▼ [第一重] 渐进式线性淡出 (500ms / 16000 bytes)
  │   逐采样按剩余字节数比例衰减:volume = bytes_remaining / 16000
  │
  ▼ [第二重] 零值静音填充 (1000ms / 50 cycles)
  │   持续向扬声器解码器写入全零 PCM 数据
  │   + 最后一块数据写入前零值钳位最后 4 采样
  │
  ▼ [第三重] 硬件增益静音
      静音填充完成后 bk_aud_intf_set_spk_gain(0)
      下次 TTS 开始时自动恢复增益
```

**核心常量**:
```c
#define TTS_END_SILENCE_CYCLES   50      // 50 cycles * 20ms = 1000ms 静音
#define TTS_FADE_ZONE_BYTES      (16000) // 500ms 线性淡出区间
                                         // (16000 bytes = 8000 samples @ 16kHz 16-bit mono)
```

**第一重:渐进式线性淡出**

在环形缓冲区最后 500ms(16000 字节)的音频数据上应用逐采样线性音量衰减。每个 PCM 采样的音量按其之后剩余数据量与淡出区间的比值线性缩放,从 100% 平滑衰减到 0%。

```c
static void emo_apply_progressive_fade(int16_t *samples, int num_samples,
                                        uint32_t bytes_remaining_after_chunk)
{
    if (bytes_remaining_after_chunk >= TTS_FADE_ZONE_BYTES) {
        return;  // 不在淡出区间,跳过
    }
    for (int i = 0; i < num_samples; i++) {
        uint32_t bytes_after = bytes_remaining_after_chunk +
                               (uint32_t)((num_samples - 1 - i) * 2);
        if (bytes_after >= TTS_FADE_ZONE_BYTES) continue;
        // volume = bytes_after / TTS_FADE_ZONE_BYTES  (线性 1.0 → 0.0)
        int32_t s = (int32_t)samples[i];
        samples[i] = (int16_t)(s * (int32_t)bytes_after / (int32_t)TTS_FADE_ZONE_BYTES);
    }
}
```

**第二重:零值静音填充 + 零值钳位**

环形缓冲区排空后,播放定时器持续向扬声器解码器 Ring Buffer 写入全零 PCM 数据,持续 50 个周期(1000ms)。同时在最后一块有效数据写入扬声器前,将末尾 4 个采样(8 字节)强制清零作为安全网。

```c
if (rb_remaining == 0) {
    // 零值钳位:写入前将最后 4 个采样清零
    if (read_len >= 8) {
        os_memset(g_play_buf + read_len - 8, 0, 8);
    }
}
bk_aud_intf_write_spk_data(g_play_buf, read_len);
if (rb_remaining == 0) {
    // 立即填充静音,防止解码器欠载
    spk_free = bk_aud_intf_get_dec_rb_free_size();
    if (spk_free > 0) {
        uint32_t sil_len = (spk_free < EMO_PLAY_BUF_SIZE) ? spk_free : EMO_PLAY_BUF_SIZE;
        os_memset(g_play_buf, 0, sil_len);
        bk_aud_intf_write_spk_data(g_play_buf, sil_len);
    }
}
```

**第三重:硬件增益静音**

静音填充周期全部完成后,调用 `bk_aud_intf_set_spk_gain(0)` 将扬声器增益设为 0,从硬件层面彻底消除 DAC/编解码器/功放的 thermal noise。下次 TTS 开始时自动恢复到用户设定的音量。

```c
// 静音完成后硬件静音
bk_aud_intf_set_spk_gain(0);
g_spk_muted_for_tts_end = true;

// 新 TTS 开始时恢复增益
bk_err_t emo_audio_notify_tts_start(void)
{
    g_tts_end_pending = false;
    g_silence_cycles_remaining = 0;
    if (g_spk_muted_for_tts_end) {
        uint8_t level = g_current_volume * (SPK_VOLUME_LEVEL - 1) / 100;
        bk_aud_intf_set_spk_gain(g_volume_gain[level]);
        g_spk_muted_for_tts_end = false;
    }
    return BK_OK;
}
```

**音量设置保护**:

在硬件增益静音期间,`emo_audio_set_volume()` 不会恢复扬声器增益,避免静音状态被意外解除:

```c
if (g_audio_engine_running && !g_spk_muted_for_tts_end) {
    bk_aud_intf_set_spk_gain(g_volume_gain[level]);
}
```

### 4.3 人脸追踪模块 (emotion_face_tracker.c)

**功能描述**:
- 通过 DVP 摄像头捕获 JPEG 帧
- 定时发送到云端进行人脸检测
- 输出人脸坐标到 COM 口

**数据流**:

```
DVP 摄像头 (CPU1) → video_engine → 帧回调 (CPU0) → 缓存最新 JPEG
                                                        ↓ (定时器 2s)
                                          emo_companion_detect_face()
                                                        ↓
                                        base64 编码 → WebSocket → 云端
                                                        ↓
                                        人脸检测结果 → COM 口输出
```

**关键参数**:

| 参数 | 值 | 说明 |
|------|-----|------|
| 摄像头分辨率 | 640x480 | 平衡画质和传输量 |
| JPEG 大小 | 20-60 KB | 典型帧大小 |
| 检测间隔 | 2000 ms | 可配置 (500-10000ms) |
| 帧缓冲区 | 100 KB | PSRAM 分配 |
| 坐标范围 | 0-1000 | 归一化坐标值 |

### 4.4 主控制模块 (emotion_companion.c)

**功能描述**:
- 协调各子模块的生命周期
- 处理网络事件(WiFi 连接/断开)
- 实现自动重连机制
- 分发事件到应用层回调

**自动重连机制**:

| 参数 | 值 |
|------|-----|
| 重连间隔 | 5000 ms |
| 最大重试次数 | 10 次 |
| 触发条件 | WebSocket 断开、网络错误 |

**网络事件处理**:
- WiFi 连接成功后自动启动服务
- 等待 2 秒确保 CPU1 媒体子系统就绪
- 音频引擎启动失败时自动重试(最多 3 次)

### 4.5 串口输出模块 (emotion_serial_output.c)

**功能描述**:
- 格式化输出各类事件到 COM 口
- 便于调试和监控

**输出格式**:

```
[EMOTION] emoji=😊, label=开心, emotion=happy
[FACE] detected=1, x=512, y=384, elapsed=450ms
[ASR] text=今天天气怎么样
[ASSISTANT] text=今天天气不错呢,阳光明媚
[TTS] chunk=3200 bytes, rb_fill=150000/307200 (48%)
[STATUS] connected
[WS_EVENT] connected: server session established
```

---

## 五、Kconfig 配置项

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| EMOTION_COMPANION | bool | n | 启用情绪陪伴功能 |
| EMOTION_COMPANION_DEBUG | bool | n | 启用调试日志 |
| EMOTION_SERVER_URL | string | wss://\<your-server\> | WebSocket 服务器地址 |
| EMOTION_HTTP_URL | string | https://\<your-server\> | HTTP 服务器地址 |
| EMOTION_AUTH_TOKEN | string | \<your-token\> | Bearer 认证令牌 |
| EMOTION_FACE_TRACKING | bool | n | 启用人脸追踪 |
| EMOTION_FACE_TRACKING_INTERVAL_MS | int | 2000 | 人脸检测间隔 |
| EMOTION_VOLUME_LEVEL | int | 60 | 默认音量 (30-100) |
| EMOTION_SERIAL_OUTPUT | bool | y | 启用串口输出 |
| EMOTION_SERIAL_UART_ID | int | 0 | UART 端口 ID |

---

## 六、API 接口

### 6.1 主模块 API (emotion_companion.h)

```c
// 初始化服务
int emo_companion_init(const emo_companion_config_t *config);

// 启动/停止服务
int emo_companion_start(void);
int emo_companion_stop(void);
int emo_companion_deinit(void);

// 语音监听控制
int emo_companion_start_listening(void);
int emo_companion_stop_listening(void);

// 状态查询
bool emo_companion_is_connected(void);

// 音量控制
int emo_companion_set_volume(uint8_t volume);
uint8_t emo_companion_get_volume(void);

// 人脸检测
int emo_companion_detect_face(const uint8_t *jpeg_data, uint32_t jpeg_len);

// 获取最近情绪
int emo_companion_get_last_emotion(emo_emotion_data_t *data);
```

### 6.2 音频引擎 API (emotion_audio_engine.h) [v7.1 新增接口说明]

```c
// TTS 生命周期通知
bk_err_t emo_audio_notify_tts_start(void);  // 新 TTS 开始,恢复增益
bk_err_t emo_audio_notify_tts_end(void);    // TTS 结束,启动三重防护

// 音频数据写入(含流控)
bk_err_t emo_audio_write_pcm(const uint8_t *data, uint32_t len);

// 音量控制(含硬件静音保护)
bk_err_t emo_audio_set_volume(uint8_t volume);
uint8_t emo_audio_get_volume(void);

// 缓冲区状态查询
bool emo_audio_rb_is_nearly_full(void);
bool emo_audio_wait_for_space(uint32_t required_space, uint32_t timeout_ms);
uint32_t emo_audio_rb_get_fill_size_ext(void);
uint32_t emo_audio_rb_get_total_size_ext(void);
```

### 6.3 事件类型

```c
typedef enum {
    EMO_EVENT_CONNECTED,         // WebSocket 已连接
    EMO_EVENT_DISCONNECTED,      // WebSocket 已断开
    EMO_EVENT_ERROR,             // 发生错误
    EMO_EVENT_LISTENING_STARTED, // ASR 监听已开始
    EMO_EVENT_LISTENING_STOPPED, // ASR 监听已停止
    EMO_EVENT_ASR_RESULT,        // ASR 转录结果
    EMO_EVENT_ASSISTANT_MESSAGE, // AI 回复消息
    EMO_EVENT_EMOTION_RESULT,    // 情绪识别结果
    EMO_EVENT_TTS_START,         // TTS 播放开始
    EMO_EVENT_TTS_END,           // TTS 播放结束
    EMO_EVENT_FACE_DETECTED,     // 人脸检测结果
    EMO_EVENT_STATUS_CHANGE,     // 状态变化
} emo_event_t;
```

### 6.4 数据结构

```c
// 情绪数据
typedef struct {
    char emotion[32];       // 英文标签: "happy", "sad", etc.
    char emoji[16];         // Emoji 字符
    char emotion_label[32]; // 中文标签: "开心", "难过", etc.
} emo_emotion_data_t;

// 人脸坐标
typedef struct {
    bool detected;      // 是否检测到人脸
    int x;              // X 坐标 (0-1000)
    int y;              // Y 坐标 (0-1000)
    int elapsed_ms;     // 检测耗时 (ms)
} emo_face_coords_t;
```

---

## 七、编译和部署

### 7.1 编译命令

```powershell
# 进入项目目录
cd "<your-project-path>"

# 编译固件
.\dbuild.ps1 make bk7258 PROJECT=beken_genie

# 清除后重新编译(如遇问题)
.\dbuild.ps1 make bk7258 PROJECT=beken_genie distclean
.\dbuild.ps1 make bk7258 PROJECT=beken_genie
```

### 7.2 固件输出

```
build\beken_genie\bk7258\all-app.bin
```

### 7.3 烧录方式

使用 BK7258 烧录工具将 `all-app.bin` 烧录到开发板。

---

## 八、调试经验总结

### 8.1 音频相关问题

#### 8.1.1 音频播放卡顿/断续

**问题现象**:
- 音频播放断断续续,只能听到短片段
- 日志显示 `player_rb_size [32768] = 32KB`

**根因分析**:
```
云端 TTS 音频推送速率: ~120KB/s
硬件播放消费速率: ~32KB/s (16kHz/16bit/mono)
32KB 缓冲区在 0.35 秒内被填满,之后大量音频数据溢出丢失
```

**解决方案**:
- 环形缓冲区从 32KB 增大到 300KB(约 9 秒 PCM 数据)
- 匹配 ali_wss 参考实现的 `c_mmi_set_audio_mode_ringbuffer(500*1024, 900*1024)`

#### 8.1.2 播放杂音(SSL 缓冲区不足)

**问题现象**:
- 音频播放有杂音
- 日志显示 `0x6880` 错误码(SSL 发送失败)

**根因分析**:
- MBEDTLS SSL 出站缓冲区默认 4096 字节
- 大数据分片发送时缓冲区不足导致失败

**解决方案**:
```kconfig
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=16384
```

#### 8.1.3 每段结尾杂音 [v7.1 重点优化]

**问题现象**:
- 每个 TTS 段落结束时出现杂音/噪声
- VOICE 模式下 PA 持续运行,即使 PCM 数据全为零,DAC 仍输出底噪

**根因分析**:

| 修复版本 | 方案 | 结果 |
|---------|------|------|
| v7.0 | 300ms 静音填充 + 30ms 淡出 | 仍有杂音 |
| v7.0.1 | 200ms 渐进式淡出 + 500ms 静音 | 仍有杂音 |
| **v7.1** | **三重防护(见下文)** | **彻底解决** |

核心问题:软件级别的 PCM 零值静音无法消除 DAC/编解码器/功放的 thermal noise(底噪),必须从硬件增益层面静音。

**v7.1 解决方案 - 三重防护**:

```
TTS 结束信号 (tts_end)
  │
  ▼ 设置 g_tts_end_pending=true, g_silence_cycles_remaining=50
  │
  ▼ 播放定时器继续消费环形缓冲区
  │
  ▼ [第一重] 渐进式线性淡出
  │   在最后 16000 字节 (500ms) 上逐采样线性衰减
  │   volume = bytes_remaining / 16000 (从 100% → 0%)
  │
  ▼ [第二重] 零值静音填充 + 零值钳位
  │   最后一块数据写入前,末尾 4 采样 (8字节) 强制清零
  │   缓冲区排空后持续写入零值 PCM,共 50 个 20ms 周期 (1000ms)
  │   每周期尽可能填满扬声器解码器缓冲区
  │
  ▼ [第三重] 硬件增益静音
      bk_aud_intf_set_spk_gain(0) - 从硬件层面切断底噪
      设置 g_spk_muted_for_tts_end=true
      emo_audio_set_volume() 在此期间不会恢复增益
      下次 emo_audio_notify_tts_start() 时自动恢复用户音量
```

**关键参数对比**:

| 参数 | v7.0 | v7.1 | 说明 |
|------|------|------|------|
| 淡出方式 | 30ms 简单淡出 | 500ms 线性渐进淡出 | 跨越多个播放周期 |
| 淡出区间 | 960 bytes | 16000 bytes | 8000 采样逐个衰减 |
| 静音时长 | 300ms (15 cycles) | 1000ms (50 cycles) | 确保 DAC 完全静默 |
| 硬件静音 | 无 | `set_spk_gain(0)` | 消除 thermal noise |
| 零值钳位 | 无 | 末尾 4 采样清零 | 防止淡出残余 |
| 音量保护 | 无 | 静音期间锁定增益 | 防止意外恢复 |

#### 8.1.4 Ring Buffer 溢出

**问题现象**:
```
emo_aud:W(232006):Audio RB overflow: need 3200, free 1280, fill 305919
```
- 播放几十秒后全部变成杂音
- 缓冲区填充到 99%(fill=307199/307200)

**根因分析**:
```
TTS 数据到达速度 > 播放消费速度
20ms 播放定时器被其他任务(SSL 分片、摄像头)阻塞
导致消费跟不上生产,最终溢出
```

**解决方案**:流控机制
```c
// emotion_audio_engine.c
bool emo_audio_rb_is_nearly_full(void) {
    uint32_t fill = audio_rb_get_fill_size();
    return (fill * 100 / g_audio_rb_size) >= 80;  // >= 80% 满
}

bool emo_audio_wait_for_space(uint32_t required_space, uint32_t timeout_ms) {
    uint32_t elapsed = 0;
    while (audio_rb_get_free_size() < required_space) {
        if (elapsed >= timeout_ms) return false;
        rtos_delay_milliseconds(10);
        elapsed += 10;
    }
    return true;
}

// emotion_companion.c - TTS 处理中集成流控
if (emo_audio_rb_is_nearly_full()) {
    if (!emo_audio_wait_for_space(pcm_len, 200)) {
        return;  // 超时则丢弃当前块
    }
}
```

#### 8.1.5 音频编解码错误

**问题现象**:
- 音频无法正常播放
- 编解码类型不匹配

**解决方案**:
```kconfig
# projects/beken_genie/config/bk7258/config
CONFIG_AUDIO_ENCODER_TYPE="PCM"
CONFIG_AUDIO_DECODER_TYPE="PCM"
CONFIG_AUDIO_ADC_SAMP_RATE=16000
CONFIG_AUDIO_DAC_SAMP_RATE=16000
```

### 8.2 编译相关问题

#### 8.2.1 `configTICK_RATE_HZ` 未声明

**问题现象**:
```
error: 'configTICK_RATE_HZ' undeclared (first use in this function)
```

**根因分析**:
- 原代码使用 FreeRTOS 的 `configTICK_RATE_HZ` 宏计算时间
- 该宏在当前 SDK 环境中未暴露给应用层

**解决方案**:使用简单计数器替代 tick 计算
```c
uint32_t elapsed = 0;
while (audio_rb_get_free_size() < required_space) {
    if (elapsed >= timeout_ms) return false;
    rtos_delay_milliseconds(10);
    elapsed += 10;  // 累加计数
}
```

### 8.3 Cache 一致性问题

#### 8.3.1 CPU0/CPU1 数据不同步

**问题现象**:
- 音频引擎初始化失败
- CPU1 未正确响应 CPU0 的 mailbox 消息

**根因分析**:
- BK7258 三核架构中 CPU0 和 CPU1 各有独立的 D-Cache
- DMA 读取内存时可能读到 Cache 中的旧数据
- Mailbox 消息传递需要 Cache 同步

**解决方案**:
```c
// CPU0 发送 mailbox 前刷新 Cache
bk_dcache_flush(addr, size);

// CPU1 接收后无效化 Cache
bk_dcache_invalidate(addr, size);
```

**关键配置**:
```kconfig
CONFIG_DCACHE=y
CONFIG_CACHE_ENABLE=y
```

### 8.4 摄像头相关问题

| 问题 | 根因 | 解决方案 |
|------|------|----------|
| 人脸追踪不工作 | 缺少摄像头启动管道 | 新增 emotion_face_tracker.c |
| 帧捕获失败 | video_engine 未初始化 | 调用 video_turn_on() 启动 DVP |

### 8.5 网络相关问题

| 问题 | 根因 | 解决方案 |
|------|------|----------|
| WebSocket 断开后无响应 | 无重连机制 | 新增自动重连(最多 10 次,5s 间隔) |
| 音频引擎启动失败 | CPU1 未就绪 | 启动前等待 500ms,失败重试 3 次 |

### 8.6 内存相关问题

| 问题 | 根因 | 解决方案 |
|------|------|----------|
| 大帧内存不足 | SRAM 有限 | 使用 psram_malloc 分配帧缓冲区 |
| 音频缓冲区分配失败 | SRAM 碎片化 | 使用 PSRAM 分配 300KB 环形缓冲区 |

### 8.7 问题排查流程

```
1. 检查 COM 口日志
   ↓
2. 分析错误码和警告信息
   ↓
3. 检查配置文件 (config)
   ↓
4. 检查缓冲区大小和流控机制
   ↓
5. 检查 Cache 一致性
   ↓
6. 检查任务优先级和时序
```

---

## 九、云端服务配置

### 9.1 服务地址

| 配置项 | 值 |
|--------|-----|
| WebSocket URL | wss://\<your-server-domain\> |
| HTTP URL | https://\<your-server-domain\> |
| 认证 Token | \<your-auth-token\> |

### 9.2 云端功能

云端服务基于 v6.0 版本,提供以下能力:
- 全双工流水线语音交互(首字延迟 1.2-1.7s)
- 智能判停与拒识(规则引擎 + LLM 语义判断)
- 长期记忆系统(Mem0 + Milvus)
- 人脸检测(qwen3.5-plus VLM)

---

## 十、版本记录

| 版本 | 日期 | 更新内容 |
|------|------|----------|
| v7.0.0 | 2026-03-12 | BK7258 端侧实现:WebSocket 客户端 + 音频引擎 + 人脸追踪 + 情绪识别;300KB 环形缓冲区 + 20ms 播放定时器 + 流控机制;自动重连 + 网络事件处理;完备的 COM 口日志输出 |
| v7.1.0 | 2026-03-29 | TTS 结尾杂音三重防护:500ms 渐进式线性淡出(16000 bytes 逐采样衰减)+ 1000ms 零值静音填充(50 cycles)+ 硬件增益静音(`set_spk_gain(0)` 消除 DAC 底噪);新增音量设置保护(静音期间锁定增益);新增 TTS 生命周期管理(`notify_tts_start` 自动恢复增益) |

---

## 十一、附录

### 11.1 参考文档

- [BK7258 Beken Genie 项目文档](https://docs.bekencorp.com/arminodoc/bk_aidk/bk7258/zh_CN/v2.0.1/projects/beken_genie/index.html)

### 11.2 相关文件路径

```
端侧代码: projects/common_components/emotion_companion/
配置文件: projects/beken_genie/config/bk7258/config
编译输出: build/beken_genie/bk7258/all-app.bin
```

### 11.3 情绪类型定义

| 表情名 | Emoji | 中文标签 | 含义 |
|--------|-------|----------|------|
| happy | 😊 | 开心 | 开心、兴奋、高兴 |
| sad | 😢 | 难过 | 难过、同情、安慰 |
| love | 🥰 | 关爱 | 关爱、喜欢、温暖 |
| thinking | 🤔 | 思考 | 思考、疑惑、好奇 |
| surprised | 😮 | 惊讶 | 惊讶、意外 |
| neutral | 😌 | 平静 | 平静、中性 |

3.4 记忆系统

记忆系统的构建目前主流有几个思路:

  • 使用部分云厂商相关组件原生自带的长期记忆功能;
  • 通过RAG的方式,把记忆存储在知识库中,按需调用;
  • 把记忆持久化存储于向量数据库中。

这里尝试:

  1. 在多模态交互开发套件中,使用原生长期记忆功能;
  2. 在自建链路中,构建基于 Mem0 + 阿里云 Milvus 向量数据库 的长期记忆系统。

3.4.1 多模态交互开发套件启用长期记忆

百炼的多模态交互开发套件自带相关功能,可以直接开启,如下图。

开启后,SDK通过user_id参数区分用户,每个用户的记忆相互隔离,具体配置可参考如下文档 https://help.aliyun.com/zh/model-studio/multimodal-interaction-protocol

开启后,通过Qoder将相关参数集成到原有项目,修改内容总结如下:

# 2.0 项目变更记录 —— 阿里云多模态交互长期记忆功能

## 需求概述

在 beken_genie 项目中启用阿里云多模态交互开发套件的**长期记忆功能**。  
核心机制:SDK 通过 `parameters.client_info.user_id` 字段按用户维度生成独立记忆库,不同 `user_id` 之间记忆隔离。

参考文档:
- SDK 使用文档:https://help.aliyun.com/zh/model-studio/mmi-rtos-sdk
- WebSocket 协议(user_id 说明):https://help.aliyun.com/zh/model-studio/multimodal-interaction-protocol

---

## 修改的文件清单(共 6 个文件)

### 1. `projects/common_components/qwen_voice_chat/Kconfig`

**变更内容**:新增 `QWEN_USER_ID` 配置项

```
config QWEN_USER_ID
    string "Qwen User ID (for long-term memory)"
    default "beken_genie_user_001"
    depends on QWEN_VOICE_CHAT
    help
        User ID for Alibaba Cloud multimodal interaction long-term memory feature.
        Memory is isolated by user_id dimension within the same application.
        Different user_id will have separate memory stores.
        Max length is 36 characters.
```

**说明**:
- 默认值为 `"beken_genie_user_001"`,最大长度 36 字符
- 可在项目 config 文件中覆盖,如 `CONFIG_QWEN_USER_ID="my_custom_user"`

---

### 2. `projects/common_components/qwen_voice_chat/include/qwen_voice_chat.h`

**变更内容**:在 `qwen_voice_chat_config_t` 结构体中新增 `user_id` 字段

```c
typedef struct {
    char *app_id;
    char *ws_id;
    char *api_key;
    char *device_name;
    char *user_id;          // 新增:用户ID(用于长期记忆功能,按user_id维度隔离记忆库,最大36字符)
    qwen_event_callback_t event_callback;
} qwen_voice_chat_config_t;
```

---

### 3. `projects/common_components/qwen_voice_chat/src/qwen_voice_chat.c`

**变更内容**:在 `qwen_voice_chat_init()` 函数中,将 `user_id` 赋值给 SDK 的 `g_mmi_config.user_id`

```c
// 设置 user_id 用于长期记忆功能(同一应用中按user_id维度生成记忆库,不同user_id之间记忆隔离)
if (config->user_id && strlen(config->user_id) > 0) {
    g_mmi_config.user_id = config->user_id;
    QWEN_LOGI("[MEMORY] User ID set for long-term memory: %s\n", config->user_id);
} else {
    g_mmi_config.user_id = NULL;  // NULL时SDK默认使用device_name
    QWEN_LOGI("[MEMORY] User ID not set, will use device_name as default for memory\n");
}
```

**说明**:
- SDK 的 `mmi_user_config_t.user_id` 字段会被包含在 WebSocket Start 消息的 `parameters.client_info.user_id` 中发送给云端
- 当 `user_id = NULL` 时,SDK 默认使用 `device_name` 作为用户标识
- `user_id` 指针需指向静态变量或持久内存(Kconfig 字符串常量满足要求)

---

### 4. `projects/beken_genie/main/app_main.c`

**变更内容**:在 Qwen 配置结构体初始化中传入 `CONFIG_QWEN_USER_ID`

```c
qwen_voice_chat_config_t qwen_config = {
    .app_id = CONFIG_QWEN_APP_ID,
    .ws_id = CONFIG_QWEN_WS_ID,
    .api_key = CONFIG_QWEN_API_KEY,
    .device_name = CONFIG_QWEN_DEVICE_NAME,
    .user_id = CONFIG_QWEN_USER_ID,       // 新增
    .event_callback = qwen_event_callback
};
```

---

### 5. `projects/common_components/qwen_voice_chat/CMakeLists.txt`

**变更内容**:无新增修改(已回滚,见踩坑 4)

---

### ~~6. `projects/beken_genie/main/CMakeLists.txt`~~

**变更内容**:无新增修改(已回滚,见踩坑 4)

---

## 踩坑点与注意事项

### 踩坑 1:Release 版本日志被编译移除(BK_LOGI 完全静默)

**现象**:设置了 `user_id` 并添加了 `QWEN_LOGI` 日志,但串口完全看不到任何输出。

**根因**:  
SDK 是 Release 版本(`bk_avdk/bk_idk/properties/modules/wifi/ip_ax` 目录不存在),  
构建系统在 `bk_avdk/bk_idk/tools/build_tools/cmake/build.cmake` 中全局设置了:
```cmake
list(APPEND compile_definitions "-DCFG_LOG_LEVEL=2")
```
`CFG_LOG_LEVEL=2` 对应 `BK_LOG_WARN`,导致 `BK_LOGI`(INFO 级别=3)在**编译时**就被宏展开为空操作 `(void)(format, ...)`,根本不会生成任何代码。

**解决**:在需要日志的组件 CMakeLists.txt 中用 `target_compile_definitions(PRIVATE)` 单独覆盖:
```cmake
target_compile_definitions(${COMPONENT_LIB} PRIVATE CFG_LOG_LEVEL=3)
```
这只影响该组件,不影响全局其他模块。

**日志级别对照**:
| 值 | 宏名 | 含义 |
|---|---|---|
| 0 | BK_LOG_NONE | 无日志 |
| 1 | BK_LOG_ERROR | 仅错误 |
| 2 | BK_LOG_WARN | 警告(Release 默认) |
| 3 | BK_LOG_INFO | 信息(需手动覆盖) |
| 4 | BK_LOG_DEBUG | 调试 |
| 5 | BK_LOG_VERBOSE | 详细 |

---

### 踩坑 2:RTOS 串口日志需显式 `\n` 才能刷新

**现象**:即使日志级别正确,部分日志仍可能看不到或与其他日志混在一起。

**根因**:BK7258 RTOS 平台的 `bk_printf_ext` 日志函数不会自动追加换行符,缓冲区可能不会及时刷新到串口。

**解决**:日志格式字符串末尾加 `\n`,并建议加 `[MEMORY]` 等前缀便于搜索:
```c
os_printf("[MEMORY] User ID set for long-term memory: %s\r\n", config->user_id);
```

> 注意:这里使用 `os_printf` 直接输出,而非 `QWEN_LOGW`/`BK_LOGW`,因为 BK_LOG 系统有运行时模块标签过滤,自定义 tag 会被过滤掉,详见踩坑 5。

---

### 踩坑 4(原踩坑 3):不能用 target_compile_definitions 覆盖全局 CFG_LOG_LEVEL

**现象**:在 CMakeLists.txt 中用 `target_compile_definitions(${COMPONENT_LIB} PRIVATE CFG_LOG_LEVEL=3)` 尝试覆盖日志级别,编译报错:
```
<command-line>: error: "CFG_LOG_LEVEL" redefined [-Werror]
```

**根因**:  
Armino 构建系统中,`build.cmake` 全局添加的 `-DCFG_LOG_LEVEL=2` 排在命令行后部,  
而 `target_compile_definitions(PRIVATE)` 添加的 `-DCFG_LOG_LEVEL=3` 排在命令行前部。  
两个 `-D` 定义同一个宏,在 GCC `-Werror` 下宏重定义警告会变成编译错误。  
且最终生效的是后出现的全局值(2),并非我们想要的 3。

**解决**:放弃 CMake 级别覆盖,改为在源代码中将关键日志用 `BK_LOGW`/`QWEN_LOGW`(WARN 级别=2)代替 `BK_LOGI`/`QWEN_LOGI`(INFO 级别=3),因为 Release 版本 `CFG_LOG_LEVEL=2` 不会移除 WARN 级别日志。

---

### 踩坑 5(原踩坑 4):运行时 log 命令与编译时级别的关系

**注意**:串口 CLI 命令 `log 1 4` 只能在编译时级别允许的范围内调整。  
例如编译时 `CFG_LOG_LEVEL=2`,那么 `BK_LOGI/BK_LOGD` 的代码已被移除,运行时设 `log 1 4` 也看不到。  
必须先确保编译时级别 >= 你想看的级别。

CLI 命令格式:`log [echo(0,1)] [level(0~5)] [sync(0,1)] [Whitelist(0,1)]`

---

### 踩坑 6:BK_LOG 运行时模块标签过滤(自定义 tag 被过滤)

**现象**:将关键日志改为 `QWEN_LOGW`(WARN 级别)后,串口仍然看不到任何 `QWEN_VOICE_CHAT` 相关输出。

**根因**:  
Armino 日志系统除了编译时级别过滤,还有**运行时模块标签过滤**:  
`bk_printf_ext_internel()`(文件 `bk_avdk/bk_idk/components/bk_system/printf.c`)中:
```c
if(tag != NULL) {
    if(bk_mod_printf_disbled(tag) ^ whitelist_enabled)
        return;  // 模块不在白名单/被禁用 → 直接返回,不输出
}
```
`"QWEN_VOICE_CHAT"` 是自定义的新 tag,不在系统默认的模块白名单中,所以运行时被过滤掉了。  
可以从日志观察到:`bt:E()`、`cli:W()`、`db-bd:W()` 等系统模块能正常输出,但 `QWEN_VOICE_CHAT:W()` 完全不出现。

**解决**:对关键诊断日志使用 `os_printf()`(即 `bk_printf`)直接输出,绕过 BK_LOG 的所有过滤机制:
```c
os_printf("[MEMORY] User ID set for long-term memory: %s\r\n", config->user_id);
```

**BK_LOG 日志输出的三层过滤机制总结**:
| 层级 | 机制 | 影响 |
|---|---|---|
| 1. 编译时 | `CFG_LOG_LEVEL` 宏条件编译 | 低于设定级别的日志宏被展开为空操作 |
| 2. 运行时级别 | `shell_level_check_valid(level)` | 运行时可调日志级别,但受编译时限制 |
| 3. 运行时模块 | `bk_mod_printf_disbled(tag)` + 白名单 | **自定义 tag 可能被默认过滤** |

---

### 注意 1:user_id 指针生命周期

SDK 要求 `mmi_user_config_t.user_id` 指向的内存在 SDK 运行期间保持有效:
> "传入时需要使用静态变量或申请对应内存空间,防止指向数据被错误释放"

当前实现使用 `CONFIG_QWEN_USER_ID`(Kconfig 编译时字符串常量),存储在只读段,生命周期与程序一致,无需额外处理。

---

### 注意 2:长期记忆功能需云端同步配置

设备端设置 `user_id` 只是启用的一半,还需在**阿里云百炼控制台**的应用配置中开启长期记忆选项。如果云端未开启,设备端即使传了 `user_id` 也不会有记忆效果。

---

### 注意 3:Kconfig 新增项需重新编译生效

新增 `QWEN_USER_ID` 后,需要重新构建项目让 Kconfig 系统重新生成 `sdkconfig.h`,  
否则 `CONFIG_QWEN_USER_ID` 宏不存在会导致编译失败。

---

## 验证方法

1. **编译烧录后**,串口日志搜索 `[MEMORY]`,应看到:
   ```
   [MEMORY] User ID set for long-term memory: beken_genie_user_001
   ```
   (注意:使用 `os_printf` 直接输出,无 tag 前缀)

2. **对话测试**:
   - 第一轮对话:告诉 AI 一个信息(如"我叫小明")
   - 重启设备
   - 第二轮对话:询问 AI 是否记得(如"你还记得我叫什么吗")
   - 如果 AI 能回忆起,说明长期记忆生效

功能集成后,进行软件编译,并烧录进硬件。

经测试,可以实现长期记忆功能(已烧录到硬件上,通过真实对话可以实现,没法截图,这里就不提供视频了)。

测试Case如下:

用户说:“请记住我叫XXX”;

智能体答复后,关机重新开机,websocket重新建立连接;

用户问:“我叫什么名字?”;
智能体会回答:“我当然记得你叫XXX啦,……”

3.4.2 记忆系统向量数据库实现

本节内容,我们在自建链路中,构建基于 Mem0 + 阿里云 Milvus 向量数据库 的长期记忆系统。

根据调研和大模型沟通,生成记忆系统部分的需求文档如下。

###  记忆系统服务 (v2.0.0 新增)

- **技术框架**: Mem0 + 阿里云 Milvus 向量数据库
- **核心功能**:
  - 三层记忆架构:long_term(永久)/ daily(30天滚动)/ conversation(内存)
  - 语义检索:根据用户输入自动检索相关记忆
  - 智能存储:通过 LLM 判断对话是否包含值得记忆的信息
  - 自动注入:对话前将记忆注入 LLM prompt
  - 定时清理:每天凌晨清理过期日记忆
  - 用户隔离:基于 user_id 完全隔离不同用户记忆

- **记忆类型**:

| 类型 | 保存时间 | 存储位置 | 说明 |
|------|----------|----------|------|
| long_term | 永久 | Milvus | 用户档案、偏好、重要事件 |
| daily | 30天 | Milvus | 每日情绪轨迹、话题摘要 |
| conversation | 会话期间 | 内存 | 当前对话历史(LLMService) |

- **记忆元数据结构**:
```python
{
    "type": "long_term" | "daily",
    "category": "user_profile" | "important_event" | "emotion_timeline",
    "emotion": "happy" | "sad" | ...,
    "timestamp": "2026-02-22T14:30:00",
    "date": "2026-02-22",  # 仅 daily 类型
    "user_id": "user-001"
}
```

- **关键方法**:
  - `add_memory()`: 添加记忆到向量数据库
  - `search_memory()`: 语义检索相关记忆
  - `get_memory_context()`: 获取记忆上下文用于 LLM 注入
  - `analyze_and_save()`: 智能判断并保存记忆
  - `cleanup_expired_memories()`: 清理过期记忆

- **工作流程**:
  1. 用户发送消息
  2. MemoryService 检索相关记忆(top-5)
  3. 记忆注入 LLM system prompt
  4. LLM 生成个性化回复
  5. 通过 qwen-turbo 判断对话是否包含值得记忆的内容
  6. 如需要,提取关键信息并存傥 Milvus

- **降级策略**:
  - 如果 Milvus 未配置或连接失败,服务自动进入降级模式
  - 基础功能(ASR/LLM/TTS)照常工作
  - 记忆功能被禁用,不影响主流程

---

用Qoder进行上述需求的代码实现,实现后,通过指令打包上传到百炼高代码应用中,用之前生成的前端页面确认功能都可用。

此时,服务端日志会看到如下报错:

因为此时Milvus 向量数据库还没有创建,服务自动降级为无记忆功能。

下面我们创建Milvus 向量数据库,按以下步骤创建:

1. 登录阿里云控制台

2. 搜索并进入 “Milvus向量数据库” 服务

3. 创建实例,配置如下图:

下图“网络及可用区”部分,需要提前创建好北京地域的VPC和交换机,这里不展开。

创建后,按照下图开启公网。

4. 创建完成后,获取以下信息:

   – Milvus 端点地址 (MILVUS_HOST)

   – 端口 (默认 19530)

   – 访问令牌 (MILVUS_TOKEN)

这里为了保证FC访问Milvus可以走内网访问,我们把高代码应用使用的FC指定到和Milvus相同的VPC,配置方法如下图。

然后把Milvus如下参数加入FC环境变量:

"MILVUS_HOST": "XXXXX-internal.milvus.aliyuncs.com",
"MILVUS_TOKEN": "root:XXXXX
"MILVUS_PORT": "19530"

再次调用前端测试页面进行对话测试。

告诉他我的名字,如下图。

FC服务端日志如下:

然后断开websocket,重新连接后询问我的名字,大模型正确回复。

4. Demo演示视频

4.1 前端页面demo展示

通过前端页面进行多模态交互,展示能力:

  • 人脸追踪;
  • 视觉理解(可以看到衣服和周围环境);
  • 知识库(问日程会调用知识库);
  • 长期记忆(基于之前的对话,在回答中体现我所在的城市);

4.2 硬件集成demo展示

硬件集成的闲聊功能展示Demo1

展示能力:

  • 语音唤醒;
  • 实时打断;
  • 天气查询、新闻查询;

硬件集成的闲聊功能展示Demo2

展示能力:

  • 语音唤醒;
  • 实时打断;
  • 知识库(问日程会调用知识库);
  • 长期记忆(知道我的名字、生日)
  • 联网查询(知道当前日期);

4.3 AI生成产品宣传视频

基于阿里的生图生视频模型、TTS模型,生成产品宣传视频如下。

5. 总结与后续计划

5.1 总结

本项目做了如下工作:

  • 通过两套方案实现了多模态交互
    • 基于百炼成熟的多模态交互开发套件SDK,实现:
      • 实时全双工语音交互;
      • 支持随时打断;
      • 可调用知识库;
      • 可一键集成天气查询、新闻资讯等功能;
      • 支持联网搜索;
      • 支持开启长期记忆;
    • 基于百炼的ASR+VLM+TTS模型,搭建完整的语音交互链路,实现:
      • 基于VL模型的人脸追踪(给出人脸坐标并在前端页面展示红点定位);
      • 基于VL模型实现对话环境的理解;
      • 实时全双工语音交互;
      • 支持随时打断;
      • 可调用知识库;
      • 可集成百炼智能体MCP;
      • 支持联网搜索;
      • 通过向量数据库构建长期记忆;
  • 把基于百炼的ASR+VLM+TTS模型搭建的完整服务,部署到百炼高代码应用(背后是阿里云函数计算FC提供的Serverless服务);
  • 构建了一个前端页面,用来通过Websocket长连接接入自建的高代码Serverless服务,验证可用性和效果;
  • 基于博通(Beken)BK7258 AI开发板,使用 Armino AIDK SDK开发框架,对官方 beken_genie 项目进行功能定制开发,把多模态交互开发套件SDK和基于百炼的ASR+VLM+TTS模型服务,进行软件编译,并烧录到硬件中,实现了通过物理设备进行实时交互。

本文大部分代码实现都是通过Qoder完成,没有AI编码的帮助,我一个人很难短时间完成这么多工作。感谢Qoder让我第一次完成硬件嵌入式开发。

5.2 后续计划

本次探索也遇到了很多尚未完全解决,或者有很大优化空间的地方,比如:硬件集成后对话延时的优化,硬件TTS语音播放的杂音。这些内容,是需要后续持续优化的。

后续有机会,我还想尝试:

  • 其他硬件的集成,累计更多的硬件集成经验;
  • 探索更多的模型调用场景;
  • 探索硬件集成OpenClaw;
  • 探索硬件部署端侧小模型解决特定场景问题(YOLO视觉模型、ASR端侧模型);
  • 探索声纹识别、语音降噪功能。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注