news 2026/7/24 22:15:04

3A+ASR语音识别算法工程代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3A+ASR语音识别算法工程代码

# 嵌入式语音关键词检测(KWS)引擎 — 工程实践讲解

> 项目名称:longAsrLib(SOC_ASR_AEC)
> 模型ID:GASR_320k
> 平台:嵌入式 Linux / MCU
> 核心功能:关键词检测(Keyword Spotting) + 声学回声消除(AEC)

---

## 第一章:项目概述与工程结构

### 1.1 项目背景

本项目是一个面向嵌入式设备的**语音关键词检测引擎**,包含从音频采集、前端信号处理、神经网络推理到关键词解码的完整流程。代码同时支持 **PC Linux 离线文件处理** 和 **嵌入式实时流式处理**。

### 1.2 核心功能

- **KWS(Keyword Spotting)**:从连续音频流中检测预定义的关键词
- **AEC(Acoustic Echo Cancellation)**:消除扬声器播放音对麦克风采集的干扰
- **NS(Noise Suppression)**:背景噪声抑制
- **VAD(Voice Activity Detection)**:人声活动检测,支持多种策略(分贝检测、特征检测等)
- **多麦克风支持**:2/3/4 通道麦克风阵列
- **CTC 解码 + Recheck 机制**:降低误触发率

### 1.3 目录结构

```
GASR_AEC/
├── CMakeLists.txt # 顶层 CMake 构建
├── build.sh # 编译脚本
├── run.sh # 运行脚本(手动模式)
├── all_run.sh # 全流程一键运行(配置生成+编译+推理)

├── ui_main.c # 主入口:离线文件推理
├── streaming.cpp # 主入口:PC 实时录音流式推理
├── kws_detect.c / .h # KWS API 封装层
├── bsp_long_asr_ctrl.c / .h # 板级支持层:ASR 初始化与控制
├── linkqueue.c / .h # 链表队列工具(用于未唤醒音频截取)

├── appLangSrc/ # 语言模型资源解析层
│ ├── long_u_lang_pc_main.c # 多语言模型资源创建/释放
│ ├── long_language.c # 语言管理
│ ├── long_keyword.c # 关键词列表解析
│ ├── long_thres_list.c # 阈值列表解析
│ ├── long_lang_model.c # 语言模型配置
│ ├── long_parse_model_config.c # 模型配置文件解析
│ └── long_parse_thres_list.c # 阈值列表文件解析

├── long_config/ # 生成的模型配置(由 main_sdk_res_cfg 生成)
│ ├── model_config.h # 模型架构参数(卷积、FSMN、特征等)
│ ├── long_alg_config.h # 算法开关配置
│ ├── long_weights.c/.h # 模型权重
│ ├── long_keyword.c/.h # 关键词数据结构
│ ├── long_thres_list.c/.h # 阈值数据结构
│ └── long_decoder_patch.c # 解码器 Recheck Patch

├── appResOut/ # SDK 资源输出模板
│ ├── lib_long_asr.h # ASR 库 API 头文件
│ ├── long_decoder_patch.c # 解码 Patch 模板
│ └── long_alg_config.h # 算法配置模板

├── template/ # 通用模型模板
│ ├── aec_weights_*.bin # AEC 权重
│ ├── weights_ns_*.bin # NS 权重
│ └── vad_tiny_*.bin # VAD 权重

├── tuning/works/ # 模型调优工作区
│ └── G111_320K/ # 当前模型(332KB)
│ ├── weights.bin # 主网络权重
│ ├── long_keyword.bin # 关键词 bin
│ ├── thres_list.bin # 阈值 bin
│ └── model_config.h # 模型配置

└── wav/, audioCut/, pcm/ # 音频输入/输出/调试目录
```

### 1.4 构建与运行流程

```
all_run.sh 全流程:
① main_sdk_res_cfg → 生成 long_config/(权重、关键词、阈值、配置)
② cp 配置文件 → app/projects/libuser_proj/
③ build.sh(cmake + make) → 编译出 main 可执行文件
④ ./main input.pcm weights.bin ... → 执行离线推理
```

---

## 第二章:神经网络模型架构

### 2.1 整体网络结构

模型基于 **CNN + FSMN(Feedforward Sequential Memory Network)** 混合架构,接收 FBank 声学特征,输出 CTC 后验概率。

```
音频输入 (16kHz)

FBank 特征提取 (80维, 40ms帧移)

PCEN (Per-Channel Energy Normalization)

Conv2D × 3 (时域卷积,时间降采样)

FSMN × 6 (双向记忆网络)

线性层 + Softmax

CTC 解码 → 关键词检测
```

### 2.2 卷积层(Conv2D)配置

