時(shí)處理架構(gòu):大規(guī)模麥克風(fēng)陣列與三維聲場記錄實(shí)踐)
1. 從一顆芯片到一片聲場XMOS XCORE 到底解決了什么問題第一次接觸 XMOS XCORE 這個(gè)平臺是在一個(gè)做會議拾音的項(xiàng)目里。當(dāng)時(shí)客戶要求 16 顆數(shù)字麥克風(fēng)組成環(huán)形陣列實(shí)時(shí)輸出波束成形后的單路音頻端到端延遲壓到 20 毫秒以內(nèi)還要在設(shè)備端直接跑聲源定位。我第一反應(yīng)是用通用 DSP 或者帶 FPU 的 MCU 硬扛結(jié)果算了一輪就發(fā)現(xiàn)不對勁16 路 PDM 數(shù)據(jù)流同時(shí)進(jìn)來每路采樣率 3.072 MHz光是把這些比特流濾波抽取成 PCM再疊加延遲求和、自適應(yīng)濾波、方向估計(jì)單核根本吃不下任務(wù)之間的實(shí)時(shí)性還會互相踩踏。XMOS XCORE 就是在這種場景下進(jìn)入視野的。它不是一顆傳統(tǒng)意義上的“音頻 DSP”而是一顆多核、多線程、可編程的實(shí)時(shí)處理器每個(gè) tile 上跑多個(gè)邏輯核邏輯核之間用硬件調(diào)度器做確定性切換沒有操作系統(tǒng)搶占帶來的抖動。這一點(diǎn)對麥克風(fēng)陣列來說太關(guān)鍵了——陣列處理本質(zhì)上是“多路并行 嚴(yán)格同步 低延遲”而 XCORE 的架構(gòu)天生就是為這種負(fù)載設(shè)計(jì)的。這篇文章我想聊的不是一份產(chǎn)品手冊而是把 XCORE 支撐大規(guī)模麥克風(fēng)陣列這件事拆開講透它為什么能行、架構(gòu)上哪些點(diǎn)決定了它能行、實(shí)際做三維聲場記錄時(shí)怎么落地、以及那些文檔里不會寫但一定會踩的坑。適合正在做陣列拾音、空間音頻、聲場重建的工程師也適合剛接觸 XCORE、被“數(shù)字麥克風(fēng)陣列沒聲音”這類問題卡住的朋友??赐昴阒辽倌芘袛噙@個(gè)平臺適不適合你的項(xiàng)目以及如果上手第一步該干什么。2. 多核實(shí)時(shí)處理架構(gòu)XCORE 憑什么扛住大規(guī)模陣列2.1 邏輯核與硬件調(diào)度確定性才是陣列處理的命根子麥克風(fēng)陣列處理和普通音頻播放最大的區(qū)別在于通道間必須嚴(yán)格同步。16 顆麥克風(fēng)如果采樣時(shí)刻錯(cuò)開哪怕一個(gè)時(shí)鐘周期波束成形的相位關(guān)系就亂了指向性會直接崩掉。通用處理器上跑多線程線程調(diào)度由操作系統(tǒng)決定你永遠(yuǎn)無法保證 16 個(gè)采集任務(wù)在同一時(shí)刻對齊抖動是常態(tài)。XCORE 的做法是把“調(diào)度”這件事下沉到硬件。每個(gè) tile 上有若干個(gè)邏輯核logical core硬件調(diào)度器按固定周期在邏輯核之間輪轉(zhuǎn)每個(gè)邏輯核分到確定的時(shí)間片。這意味著每個(gè)邏輯核的執(zhí)行時(shí)機(jī)是可預(yù)測的最壞情況執(zhí)行時(shí)間WCET可以精確計(jì)算邏輯核之間通過通道channel通信通信本身也是硬件支持的不經(jīng)過內(nèi)存總線爭搶沒有緩存一致性問題沒有中斷延遲的不確定性。我實(shí)測過在 XCORE-200 上跑 8 路 PDM 采集 波束成形邏輯核切換的抖動在納秒級這個(gè)量級對音頻陣列來說完全夠用。你可以把它理解成一條流水線每個(gè)工位邏輯核在固定的節(jié)拍上干活節(jié)拍由硬件打不靠軟件搶。注意邏輯核數(shù)量是有限的一個(gè) tile 通常 8 個(gè)邏輯核兩個(gè) tile 就是 16 個(gè)。分配任務(wù)時(shí)要把計(jì)算量大的核和 I/O 密集的核錯(cuò)開別讓一個(gè)核既管采集又管濾波否則時(shí)間片會被撐爆。2.2 多 tile 互聯(lián)與數(shù)據(jù)流把“大規(guī)?!比齻€(gè)字落到實(shí)處“大規(guī)模麥克風(fēng)陣列”里的“大規(guī)模”通常指 16 路起步32、64 路也不罕見。XCORE 的多 tile 架構(gòu)就是為擴(kuò)展準(zhǔn)備的。每個(gè) tile 有自己的內(nèi)存和 I/Otile 之間通過 xCONNECT 鏈路互聯(lián)鏈路帶寬足夠支撐音頻數(shù)據(jù)流的搬運(yùn)。實(shí)際做 32 路陣列時(shí)我的分配策略是這樣的Tile 0 負(fù)責(zé)前 16 路 PDM 采集和初級抽取濾波Tile 1 負(fù)責(zé)后 16 路同時(shí)承擔(dān)波束成形的部分計(jì)算兩個(gè) tile 通過 xCONNECT 交換中間結(jié)果主控邏輯核做最終的聲場合成。這種劃分的好處是每個(gè) tile 的負(fù)載可控?cái)U(kuò)展通道數(shù)時(shí)基本是線性增加 tile而不是讓單核越來越吃力。相比之下單核 DSP 擴(kuò)到 32 路時(shí)你只能不斷優(yōu)化算法最后往往卡在內(nèi)存帶寬或者中斷響應(yīng)上。2.3 與通用方案的對比為什么不用 FPGA 或 ARM經(jīng)常有人問FPGA 不是更擅長并行嗎為什么選 XCORE我的看法是分工不同方案優(yōu)勢短板適合場景XMOS XCORE確定性調(diào)度、開發(fā)門檻低于 FPGA、原生支持 PDM/I2S算力上限不如高端 FPGA中小規(guī)模陣列、實(shí)時(shí)音頻前端FPGA極致并行、延遲極低開發(fā)周期長、音頻算法實(shí)現(xiàn)復(fù)雜超大規(guī)模陣列、定制協(xié)議ARM DSP生態(tài)成熟、算法庫豐富調(diào)度抖動、多路同步難通道數(shù)少、對延遲不敏感專用音頻 DSP功耗低、音頻外設(shè)全可編程性差、擴(kuò)展受限消費(fèi)級固定功能產(chǎn)品XCORE 的定位很清晰你要的是可編程的實(shí)時(shí)性又不想掉進(jìn) FPGA 的開發(fā)深坑。用 XC 語言基于 C 的擴(kuò)展寫邏輯核任務(wù)比寫 Verilog 做音頻濾波要快得多而且調(diào)試手段更接近軟件工程。3. 麥克風(fēng)陣列落地的核心細(xì)節(jié)從 PDM 到波束3.1 數(shù)字麥克風(fēng)接口PDM 采集的時(shí)鐘與數(shù)據(jù)對齊數(shù)字麥克風(fēng)陣列最常用的接口是 PDM脈沖密度調(diào)制一根時(shí)鐘線加一根數(shù)據(jù)線每個(gè)時(shí)鐘周期出一位。XCORE 的 I/O 口可以直接配置成 PDM 接收模式硬件負(fù)責(zé)在時(shí)鐘邊沿采樣數(shù)據(jù)位。關(guān)鍵參數(shù)是時(shí)鐘頻率和抽取比。以常見的 3.072 MHz PDM 時(shí)鐘為例抽取比 64 時(shí)得到 48 kHz 的 PCM 采樣率。計(jì)算關(guān)系是PCM 采樣率 PDM 時(shí)鐘頻率 / 抽取比 3.072 MHz / 64 48 kHz采集時(shí)最容易出問題的是多路麥克風(fēng)的時(shí)鐘相位。如果每路麥克風(fēng)用獨(dú)立的時(shí)鐘分頻器相位可能不一致。正確做法是用同一個(gè)時(shí)鐘源驅(qū)動所有麥克風(fēng)的時(shí)鐘線數(shù)據(jù)線各自獨(dú)立接到不同的輸入引腳。XCORE 的端口可以配置成“時(shí)鐘輸出 多路數(shù)據(jù)輸入”的模式保證所有麥克風(fēng)在同一時(shí)鐘邊沿采樣。提示布線時(shí) PDM 時(shí)鐘線要等長數(shù)據(jù)線盡量短否則高頻時(shí)鐘的反射會導(dǎo)致采樣錯(cuò)誤表現(xiàn)為某些通道噪聲大或者完全沒數(shù)據(jù)。3.2 抽取濾波與降采樣算力分配的第一道關(guān)口PDM 比特流不能直接用于波束成形必須先做低通濾波和降采樣把 1 位的高頻流變成多位的 PCM。這一步的計(jì)算量很大每路麥克風(fēng)每個(gè)輸出采樣點(diǎn)要做 64 次乘加對應(yīng) 64 倍抽取。XCORE 上實(shí)現(xiàn)抽取濾波有兩種思路用硬件資源做移位累加PDM 本質(zhì)是 1 位流濾波可以用簡單的滑動平均或者 CIC 濾波器只需要加法和移位不需要乘法器。這對邏輯核的算力消耗很小。用 DSP 指令做 FIR如果抽取比不是 2 的冪或者需要更陡的濾波特性就得用 FIR這時(shí)要占用乘法資源。我的經(jīng)驗(yàn)是第一級用 CIC 或者簡單積分梳狀濾波把采樣率降到一個(gè)中間值第二級再用 FIR 做精細(xì)濾波。這樣算力分配最合理邏輯核的時(shí)間片不會被第一級吃光。3.3 波束成形與聲源定位算法怎么映射到邏輯核波束成形的核心是延遲求和delay-and-sum根據(jù)聲源方向給每路麥克風(fēng)信號加上不同的延遲然后求和。延遲的精度直接決定波束的指向性。在 XCORE 上我通常這樣分配一個(gè)邏輯核專門負(fù)責(zé)計(jì)算延遲參數(shù)根據(jù)目標(biāo)方向查表或者實(shí)時(shí)計(jì)算若干個(gè)邏輯核并行做各路信號的延遲和累加一個(gè)邏輯核做求和后的后處理增益、均衡。聲源定位比如 GCC-PHAT 或者 SRP-PHAT計(jì)算量更大通常放在單獨(dú)的 tile 上跑或者降低更新率比如每 100 毫秒更新一次方向而不是每個(gè)采樣點(diǎn)都算。實(shí)操心得延遲參數(shù)用定點(diǎn)數(shù)表示小數(shù)延遲用分?jǐn)?shù)延遲濾波器實(shí)現(xiàn)。別用浮點(diǎn)XCORE 的浮點(diǎn)性能有限定點(diǎn)足夠滿足音頻精度。4. 三維聲場記錄從陣列數(shù)據(jù)到空間音頻4.1 聲場記錄的本質(zhì)捕捉空間信息而不只是聲音普通錄音記錄的是“某一點(diǎn)的壓力變化”而三維聲場記錄要捕捉的是空間中聲波的分布。這需要陣列具備兩個(gè)能力一是分辨不同方向來的聲音二是保留足夠的空間線索用于后續(xù)重建。麥克風(fēng)陣列的幾何布局決定了它能分辨的方向范圍。環(huán)形陣列在水平面分辨力好但垂直方向弱球形陣列能覆蓋全空間但通道數(shù)多、布線復(fù)雜。XCORE 的多 tile 架構(gòu)在這里的優(yōu)勢就體現(xiàn)出來了球形陣列動輒 32 路以上正好可以分?jǐn)偟蕉鄠€(gè) tile 上并行處理。4.2 數(shù)據(jù)流設(shè)計(jì)采集、處理、輸出的流水線一個(gè)完整的三維聲場記錄系統(tǒng)數(shù)據(jù)流大致是這樣的采集層所有 PDM 麥克風(fēng)同步采樣輸出原始比特流預(yù)處理層抽取濾波得到多路 PCM空間處理層波束成形、聲源定位、聲場分解比如 Ambisonics 編碼輸出層把處理結(jié)果打包成多通道音頻流通過 USB 或者 I2S 輸出。XCORE 的通道通信機(jī)制讓這個(gè)流水線可以拆成多個(gè)邏輯核串起來每個(gè)核只關(guān)心自己那一段數(shù)據(jù)通過通道傳遞。這種設(shè)計(jì)的好處是每一級的延遲可控整體延遲可以壓到很低。4.3 Ambisonics 編碼把陣列輸出變成可重建的聲場格式Ambisonics 是三維聲場記錄常用的格式它把聲場分解成一組球諧函數(shù)系數(shù)B-format。一階 Ambisonics 有 4 個(gè)通道W、X、Y、Z高階則更多。從麥克風(fēng)陣列到 Ambisonics 的轉(zhuǎn)換本質(zhì)是一個(gè)矩陣運(yùn)算每個(gè) Ambisonics 通道是各路麥克風(fēng)信號的加權(quán)和。權(quán)重取決于麥克風(fēng)的位置和方向。這個(gè)矩陣可以預(yù)先算好運(yùn)行時(shí)只需要做乘加。在 XCORE 上這個(gè)矩陣運(yùn)算可以分配到多個(gè)邏輯核并行做。比如 32 路麥克風(fēng)轉(zhuǎn) 16 通道三階 Ambisonics矩陣是 16×32每個(gè)輸出通道需要 32 次乘加總共 512 次乘加。分?jǐn)偟?8 個(gè)邏輯核每個(gè)核 64 次完全在時(shí)間片預(yù)算內(nèi)。注意Ambisonics 編碼對麥克風(fēng)的幅度和相位一致性要求很高。如果某路麥克風(fēng)增益偏差超過 1 dB重建的聲場會出現(xiàn)方向畸變。校準(zhǔn)這一步不能省。5. 常見問題與排查那些讓人抓狂的“沒聲音”5.1 數(shù)字麥克風(fēng)陣列沒聲音從時(shí)鐘查起“數(shù)字麥克風(fēng)陣列沒聲音”是搜索熱詞里出現(xiàn)頻率很高的問題。我排查過很多次絕大多數(shù)情況出在時(shí)鐘上。排查順序建議這樣確認(rèn) PDM 時(shí)鐘有沒有輸出用示波器量時(shí)鐘引腳沒有波形就是配置問題檢查端口配置和時(shí)鐘分頻確認(rèn)時(shí)鐘頻率對不對頻率偏差太大會導(dǎo)致抽取濾波后的采樣率不對表現(xiàn)為聲音變調(diào)或者完全靜音確認(rèn)數(shù)據(jù)線有沒有翻轉(zhuǎn)時(shí)鐘正常但數(shù)據(jù)線不動可能是麥克風(fēng)沒被正確使能檢查使能引腳和電源確認(rèn)抽取濾波參數(shù)匹配抽取比和時(shí)鐘頻率不匹配時(shí)輸出數(shù)據(jù)全是零或者噪聲?,F(xiàn)象可能原因排查方法完全沒聲音時(shí)鐘未輸出、麥克風(fēng)未使能示波器量時(shí)鐘和數(shù)據(jù)線部分通道沒聲音該路數(shù)據(jù)線虛焊、引腳配置錯(cuò)誤逐路檢查數(shù)據(jù)引腳聲音有噪聲時(shí)鐘抖動、電源紋波檢查時(shí)鐘源和電源濾波聲音變調(diào)采樣率不匹配核對時(shí)鐘頻率和抽取比通道間不同步時(shí)鐘相位不一致確認(rèn)共用時(shí)鐘源5.2 xmos 驅(qū)動相關(guān)問題枚舉失敗與通道映射在 USB 音頻應(yīng)用里xmos 驅(qū)動相關(guān)的問題也很常見。設(shè)備插上電腦后不識別或者識別了但通道數(shù)不對通常有幾個(gè)原因USB 描述符配置錯(cuò)誤通道數(shù)、采樣率、格式要和固件里的一致改了一處忘了另一處就會枚舉失敗驅(qū)動簽名問題某些系統(tǒng)對未簽名驅(qū)動有限制需要確認(rèn)驅(qū)動安裝狀態(tài)通道映射錯(cuò)位陣列有 16 路但驅(qū)動只認(rèn) 2 路檢查 USB 音頻描述符里的通道配置。我的做法是先用 USB 分析儀抓描述符確認(rèn)設(shè)備上報(bào)的配置和預(yù)期一致再排查驅(qū)動層。這一步能省掉大量猜測時(shí)間。5.3 實(shí)時(shí)性抖動邏輯核時(shí)間片被撐爆的信號如果系統(tǒng)跑著跑著出現(xiàn)周期性的爆音或者數(shù)據(jù)丟失大概率是某個(gè)邏輯核的時(shí)間片不夠用了。判斷方法是在邏輯核里加一個(gè) GPIO 翻轉(zhuǎn)用示波器看任務(wù)執(zhí)行時(shí)間如果執(zhí)行時(shí)間接近或超過時(shí)間片周期就需要拆分任務(wù)或者優(yōu)化算法常見的原因是某個(gè)濾波器的階數(shù)太高或者通道數(shù)增加后沒有重新分配負(fù)載。實(shí)操心得設(shè)計(jì)階段就要做算力預(yù)算把每個(gè)邏輯核的負(fù)載算清楚留 20% 余量。后期加功能時(shí)才不會捉襟見肘。6. 工程實(shí)踐中的幾點(diǎn)體會做陣列項(xiàng)目這些年我越來越覺得 XCORE 這類平臺的價(jià)值不在于單點(diǎn)性能多強(qiáng)而在于它把“實(shí)時(shí)性”這件事變得可設(shè)計(jì)、可預(yù)測。通用處理器上你是在和調(diào)度器博弈在 XCORE 上你是在設(shè)計(jì)一條流水線每個(gè)環(huán)節(jié)的時(shí)序都是你自己定的。另一個(gè)體會是陣列項(xiàng)目的成敗往往在硬件階段就決定了。麥克風(fēng)的選型、布局、時(shí)鐘布線、電源濾波這些做不好后面軟件再優(yōu)化也救不回來。我見過太多項(xiàng)目在算法上反復(fù)折騰最后發(fā)現(xiàn)是某幾路麥克風(fēng)的時(shí)鐘相位差了半個(gè)周期。最后分享一個(gè)調(diào)試小技巧在陣列前端加一個(gè)已知位置的測試聲源比如一個(gè)揚(yáng)聲器播放掃頻信號用采集到的多路信號反推延遲可以快速驗(yàn)證陣列的同步性和波束指向是否正確。這比盲調(diào)算法高效得多。