实现实时音频噪音抑制:Raspberry Pi Pico 2上的机器学习应用指南

全面解密:如何在 Raspberry Pi Pico 2 上实现音频噪音抑制
Arm 公司的首席软件工程师 Sandeep Mistry 向我们展示了一种创新的方法:如何将音频噪音抑制应用于麦克风输入。机器学习(ML)技术正在彻底改变许多软件应用程序的开发方式。开发者现在可以为所需系统整理出包含大量输入和输出示例的数据集,并利用这些数据集来训练 ML 模型。在训练阶段,ML 模型会从输入和输出中学习模式。经过训练的模型被部署到设备上,以对来自现实世界的输入进行推理,并根据 ML 模型的预测输出执行一个或多个操作。可以部署到基于微控制器的设备(例如,使用 Arm Cortex-M33 微控制器 RP2350 的新 Pico 2 板)的小型 ML 模型仅需几千字节的内存。将 ML 模型部署到微控制器上可以显著降低系统延迟,因为数据是在接近输入数据源的设备上处理的。本文将深入探讨如何将基于现有 ML 的音频噪音抑制算法部署到新 Pico 2 板的 RP2350 微控制器上。RP2350 的双核 Arm Cortex-M33 CPU 使得开发者能够部署更为计算密集型的应用,其性能超越了原始 Raspberry Pi Pico 板中使用的 RP2040 微控制器。随后,该算法将被集成到我为原始 Pico 板开发的 USB 麦克风应用程序中。最初的应用程序从数字脉冲密度调制(PDM)麦克风捕获数据,并将其处理为兼容 USB 音频标准的格式,通过 USB 传输。
Arm Cortex-M33: https://developer.arm.com/Processors/Cortex-M33
Pico 2: https://www.raspberrypi.com/products/raspberry-pi-pico-2/
RP2350: https://www.raspberrypi.com/products/RP2350/
原始 Pico 板开发的 USB 麦克风应用程序: https://www.hackster.io/sandeep-mistry/create-a-usb-microphone-with-the-raspberry-pi-pico-cc9bd5

图片
Hackster.io上使用Raspberry Pi Pico指南创建USB麦克风的屏幕截图

算法背景介绍

2018年,Jean-Marc Valin发表了一篇关于实时全带语音增强的混合DSP/深度学习方法的论文。本文详细介绍了使用基于递归神经网络(RNN)的 ML 模型来抑制音频源中的噪声。如果您希望了解更多关于该算法的信息,可以访问 Jean-Marc 的 RNNoise:学习噪声抑制页面,那里包含了算法的详细介绍和交互式示例。该项目的源代码也可以在 RNNoise Git 存储库中找到。
RNNoise: https://jmvalin.ca/demo/rnnoise/

图片
RNNoise的屏幕截图:学习噪声抑制页面

该算法的基本原理是通过将信号分成 22 个频段,从 10 毫秒的 48 kHz 音频源中提取出 42 个特征。

图片
屏幕截图:RNNoise的“定义问题”部分:学习噪音抑制页面

然后,这 42 个特征被用作神经网络的输入,神经网络计算出 22 个频段的增益。计算出的增益会应用于原始音频信号,以生成去噪版本。神经网络还会输出"语音活动检测"(VAD)结果,该结果显示输入信号中存在语音的预测置信度,其值范围为 0 到 1 之间。

图片
屏幕截图:RNNoise的“深度架构”部分:学习噪音抑制页面

移植和基准测试算法

RNNoise 项目的原始 C 代码可以集成到使用 Raspberry Pi Pico SDK 的 CMake 项目中。移植的所有源代码都可以在 GitHub 的 rnnoise-examples-for-pico-2 代码库中找到。我们创建了一个新的 CMake 目标库,该库基于 RNNoise 项目 v0.1.1 的 celt_lpc.c、denoise.c、kiss_fft.c、pitch.c、rnn.c 和 rnn_data.c 文件。对 denoise.c 进行了轻微的修改,以便在 biquad 函数中使用单精度浮点计算,同时使用 log10f(...) 和 sqrtf(...) 替代 log10(...) 和 sqrt(...) 函数。随后,可以将该库集成到一个基准测试应用程序中,调用 rrnoise_create(...) 函数初始化模型,然后测量 rnnoise_process_frame(...) 函数处理 480 个样本所需的时间。要将此基准测试应用程序部署到 Raspberry Pi Pico 1 或 Pico 2 板上,首先遵循“Raspberry Pi Pico 入门”C/C++ SDK 指南的第 2 节和第 9 节,然后运行以下命令以构建 .uf2 应用程序并将其部署到板上:

