声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台

2026-10-072

用一只 Sipeed R6+1 麦克风阵列(6 个环形 + 1 个中心 MEMS 麦克风)在 Maixduino(K210) 上做声源方向检测:听到声音从哪来,水平舵机就转头朝向它,同时环形 LED 指出方向。一套"听声 → 定向 → 伺服转头 → LED 指示"的完整声学伺服闭环,全程只需 MaixPy(MicroPython),可脱离 IDE 上电自跑。

声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_1.webp

一、项目简介

用麦克风阵列当"耳朵",双舵机云台当"脖子",实现:

  • 声源定位:12 个方向分区 + 加权重心,算出声音的方位角;
  • 听声转头:声源在左,水平舵机转向左;在右,转向右;
  • 方向指示:环形 12 颗 SK9822 LED 亮蓝灯指向声源,LCD 显示声源热力图;
  • 防自激:舵机自己的电机声不会把自己"越叫越动"(高阈值 + 转向后静默锁定期 + 死区);
  • 独立运行:程序存成 main.py 上传 K210 Flash,上电自动跑,不用一直开 IDE。

项目特点是**"只听方向、只认声音"**:不依赖图像标定,闭环由"声源方位角 → 加权重心 → 舵机增量"直接完成。方向数据在串口实时可看,方便标定和调试。

声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_2.webp

二、功能特性

  • 开机即定位:麦克风阵列初始化,环形 LED 上电自检
  • 声源定向:12 分区 → 加权重心方位角(比峰值 bin 稳得多)
  • 听声转头:方位角 → 水平舵机角(0~180°,左/右对正)
  • 防抖死区:方向变化 < 5° 不转;能量门槛只响应"够响的持续人声"
  • 防自激:舵机转完进入 1.2s 静默锁定,忽略这段时间内的声音
  • 方向指示:环形 LED 蓝灯指方向;LCD 热力图;串口打印 az / pan
  • 独立运行:main.py 存进 Flash,上电自跑

三、硬件清单