| 层 | 输入通道 | 输出通道 | 卷积核 | 步长 | 输出尺寸 |
|---|---|---|---|---|---|
| Conv2D-1 | 1 | 8 | (4, 3) | (1, 2) | (8, 4, 39) |
| Conv2D-2 | 8 | 16 | (3, 3) | (2, 2) | (16, 2, 19) |
| Conv2D-3 | 16 | 32 | (3, 3) | (2, 1) | (32, 1, 17) |

- 每层输出经过 **ReLU6** 激活
- 用于时间维度的降采样和局部模式提取
- 缓存机制:`TOTAL_CACHE_SIZE` 计算跨帧依赖

### 2.3 FSMN 层(核心建模单元)

- **6 层** FSMN,每层隐层维度 **192**
- 记忆维度(P_DIM):**108**
- 前看帧数(RIGHT_FRAMES):**1**
- 状态长度:**7**(左 3 + 右 1 + 自身 1)
- **FSMN V1 层无 bias**(`FSMN_V_BIAS = 0`)

FSMN 通过在隐层状态上叠加记忆单元来建模时序依赖,比传统 RNN 更轻量、更适合嵌入式部署。

### 2.4 CTC 输出

- 类别数:**66**(中文音素 + 静音 + 空白符)
- 混合精度量化(MixedPTSQ):**46 个 Q 值**,**23 个混合精度层**
- 模型总大小:**332,876 字节**(约 325KB)

### 2.5 PCEN 特征增强

```c
#define long_PCEN_EN 1 // 启用 PCEN
#define long_PCEN_MAX_SIZE 1 // PCEN 增益上限
```

PCEN 替代传统 CMVN,对动态范围进行逐频带自适应压缩,在噪声环境下更鲁棒。

---

## 第三章:音频前端处理

### 3.1 多麦克风通道处理

根据 `long_DSP_MIC_MAX_NUM`(可配 1/2/3/4),选择对应的通道分离函数:

```c
// ui_main.c: 通道分离实现
long_ch_separate_process2 — 2通道:LR
long_ch_separate_process3 — 3通道:L,R,M
long_ch_separate_process4 — 4通道:L,R,M,N
```

### 3.2 AEC(声学回声消除)

```c
// long_alg_config.h
#define long_AEC_CLOSE_T 0 // 关闭
#define long_AEC_V1_T 1 // AEC V1 版本
#define long_AEC_V2_T 2 // AEC V2 版本
#define long_AEC_ONLY_T 6 // 仅 AEC

#define long_ASR_UI_AEC_TYPE 1 // 当前启用 AEC
```

AEC 参考信号来自扬声器通道,通过自适应滤波器建模回声路径,从麦克风信号中减去。

主麦克风和 AEC 参考麦克风的选择:
```c
// bsp_long_asr_ctrl.c
slong_long_mcu_micMasterIdx(0); // 主麦克风
slong_long_mcu_micAecIdx(1); // AEC 参考
```

### 3.3 NS(噪声抑制)

```c
#define long_NS_CLOSE_T 0
#define long_NS_V1_T 1 // 神经网络噪声抑制
#define long_NS_ONLY_T 6
#define long_ASR_UI_NS_TYPE 0 // 当前未启用 NS
```

模板中提供了 3k/5k/9k 三种规模的 NS 权重,可按需切换。

### 3.4 VAD(语音活动检测)

支持多种 VAD 策略,通过 `long_ASR_UI_VAD_TYPE` 选择:

| 值 | 宏 | 说明 |
|---|---|---|
| 0 | long_VAD_TYPE_CLOSE | 关闭 VAD |
| 1 | long_VAD_TYPE_PURE_DB | 纯分贝检测 |
| 2 | long_VAD_TYPE_HUMAN_VOL | 人声分贝检测 |
| 3 | long_VAD_TYPE_HUMAN_NS | 基于 NS 的人声检测 |
| 5 | long_VAD_TYPE_HUMAN_FEATURE | 基于特征的人声检测 |
| 6 | long_VAD_TYPE_ONLY_VAD | 仅 VAD |

VAD 结束判决参数(`ui_main.c`):
```c
t_cfg_p->long_hm_no_cnt_max = 40; // 连续 40×40ms = 1.6s 静音判定结束
t_cfg_p->long_wake_times_thd = 2; // 唤醒次数阈值
```

### 3.5 FBank 特征提取

```c
#define SAMPLE_RATE 16000
#define NFFT 512 // FFT 点数
#define NFILT 70 // Mel 滤波器组维度
#define long_FBANK_STRIDE 192 // 帧移(12ms @16kHz)
#define WINDOW_STRIDE 4 // 特征帧移
```

特征提取流程:
1. 预加重 → 分帧加窗(Hanning)
2. 512 点 FFT → 功率谱
3. Mel 滤波器组(70 维)→ Log 压缩
4. PCEN 增强 → 输出(1, 4, 70)特征块