git clone --recurse-submodules https://github.com/ArmDeveloperEcosystem/rnnoise-examples-for-pico-2.git
cd rnnoise-examples-for-pico-2
mkdir build
cmake ... -DPICO_BOARD=pico2
make rnnoise-benchmark

编译完成后,可以通过按住电路板上的白色 BOOTSEL 按钮来将 examples/benchmark/rnnoise-benchmark.uf2 文件部署到电路板上,同时将 USB 电缆插入计算机并将 .uf2 文件复制到 Pico 的 USB 磁盘上。
以下是 Pico 1 和 Pico 2 板上的基准测试结果:

Pico (RP2040) Cortex-M0+ @ 125 MHzPico 2 (RP2350) Cortex-M33 @ 150 MHz
rnnoise_process_frame(...)372,644 微秒22,093 微秒

原始的 Pico 1 大约需要 372.6 毫秒,而新的 Pico 2 仅需 22.1 毫秒,这是电路板之间的 16.87 倍速度提升。

针对 16 kHz 音频修改算法

电路板以 48 kHz 的采样率处理 480 个音频样本时, rnnoise_process_frame(...) 函数必须在 0.01 秒(480 / 48,000)或 10 毫秒内完成。Pico 2 的基准结果为 22.1 毫秒,对于 48 kHz 音频仍显不足,但对于处理采样率为 16 kHz 的音频,则足够满足 30 毫秒内完成音频处理的要求。可以轻易地修改 denoise.c 中的 eband5ms 变量,以调整算法以处理 16 kHz 数据。该变量控制 22 个频段的起始范围。调整的方法是将原始值乘以 3(由于 16 kHz 音频采集样本的时间是 48 kHz 音频的 3 倍),并将最大起始位置设为 120。以下是原始值:

static const opus_int16 eband5ms[] = {  
/*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 20k*/  
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100  
};

以及用于 16 kHz 音频的修改值:

static const opus_int16 eband5ms[] = {  
/*0 200 400 600 800 1k 1.2 1.4 1.6 2k 2.4 2.8 3.2 4k 4.8 5.6 6.8 8k 9.6 12k 15.6 20k*/  
0, 3, 6, 9, 12, 15, 18, 21, 24, 60, 36, 42, 48, 60, 72, 84, 102, 120, 120, 120, 120, 120  
};

串行示例可以编译并部署到电路板上,以测试修改后的算法。该示例通过 USB 连续循环接收 480 个 16 位音频样本,使用去噪算法对其进行处理,然后通过 USB 传输经过去噪处理的样本。在个人电脑上,您可以使用 serial_denoise.py Python 脚本从文件中发送 16 位、16 千赫的原始音频,并将去噪音频保存到文件中。这些原始值可导入 Audacity 等应用程序,用于可视化和回放。下面是一个例子:第一轨是原始音频(噪音),第二轨是 Pico 2 上去噪后的版本。

图片
在Audacity应用程序的两个音轨的屏幕截图。

**顶部:*原始音频。底部:音频的去噪版本。

我选择了一个噪声明显降低的区域。到目前为止一切顺利;该算法已通过验证,可在电路板上通过 USB 从个人电脑串流 16 kHz 音源!

将算法集成到 USB 麦克风应用程序中

最初为 Pico 1 开发的 USB 麦克风应用程序现在通过板载去噪功能得以增强。

硬件

此应用程序需要以下硬件:

  • Raspberry Pi Pico 2 主板
  • Adafruit PDM MEMS 麦克风接口
  • 半尺寸试验板
  • 跳线
  • 滑动开关(可选)
  • 触觉按钮(可选)

