双麦克风固定波束形成:从原理到es8311硬件落地的语音增强指南
2026/9/13 11:43:21 网站建设 项目流程

简介:固定波束形成(Fixed Beamforming)是麦克风阵列语音增强中的关键算法,通过调整各麦克风通道权重形成指向性波束,从而增强目标方向语音、抑制环境噪声,广泛用于语音识别、会议系统、智能家居等场景。这份基于MATLAB的BeamF代码,适合音频信号处理与语音增强方向的开发者、学生或研究人员用于入门学习与算法验证。压缩包内共1个文件,即BeamF.m脚本,整体仅1KB,代码量少、结构清晰,覆盖阵列信号预处理、波束指向参数设置、麦克风加权系数计算与合成输出等环节,便于快速理解延迟求和(Delay-and-Sum)等固定波束形成算法的实现流程。作为极简示例,它也可作为进一步扩展自适应波束形成或开展不同算法对比的实验起点。目前CSDN上已有328人学习浏览,可作语音增强入门学习与课题实验的参考。

1. 固定波束形成不是把两路麦克风信号加起来那么简单

双麦克风阵列的固定波束形成,是语音增强前端里性价比最高的第一级处理:不估计噪声、不迭代、不收敛,只要阵列几何已知,就能把波束指向固定方向,压掉来自侧面和后方的干扰。很多人把它当作"两路相加取平均",结果出来的语音反而发闷、噪声更厚——因为相位没对齐。固定波束形成的核心就一句话:先把各路信号按到达时间差对齐,再加权叠加,让目标方向的信号同相相加、非目标方向被部分抵消。下面按"原理 → 最小实现(Python) → 板级落地(双麦克风阵列和 es8311 音频编解码器电路) → 验收指标"的顺序,把一个固定波束工程从公式讲到能验收到位的程度。适合做语音前端、会议设备、车载语音交互的工程师,尤其是拿到 BeamF.rar 这类固定波束资源包却不知道怎么改参数的那拨人。

2. 固定波束形成的原理与选型:语音增强为什么先把空间信息用掉

2.1 远场假设与导向矢量:先算准到达时间差

波束形成把麦克风阵列当成空间采样器,固定波束形成分三步:算到达时间差、做相位对齐、加权求和。第一步最容易被低估。常用远场模型认为声波以平面波到达阵列,第 m 路相对参考麦克风的到达时间差是 τ_m = (d_m·cosθ)/c,d_m 是第 m 路到参考路的间距,θ 是声源相对阵列轴线的夹角,c 取 343 m/s。

远场假设什么时候成立?用 Fraunhofer 距离判据 r > 2D²/λ。双麦 d=2 cm、3 kHz 的波长约 11.4 cm,临界距离只有约 0.7 cm,也就是说说话人离麦克风超过 1 cm 基本就满足远场假设。近场(说话人贴着麦克风或阵列孔径很大)才需要按距离逐点建模,嵌入式双麦产品绝大多数按远场做,误差可以忽略。

导向矢量 a(f,θ) = [1, e^{-j2πfτ}]^T 描述的是"目标方向信号在各路之间应有的相位关系"。固定波束输出写成 Y(f) = w^H(f)·x(f),w 在设计阶段一次算好,运行时不读数据、不迭代——这是"固定"两个字的含义。θ=90°(侧射)时 τ=0,两路相位天然一致,波束退化为等权平均,只靠"噪声不相干"拿 3 dB 增益;θ=0°(端射)时 τ 最大,相同间距下方向性最好。双麦降噪产品把目标设置在端射方向,原因就在这里。

2.2 延迟求和与超指向的权重公式:双麦克风固定波束怎么选

最常见的两种固定权重:延迟求和(Delay-and-Sum,DSB)和超指向(Superdirective)。

DSB 的权重是 w = a/M,实现极简,效果不差:对空间不相干噪声,M 路理论增益 10lgM,双麦 3 dB;对扩散噪声场,增益随频率起伏,低频段趋近 1——这是物理限制,不是实现问题。DSB 的优势是稳健:通道幅度失配 ±1 dB、相位偏差几度,方向图仍然可用。第一版实现优先选 DSB。