部件 型号 / 参数 数量
主控 Maixduino(Sipeed,基于 K210:RISC-V 双核 400MHz + FPU,自带 5" LCD) 1
麦克风阵列 Sipeed R6+1(6 环形 + 1 中心 MSM261S4030H0 MEMS,环形 12×SK9822 LED,10 脚 FPC) 1
执行器 双舵机云台:水平 pan 180°、俯仰 tilt 180°(只用 90~180°) 2
连线 杜邦线/排针 若干
供电 阵列 5V(同 LED);舵机独立 5V 电源 + GND 共地 —

接线要点(本实机制坑后的正确接法):阵列信号直连 K210 的 GPIO,不是那排 A0A5 丝印(A0A5 是 ESP32 的脚,不是 K210 的!)。本机实测有效配置:

MIC_D0 → GPIO24   MIC_D1 → GPIO22   MIC_D2 → GPIO15   MIC_D3 → GPIO32
MIC_WS → GPIO21   MIC_BCK→ GPIO23   LED_DAT → GPIO12   LED_CLK → GPIO11
舵机: pan = GPIO13 (全0~180)     tilt = GPIO14 (只用90~180)

声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_3.webp声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_4.webp

声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_5.webp

四、系统方案与原理

1. 声源方向:为什么"峰值 bin"不稳,要用加权重心

MIC_ARRAY.get_dir() 返回 12 个分区(对应环形 360°)。单帧峰值 bin 在噪声下会 0↔8↔2↔3 乱跳(本机实测:同一个方向峰值 bin 到处跳)。改用加权重心(circular mean):

方位角 az = atan2( Σ v[i]·sin(2πi/12), Σ v[i]·cos(2πi/12) )   (0~360°)

峰值只取"哪个 bin 最响"做粗参考,加权重心给出连续稳定的方位角,再加一个 EMA 平滑(0.7 旧 + 0.3 新),方向从 180 这种散点收敛到很稳的值。

2. 面板竖放,是"平面阵列":左/右好测,正前最弱

R6+1 六个麦克风排在一个竖直圆上。平面阵列对**同一平面内(左/右)的方向分辨最好,对正前(垂直面板方向)**天生很弱。实测标定出左右两个稳定锚点:

正左 AZ ≈ 67    正右 AZ ≈ 258   (两者约相差 180°,符合"左/右互为反方向")

所以这套平台只负责左/右转头(配你左右两侧的麦克风列),竖直面板正前弱是平面阵列固有特性。

3. 方位角 → 舵机角

把 az 相对 AZ_LEFT 归一化成横向位置 lat(0=左、180=右),再映射到水平舵机角并夹在安全区间:

lat = ( (az - AZ_LEFT) % 360 )          // 取 ≤180 的弧
pan = 90 + (lat/180 - 0.5) × 2 × 70      // 夹在 [0,180]

4. 防自激:为什么必须"高阈值 + 锁定 + 死区"

最初舵机一转,它自己的电机声被阵列听到 → 又触发转向 → 越转越响。三个措施一起破回路:

  • 能量门槛 MIN_ENERGY:只响应"够响的持续人声",挡掉环境/舵机小幅噪声;
  • 转向后静默锁定期 LOCKOUT_MS:舵机转完 1.2s 内忽略麦克风——自己的声音被"堵在门外";
  • 方向死区 DEADBAND:方向变化 < 5° 不动,不抖。
    声源定位·听声转头平台:Maixduino + 6+1麦克风阵列 + 双舵机云台_image_6.webp

五、程序实现(完整可直接运行)

from Maix import MIC_ARRAY as mic
from machine import PWM
import time, math

# ===== 舵机(本机:pan=IO13 全量程,tilt=IO14 只用90~180)=====
pan  = PWM(0, freq=50, duty=0, pin=13)   # 水平
tilt = PWM(1, freq=50, duty=0, pin=14)   # 俯仰
PAN_LIM, TILT_LIM = (0, 180), (90, 180)
def angle2duty(deg): return 2.5 + deg/180.0*10.0      # 0~180° -> 2.5~12.5%占空比
def set_pan(deg):  pan.duty(angle2duty(max(PAN_LIM[0],  min(PAN_LIM[1], deg))))
def set_tilt(deg): tilt.duty(angle2duty(max(TILT_LIM[0], min(TILT_LIM[1], deg))))

# ===== 麦克风阵列(本机实测有效引脚)=====
mic.init(i2s_d0=24, i2s_d1=22, i2s_d2=15, i2s_d3=32,
         i2s_ws=21, i2s_sclk=23,
         sk9822_dat=12, sk9822_clk=11)

# ===== 标定:左/右方位锚点 =====
AZ_LEFT, AZ_RIGHT = 67, 258
PAN_GAIN    = 70     # 转头灵敏度
MIN_ENERGY  = 8      # 能量门槛(挡环境声/舵机声)
LOCKOUT_MS  = 1200   # 舵机转完静默锁定期(防自激)
DEADBAND    = 5      # 方向死区

def az_deg(d):                        # 加权重心方位角 0~360°
    x = y = 0.0
    for i in range(12):
        x += d[i]*math.cos(2*math.pi*i/12)
        y += d[i]*math.sin(2*math.pi*i/12)
    return (math.atan2(y, x)*180/math.pi + 360) % 360

def az_to_pan(az):                    # 方位角 -> 水平舵机角
    raw = (az - AZ_LEFT) % 360
    lat = raw if raw <= 180 else 360 - raw      # 0=左, 180=右
    a = 90 + round((lat/180 - 0.5)*2*PAN_GAIN)
    return max(PAN_LIM[0], min(PAN_LIM[1], a))

set_pan(90); set_tilt(90)             # 回中:水平90、俯仰90(平视)
pan_deg, ema = 90.0, 180.0
lock_until = 0

while True:
    now = time.ticks_ms()
    if now < lock_until:              # 静默锁定期:忽略,照常读避免卡缓冲
        mic.get_map()
        continue
    d = mic.get_dir(mic.get_map())
    if max(d) >= MIN_ENERGY:          # 只对"够响"持续声响应
        az = az_deg(d)
        diff = (az - ema + 540) % 360 - 180      # 循环平滑
        ema  = (ema + diff*0.3) % 360
        t = az_to_pan(ema)
        if abs(t - pan_deg) >= DEADBAND:         # 死区消抖 + 慢速趋近
            pan_deg = (pan_deg*0.5 + t*0.5)
            set_pan(pan_deg)
            lock_until = time.ticks_ms() + LOCKOUT_MS   # 转完锁定防自激
    mic.set_led(d, (0, 0, 255))       # 环形 LED 指方向(蓝)
    print("az", round(az,1), "pan", round(pan_deg))
    time.sleep_ms(120)

独立运行:MaixPy IDE 里 File → Save to device 存成 main.py,上电即自动运行,脱离 IDE。

六、调试实录与踩坑

  1. 固件里根本没有 mic 模块(最坑):v0.6.0 的普通/精简/with_lvgl 全系固件都没编声源定位的 mic_array。必须用 v0.6.2 标准完整版固件才有 MIC_ARRAY,也才支持 MaixPy IDE。
  2. 模块名是大写的:是 from Maix import MIC_ARRAY as mic,不是小写 from maix import mic_array。写小写就是 ImportError: no module named 'maix'。
  3. A0~A5 丝印在 ESP32 上,不在 K210 上:最初的迷茫全来自这里——那排 A 引脚是 ESP32 的地盘,外接麦克风阵列根本不该往那接,要直连 K210 的 GPIO(本机 24/22/15/32/21/23、LED 12/11)。
  4. init 一直返回 True、方向却全 0:mic.init(...) 只是"逻辑上把 GPIO 配成 I2S",不检查物理上是否真接了对的脚。接错脚时 init 照样"成功"、get_map 也有图,但 get_dir 恒 0。判断"接没接对"要看数据是不是随声源变,不能只看 init。
  5. 舵机信号没路由到脚上一个隐藏坑:machine.PWM(ch, freq=50, duty=0) 创建后舵机不动——因为没传 pin=,PWM 信号去了默认脚。正确是 PWM(0, freq=50, duty=0, pin=13),pin= 参数把通道路由到目标 GPIO(实测打印 [MAIXPY]PWM: channel=0, freq=50, duty=5.00)。duty 量程是 0~100 百分比。
  6. 没接电测舵机会误判"死区":一度以为俯仰有死区、0~180 不到底,其实是不小心没给舵机供电在测。接电后 pan/tilt 分别用通道 0/1 全行程扫描都顺畅——先确认供电再做死区结论。
  7. 死区误判的教训:怀疑某个舵机"有死区/不转"时,用"好用的通道去驱那个引脚、再用另一通道去驱好用的引脚"做交叉隔离,别急着改代码。

七、实测效果与数据

  • 方位角:同一方向连续测,峰值 bin 在 0↔8↔2↔3 乱跳,加权重心 az 稳定在 70±10 / 258±12 两个簇,标定可靠;
  • 转向:左喊 → 水平舵机转左,右喊 → 转右(反向就调换 AZ_LEFT/AZ_RIGHT);
  • 防自激:加 MIN_ENERGY=8 + LOCKOUT_MS=1200 + DEADBAND=5 后,舵机自身电机声不再触发自己。
    听声转头演示

八、下一步改进

  • 真正"只认人声":当前 DOA 只认方向、不认"是不是人声"。两条路:① 换带 speech/关键词唤醒 的固件,喊口令(如"小智")才转头——最可靠;② 用 audio 做音高(Voiced)检测,基频 85~300Hz 才响应——不换固件但较凑合。
  • 竖直面板正前方向弱:加前后遮挡/改立体阵列增强水平朝向分辨。
  • 视觉联动:麦克风阵判定方位 → 二哈2视觉细瞄 → 双舵机(甚至六舵机机械臂)锁定目标,参考本系列另一篇《二哈2 视觉跟随机器人》。

安全提醒:舵机用独立 5V 供电 + GND 共地,别用 K210 逻辑电(两舵一起转电流瞬时可达 1A+);上电前把舵机手动回中位。所用引脚/标定锚点均为本机实测示例,请按你的安装与麦克风朝向微调。

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(0)
- 没有更多了 -