可选的滑动开关将用作切换开关,以便在运行时禁用或启用噪声抑制处理,而可选的轻触开关则为重置电路板提供便利。以下是连接硬件的方式:

图片
项目接线图

Raspberry Pi Pico 2PDM MEMS 麦克风接口滑动开关(可选)触觉按钮(可选)
3V3(输出)3V
GNDGNDSEL中间针底部针
RUN顶部针
GPIO21DAT
GPIO22CLK
GPIO17底部针

接线完成后,您的试验板将看起来如下所示:

图片

软件

应用程序将使用 microphone-library-for-pico 从采样率为 16 kHz 的 PDM 麦克风采集 480 个 16 位采样。该库结合了 RP2350 的可编程 I/O (PIO) 和直接内存访问 (DMA) 功能,利用 OpenPDM2PCM 库将原始 PDM 数据转换为脉冲编码调制 (PCM) 格式。16 位 PCM 数据被转换为 32 位浮点,并应用 RNNoise 算法去噪。随后,去噪帧被转换为 16 位整数,并使用 TinyUSB 库通过 USB 发送。USB 传输每 1 毫秒发送 16 个去噪采样。

图片
带去噪功能的 USB 麦克风框图

RP2350 上的两个 Cortex-M33 内核分别负责此应用程序的不同功能。内核 1 从 PDM 麦克风捕获原始数据,并对其进行过滤和去噪处理。内核 0 则利用 TinyUSB 库和 RP2350 的 USB 接口通过 USB 传输去噪数据。

RNNoise 模型的语音活动检测(VAD)输出将通过脉宽调制 (PWM) 显示在 Pico 2 的内置 LED 上。当 VAD 输出接近 1.0 时,LED 将亮起;接近 0.0 时,LED 将熄灭。

图片
记录 Pico 2 电路板使用 RNNoise VAD 输出通过 PWM 控制内置 LED 亮度的情况

应用程序的源代码可在 rnnoise-examples-for-pico-2 GitHub 代码库的 examples/usb_pdm_microphone 文件夹中找到。该应用程序的编译方式与基准测试程序类似,使用以下 make 命令:

make rnnoise_usb_pdm_microphone

编译完成后,按住 BOOTSEL 按钮并重置电路板后,即可将 examples/usb_pdm_microphone/rnnoise_usb_pdm_microphone.uf2 文件复制到 Pico 2 的 USB 磁盘中。
源代码:https://github.com/ArmDeveloperEcosystem/rnnoise-examples-for-pico-2/tree/main/examples/usb_pdm_microphone

测试

应用程序加载到电路板后,可以使用 Audacity 测试音频录制,方法是首先单击音频设置按钮 -> 重新扫描音频设备,然后在音频设置按钮中选择录音设备 -> MicNode,最后点击录音按钮。

图片
在Audacity中选择MicNode作为记录设备

如果连接了可选的滑动开关,则可以通过将开关滑向 Pico 2 的 USB 接口,来禁用噪声抑制功能,然后通过将开关滑离 USB 接口来重新启用噪声抑制功能。

下一步工作
本博客展示了如何利用 Raspberry Pi Pico 2 的 Arm Cortex-M33 CPU 的额外计算能力,使用 ML 模型对从 PDM 麦克风捕获的 16 kHz 的 16 位实时音频数据进行去噪处理。去噪算法利用了 Cortex-M33 的浮点运算单元 (FPU),其运行速度比原始 Pico 板上的 Cortex-M0+ 快 16.87 倍。该应用利用一个 CPU 捕捉、过滤和去噪数据,另一个 CPU 通过 USB 将音频数据传输到 PC。下一步,您可以修改应用程序,在通过 USB 向个人电脑发送降噪数据之前添加自动增益控制 (AGC)。此外,去噪数据也可以直接在电路板上使用,作为另一种数字信号处理 (DSP) 算法或 ML 模型的输入,在核心 0 上运行,而不是 USB 栈。