超指向利用扩散噪声场的空间相干性做白化,权重 w = Γ^{-1}a/(a^HΓ^{-1}a)。扩散噪声场里两路麦克风的相干系数是 Γ_mn = sinc(2πfd/c),间距越小、频率越低,矩阵越接近奇异,权重会变得非常大,把麦克风增益失配和位置误差放大成明显的语音失真。通用做法是加对角加载:w = (Γ + λI)^{-1}a,λ 从 Γ 最大特征值的 1% 起调,我一般从 0.05 起。下面代码是双麦超指向权重的计算,可直接替换 3.1 节里的叠加权重。

import numpy as np def fixed_weights_superdirective(f, d, c=343.0, load=0.05): # 2 元线性阵列,目标指向端射(0°) M = 2 gamma = np.ones((M, M), dtype=complex) for m in range(M): for n in range(M): if m != n: # 扩散噪声场两路相干系数: sinc(2fd/c) gamma[m, n] = np.sinc(2.0 * f * d / c) a = np.ones((M, 1), dtype=complex) # 端射导向矢量 w = np.linalg.solve(gamma + load * np.eye(M), a) w = w / (a.conj().T @ w).item() # 归一化,目标方向增益为 1 return w.squeeze().real

参数说明:f 是频点(Hz);d 是麦克风间距(m);load 是加载量,0.01~0.1,越大越稳健但指向性越差;返回值 w 是两路实数权重,通常配合 2.1 的分数延迟一起用。这个权重在低频会显著放大噪声,实际部署时只在部分频段启用,低频让权平滑过渡,避免染色。

固定波束类型权重是否依赖数据双麦稳态增益稳健性首选场景
延迟求和 DSB不相关噪声 3 dB,扩散噪声低频趋近 0高,失配容忍度大第一版、麦间距小、硬件一致性一般
超指向 Superdirective否(设计时算好)低频指向性更好,整体 2~6 dB需对角加载,否则失配敏感麦间距小且以扩散噪声为主
自适应 GSC/MVDR理论最高,受模型误差限制低,需发散监控噪声方向稳定、算力富余

选型上还有自适应波束(MVDR/GSC),权重随数据变,理论上抑制能力最强,但双麦场景下语音抵消(self-cancellation)风险高,噪声突变时方向图会畸变,需要监控发散。固定波束的定位是"稳健前端":不指望它把噪声消到零,而是稳定提升几 dB,把更精细的活交给后面的单通道降噪。

2.3 固定波束不能处理什么:决定后级要不要接别的模块

固定波束有三个明显边界。一是目标方向必须已知且变化受限,说话人转头或走动时波束方向失配,增益直接从 3 dB 掉到负数。二是混响场景里直达声比例低,空间滤波收益有限,RT60 超过 600 ms 的房间建议配合去混响。三是两个说话人来自同一方向时,固定波束无法区分,这是单阵列的物理限制,只能靠声纹或分离模型。理解这些边界,才知道一个固定波束工程(自己写的还是拿来的)后面为什么通常还要接噪声估计、VAD、单通道降噪——固定波束负责"空间分集",剩下的交给"时间分集"。

3. 用 Python 跑通固定波束形成最小实现:从双通道 WAV 到增强语音

3.1 用 numpy 实现分数延迟的延迟求和波束

先给一个能直接跑的最小实现,输入是 16 kHz 双通道 WAV,输出是增强后的单通道。关键在于"分数延迟":双麦间距 2 cm、端射 0° 时,到达时间差约 58 µs,而 16 kHz 下一个采样点是 62.5 µs——不足一个采样点。整数移位会引入高频失真,所以用 sinc 插值做分数延迟。

import wave import numpy as np def read_stereo(path): with wave.open(path, 'rb') as wf: sr = wf.getframerate() raw = np.frombuffer(wf.readframes(wf.getnframes()), dtype=np.int16) return raw.reshape(-1, 2) / 32768.0, sr def frac_delay(x, shift): # 理想分数延迟的 sinc 插值;shift>0 表示信号向后移 n = np.arange(len(x)) kernel = np.sinc(n - shift) return np.convolve(x, kernel, mode='same') def delay_sum_beam(x, sr, d=0.02, theta_deg=0): c = 343.0 # 声速 m/s tau = d * np.cos(np.radians(theta_deg)) / c # 到达时间差 shift = tau * sr # 折算成采样点 # 参考路(mic0)延迟 shift,和 mic1 对齐后再平均 y = (frac_delay(x[:, 0], shift) + x[:, 1]) / 2.0 return y if __name__ == '__main__': x, sr = read_stereo('room_mic.wav') y = delay_sum_beam(x, sr, d=0.02, theta_deg=0) y = np.clip(y, -1.0, 1.0) out = (y * 32767).astype(np.int16) with wave.open('beam_out.wav', 'wb') as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sr) wf.writeframes(out.tobytes())