---

## 第四章:关键词检测与解码

### 4.1 关键词数据结构

关键词在系统中以**音素序列**表示。关键词列表通过 `long_keyword.bin` 加载解析为 `long_KWS_PARAM` 结构体数组:

```c
// appLangSrc/long_keyword.h
typedef struct {
int16_t *phone_idx_seq; // 音素索引序列
int16_t seq_len; // 序列长度
char *label; // 关键词文本标签(如"关灯")
} long_KWS_PARAM;
```

### 4.2 解码流程

```
FBank 特征
→ 神经网络前向 → CTC 后验概率矩阵 (T × 66)
→ 贪婪解码 / 前缀束搜索 → 音素对齐路径
→ 关键词匹配(检测音素序列是否匹配)
→ Recheck 验证 → 输出触发事件
```

### 4.3 Recheck 机制(防误触)

`long_decoder_patch.c` 实现了三重验证:

1. **`recheck_neg`**(负例验证):
- 检查两个识别音素之间的**时间间隔**是否过大(>40帧=1.6s => 拒识)
- 检查 CTC 最大路径与对齐路径的**不一致计数**是否超阈值

2. **`recheck_on_talk`**(起始验证):
- 使用 `KWS_COMPLETE_FLAG`(prefix_flag=254)标记关键词结束
- 检查起始音素前的负例帧计数

3. **`has_k_zeros`**(跳帧检测):
- 检测是否存在连续 K 帧 CTC 得分极低的情况

### 4.4 阈值系统

每个关键词对应一组阈值(`thres_list.bin`),包含:
- `thres_neg_left` / `thres_neg_right`:负例数量阈值
- 各层得分的门限值
- 聚类配置(`WordsSimilarGroups`):计算关键词间的相似度,生成聚类集

### 4.5 流式处理与离线处理

**离线模式**(`ui_main.c`):
```c
while (fread(pcm_data, 2, buffer_len, pcm1_fin) == buffer_len) {
// 通道分离
long_ch_separate_process4(pcm_data, samples, n_ch_diff_data);
// 送入 ASR 引擎
long_asr_wakeup_buf_write(n_ch_diff_data, samples, 1, 2, 0, 1);
// 检测唤醒
if (wakeup) { /* 保存语音片段 */ }
}
```

**流式模式**(`streaming.cpp`):
- 使用 **RtAudio** 库采集实时音频(6通道,16kHz,SINT16)
- 回调函数中提取通道数据后调用 `kws_detect()`
- 适用于 ReSpeaker 4-Mic Array 等 USB 麦克风阵列

### 4.6 KWS API 接口

```c
// kws_detect.h
int kws_init(); // 默认资源路径初始化
int kws_init_with_res(weights, param, // 指定资源文件路径
keyword, thres, recheck);
int kws_detect(short *pcm, int len); // 逐帧检测
int kws_exit(); // 清理资源
```

---

## 第五章:工程实践与调试

### 5.1 编译与运行

#### 首次全流程
```bash
# 1. 配置 all_run.sh 中的资源路径
# 2. 一键执行
./all_run.sh
# → 自动完成:配置生成 → 编译 → 推理
```

#### 手动分步
```bash
# 1. 编译 outFactory(仅首次)
cd outFactory && ./build.sh && ./run.sh

# 2. 生成 long_config/
./main_sdk_res_cfg ...(参数见 all_run.sh)

# 3. 编译主程序
./build.sh

# 4. 运行推理
./main input.pcm weights.bin \
"outfile_disable_recEnable" "langNo:0" \
dummy.pcm long_keyword.bin thres_list.bin \
model_config.h "audio_cut_enable" \
"pcm_wake_enable123" "pcm_unwake_enable123"
```

### 5.2 调试工具

#### 内存检测
```bash
# run.sh 中取消注释即可
valgrind --tool=memcheck --leak-check=full \
--track-origins=yes ./main ...
```

#### 唤醒音频保存
```c
// ui_main.c
#define long_DEBUG_ASR_WAKE_FILE_EN 1 // 保存唤醒前后的 PCM
#define long_DEBUG_ASR_UNWAKE_FILE_EN 1 // 保存未唤醒的 PCM(用于负例分析)
```

保存位置:`./pcm/` 和 `./pcm_unwake/`

#### 音频分割
```c
// ui_main.c: 命令行参数 "audio_cut_enable"
// 使用 VAD 回调将每个语音片段保存到 ./audioCut/s_<N>.pcm
```

### 5.3 关键配置项调整