代码逻辑:read_stereo 按 int16 读入并归一化到 [-1,1];frac_delay 用 sinc 核卷积实现任意采样点延迟,kernel 在目标位置取峰值、在其它整数点过零,对带限信号是近似无损的;delay_sum_beam 算完到达时间差后,把先到的 mic0 向后延迟 shift,与 mic1 同相对齐再平均,顺序不能反,反了就变成相消。d 是麦克风间距(米),theta_deg 是声源相对阵列轴线的角度,0° 表示声源在 mic0→mic1 方向的前方;theta_deg=90° 时 shift 为 0,退化为普通平均。

真实的 DSP 工程很少在时域直接做这个卷积,更常见的是 STFT 域里按频点乘一个相位旋转 e^{-j2πfτ_m},等价的数学,计算量小得多。时域版本的好处是方便验证和理解:把输出和任一输入通道对拍,能清楚地听到低频噪声被压低、语音变"干"的效果。

3.2 麦克风间距与栅瓣约束:d 取 2cm 还是 4cm 的判定依据

双麦间距不是随便定的。固定波束的空间采样和时域采样一样有混叠:当 d > c/(2f) 时,某个非目标方向会出现和主瓣同增益的栅瓣。语音前端按采样率定上限:16 kHz 采样对应语音带宽 8 kHz,d ≤ 343/(2×8000) ≈ 2.14 cm;8 kHz 采样(电话带宽 4 kHz)时可放宽到 4.3 cm。实际选值如下表。

采样率有效带宽最大间距常见取值超限后果
16 kHz8 kHz2.14 cm1.5~2 cm4 cm 时约 4.3 kHz 起出现栅瓣
48 kHz按算法带宽定(语音只关心 8k)2.14 cm2 cm 或更小高频端旁瓣抬升
8 kHz4 kHz4.3 cm3~4 cm目标偏移时后侧噪声上涌

判断是否出现栅瓣,可以直接画方向图。指向 0° 的双麦 DSB,幅值响应是 |cos(πfd(1-cosθ)/c)|,用这个公式按角度扫描即可验证。

def dsb_response(theta_deg, f, d, c=343.0): # theta: 声源入射角;f: 频率;返回双麦 DSB 幅值(0~1) theta = np.radians(theta_deg) phase = np.exp(1j * 2 * np.pi * f * d * (1 - np.cos(theta)) / c) return np.abs(0.5 * (1.0 + phase))

用这个函数扫 0°~360°,频率取 1000、4000、8000,把 d=0.02 和 d=0.04 各画一组,能直观看到后者在 4 kHz 以上的旁瓣已经到 -3 dB 以内。方向图确认没问题之前,不要上硬件改结构。

3.3 BeamF.rar 这类工程包的参数解读顺序

像 BeamF.rar 这样以固定波束命名的资源包,解压后常见结构是:主程序(.m 或 .cpp)、配置脚本、测试音频、输出目录几类文件。拿到手先别急着跑,按下面顺序核对参数:

  1. 先看配置里的 fs、nch、mic_spacing、theta、frame_len 五个参数和你的阵列是否一致。多数"跑出来效果差"是配置与硬件不符,而不是算法问题。
  2. 再看权重是实数还是复数、按频点给还是按帧给。频域权重意味着内部做了 STFT,随机测试音频时要确保和算法帧长对齐。
  3. 最后看输出有没有参考对比文件。没有的话自己录一段双通道 WAV,按 3.1 的代码作为对照基线。

参数对照表(常见设置):

参数典型值说明
fs16000语音增强默认,PESQ 也按 16k 标准
nch2双麦面板或立体声文件
mic_spacing0.02 m必须 ≤ c/(2·fmax),见 3.2
theta端射;侧射 90° 适合方向无定义场景
frame_len51232 ms @16k,匹配常见 VAD/降噪
hop_len25650% 重叠,加窗后能量重建

参数说明:frame_len 决定频域分辨率,512 点 @16k 是约 31.25 Hz/线,足以区分说话人基频和前几个谐波;hop 取 50% 重叠是为了窗函数重建。把这些参数落成一份配置结构体,后面换硬件只需要改 mic_spacing 和 fs,主流程不动。如果包里给的是 .mat 权重文件,用 scipy.io.loadmat 读出来,先打印 shape 和数值范围,权重偏离 1/M 太多就要警惕是不是超指向系数配了过小的对角加载。

4. 双麦克风阵列和 es8311 音频编解码器电路:板级落地从仿真到可跑的四个检查点

4.1 固定波束对 ADC 电路的三个硬要求

理论推导假设两路信号同时采样、幅度一致、相位由声程差唯一决定。落到电路上是三条硬约束:一是两路 ADC 必须共享同一 MCLK 时钟域,I2S 帧边界对齐,否则采样时基不同步,波束直接失效;二是两路模拟增益要一致,幅度失配超过 ±1 dB 时,端射 DSB 的方向性下降,旁瓣变高;三是模拟链路相位一致,耦合电容、抗混叠滤波、走线长度差异都会折算成额外相位偏差,等效于目标角度被偏转。前两条容易理解,第三条在双麦克风阵列和 es8311 音频编解码器电路的组合里最常见。

4.2 es8311 是单 ADC 通道,双麦克风怎么接

es8311 常见的是单 ADC 通道的音频编解码器,一颗芯片只拾一路模拟麦克风。双麦方案有两种接法:

方案 A:两颗 es8311 各接一路麦克风。I2C 用 CSB 引脚区分地址,I2S 走 TDM 模式把两路数据塞进同一帧。成本低、器件通用,但要求主控支持至少两个槽位的 TDM,而且要保证两片在上电时序上同时完成配置,否则起播瞬间两路帧错位。

方案 B:前端换成多通道 ADC(如 es7243/es7210 这类双/四通道 ADC),es8311 只负责回放。帧对齐天然保证,模拟布局也更简单,适合对波束质量要求高的产品。

方案ADC 通道帧对齐难度器件成本适合场景
双 es8311 + TDM1×2 片高,靠软件对齐嵌入式低成本降噪板
多通道 ADC + es83112~4低,硬件固定会议设备、智能音箱原型

4.3 es8311 初始化顺序与两个必调参数

无论哪种接法,es8311 的初始化顺序一致:复位 → 时钟分频 → ADC 输入源选择 → ADC 增益 → 等待 MCLK/LRCK 稳定。下面代码展示写寄存器的框架和顺序,寄存器地址以你手上的芯片数据手册和驱动头文件为准,不要照搬网上版本。

static void es8311_wr(uint8_t reg, uint8_t val) { uint8_t buf[2] = { reg, val }; i2c_write(ES8311_I2C_ADDR, buf, 2); } void es8311_init(void) { es8311_wr(ES8311_SYS_RESET, 0x1F); /* 1: 软件复位 */ es8311_wr(ES8311_CLK_MANAGER1, 0x00); /* 2: 时钟分频,按 MCLK/fs 配 */ es8311_wr(ES8311_CLK_MANAGER2, 0x08); /* 3: MCLK/LRCK 比例,通常 256fs */ es8311_wr(ES8311_ADC_INPUT, 0x00); /* 4: 模拟输入通道选择 */ es8311_wr(ES8311_ADC_GAIN, 0x10); /* 5: ADC 数字增益,0dB 附近 */ }

参数说明:复位之后,时钟分频必须先于 ADC 使能,分频错误时 ADC 会输出恒定直流或高频噪声;ADC_GAIN 决定数字增益档位,两片 es8311 必须写相同的值,否则方向图不对称。初始化完成后用示波器量 MCLK 与 LRCK 波形,确认比例和抖动正常再进算法调试——这一步能省掉一半音频问题排查时间。