| 配置文件 | 配置项 | 作用 |
|---|---|---|
| `long_alg_config.h` | `long_DSP_MIC_MAX_NUM` | 麦克风通道数 |
| `long_alg_config.h` | `long_ASR_UI_AEC_TYPE` | AEC 开关 |
| `long_alg_config.h` | `long_ASR_UI_VAD_TYPE` | VAD 策略选择 |
| `model_config.h` | `H_DIM`, `P_DIM` | FSMN 隐层/记忆维度 |
| `model_config.h` | `RIGHT_FRAMES` | 前看帧数 |
| `long_alg_patch_config.h` | `long_PATCH_USE_EN` | Recheck 开关 |

### 5.4 性能优化建议

1. **内存优化**:
- 启用 `long_VAD_NEED_UI_HEAP_EN` / `long_ASR_NEED_UI_HEAP_EN` 使用堆内存
- `TOTAL_CACHE_SIZE` 控制 Conv2D 缓存大小
- `long_PRIV_VAL_POOL_SIZE` 控制私有内存池

2. **计算优化**:
- 混合精度量化(MixedPTSQ)减少模型大小和推理时间
- Conv2D 使用 `img2col + GEMM` 加速(在 DSP 平台上)
- FSMN 的 `STATE_CACHE_LEN` 控制状态缓存量

3. **准确性调试**:
- 调整 `thres_list.bin` 中的阈值改善召回率与精度的平衡
- 调整 `t_cfg_p->long_hm_no_cnt_max` 控制 VAD 断句时长
- 调整 `recheck_neg` 中的 `thres_neg_left/right` 参数

### 5.5 常见问题

| 问题 | 排查方向 |
|---|---|
| 唤醒率低 | 检查 AEC/NS 是否正常;降低 thres_list 阈值;确认麦克风增益 |
| 误触发多 | 开启 Recheck(long_PATCH_USE_EN=1);提高阈值;检查音频质量 |
| 编译失败 | 运行 `sed -i -e 's/\r$//' build.sh run.sh` 转换换行符 |
| 运行时崩溃 | 使用 Valgrind 检测内存泄漏;检查权重文件路径是否正确 |

---

*本文档基于 etAsrLib 工程(JLLIBETASR_AEC2 / G111_320k)编写,用于课程设计教学参考。*

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 22:13:44

Topit:终极Mac窗口置顶工具完整指南 - 高效多任务工作新体验

Topit&#xff1a;终极Mac窗口置顶工具完整指南 - 高效多任务工作新体验 【免费下载链接】Topit Pin any window to the top of your screen / 在Mac上将你的任何窗口强制置顶 项目地址: https://gitcode.com/gh_mirrors/to/Topit 还在为Mac上频繁切换窗口而烦恼吗&…

作者头像 李华
网站建设 2026/7/24 22:11:31

AI-HF Patch终极指南:3分钟搞定AI少女游戏增强补丁

AI-HF Patch终极指南&#xff1a;3分钟搞定AI少女游戏增强补丁 【免费下载链接】AI-HF_Patch Automatically translate, uncensor and update AI-Shoujo! 项目地址: https://gitcode.com/gh_mirrors/ai/AI-HF_Patch AI-HF Patch是一款专为AI-Shoujo&#xff08;AI少女&a…

作者头像 李华
网站建设 2026/7/24 22:09:39

第二讲:C语言数据类型和变量

目录 一、数据类型介绍 1. 什么是数据类型 2. 常见的数据类型分类 2.1 内置数据类型 2.2 自定义数据类型 二、变量 1. 变量的创建与命名规则 1.1 变量的创建&#xff08;声明与初始化&#xff09; 1.2 变量的命名规则 2. 变量的作用域&#xff1a;全局变量与局部变量…

作者头像 李华
网站建设 2026/7/24 22:08:10

社区扩展管理_add-community-extension

以下为本文档的中文说明该技能用于处理GitHub Issue提交的社区扩展&#xff08;Extension&#xff09;&#xff0c;将其添加到Spec Kit目录中。主要功能是解析扩展提交Issue、验证目录条目、更新catalog.community.json和docs/community/extensions.md文档&#xff0c;并创建PR…

作者头像 李华
网站建设 2026/7/24 22:06:33

5分钟掌握本地图片搜索神器:告别海量图片管理烦恼的终极指南

5分钟掌握本地图片搜索神器&#xff1a;告别海量图片管理烦恼的终极指南 【免费下载链接】ImageSearch 基于.NET10的本地硬盘千万级图库以图搜图案例Demo和图片exif信息移除小工具分享 项目地址: https://gitcode.com/gh_mirrors/im/ImageSearch 还在为在数万张照片中寻…

作者头像 李华
网站建设 2026/7/24 22:05:36

3分钟掌握网页视频下载:猫抓视频嗅探工具完全指南

3分钟掌握网页视频下载&#xff1a;猫抓视频嗅探工具完全指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你是否曾经遇到过这样的困扰&#xf…

作者头像 李华