两个必调电路参数:

  • MICBIAS 偏置与耦合电容。驻极体麦克风的偏置从 MICBIAS 引脚经偏置电阻(常见 2.2 kΩ)接入,耦合电容 0.1~1 µF 隔直。两路电容必须同批次同容值,否则低频相位偏差会被波束放大成"低频发空"。
  • 通道增益校准。固件启动后放一段同一声源,分别统计两路 RMS,在 DSP 侧算补偿系数。若补偿量超过 3 dB,说明模拟电路有硬伤,不要靠算法硬扛。

4.4 用 I2S 帧对齐检查波束前级

装好板子后先别跑算法,录一段"拍手"或 1 kHz 正弦,在 I2S 回调里把两个槽位拼成一个双通道 buffer,检查两路峰值是否落在同一采样序号附近。偏差超过 1 个采样点(16 kHz 下 62 µs),优先查 TDM 槽位映射和 DMA 描述符链。

int16_t frame_cnt = 0; void mic_i2s_rx_cb(int16_t slot0, int16_t slot1, int16_t *out) { if ((frame_cnt & 0x01) == 0) { /* 两槽位映射到同一个 audio frame */ out[frame_cnt] = slot0; out[frame_cnt + 1] = slot1; } frame_cnt++; }

frame_cnt 的奇偶判断只是示意:如果回调里只拿到一个槽位的数据,说明 TDM 被配置成了单槽模式,另一路根本没进 DMA。对齐验证通过后再接固定波束算法,排错面就小很多。

提示:如果 TDM 槽位顺序和实际接线相反,波束方向会直接指向后方,症状是目标语音衰减而背后噪声清晰。这时该查的不是算法,而是槽位映射。

5. 验证固定波束形成的三个指标与一个后处理技巧:SNRI、PESQ、方向图

5.1 用三个客观指标验收固定波束

固定波束做完第一版,不要只靠耳朵。三个指标:

  1. SNR 提升(SNRI):分别用语音段 RMS 和静音段 RMS 算输入单通道与输出波束的 SNR,差值就是算法增益。双麦端射、说话人距离 30 cm、办公室噪声下,DSB 一般能拿到 3~8 dB。低于 3 dB 先检查是不是有一路麦克风被遮挡或增益失配。
  2. PESQ:用参考语音与处理后语音比较。Python 里可以用 pesq 包,注意两个输入要先对齐时延。
from pesq import pesq score = pesq(16000, ref, deg, 'wb') # ref: 干净参考, deg: 波束输出

注意:PESQ 的两个输入采样率必须一致,deg 比 ref 多出的前导延迟要先裁掉,否则分数会被时延系统性拉低。

  1. 方向图实测:把声源分别放在 0°、45°、90° 三个位置,各录一段固定内容,比较波束输出的 RMS 差。0° 与 90° 的差值是方向性指标,越接近理论方向图越好。

5.2 一个值得保留的工程技巧:波束输出再压一层维纳滤波

固定波束给完空间增益后,残余噪声仍随时变。常见做法是在波束输出后面再接轻量维纳滤波:用参考麦克风原始信号做噪声估计(端射下目标语音在参考路里能量弱,这个近似成立),波束输出当观测,两者比值做后验 SNR,映射成维纳增益,再乘回波束输出。

def wiener_post_filter(beam, ref, floor=0.1, ceil=10.0): xi = beam**2 / np.maximum(ref**2, 1e-10) xi = np.clip(xi, floor, ceil) # 限制先验SNR范围,防音乐噪声 gain = xi / (1.0 + xi) # 维纳增益映射 gain = np.convolve(gain, np.ones(5)/5, mode='same') return beam * gain

参数说明:floor 取 0.1 相当于增益下限约 -10.4 dB,防止静音段把噪声压到发闷;ceil 取 10 限制增益上限,避免语音段过冲;5 点平滑在 16 kHz、512 帧长下对应约 10 ms 时间常数,能显著减少增益抖动。这个后置滤波器不迭代、只依赖当前帧和参考路,放在 DSP 上成本极低,是我做双麦方案时每版必留的一级。把方向图函数和这级后处理都收进工具包,每次改间距或目标角度,先重画方向图再跑 PESQ,半小时内能定位是结构问题还是参数问题。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询