波束形成算法仿真:從CBF到MVDR的工程實踐)
簡介壓縮包內(nèi)共114個文件含23個Python腳本、73張仿真結(jié)果圖、17個編譯緩存文件及1份README說明文檔整體大小約9.11MB。項目聚焦波束形成典型算法仿真覆蓋延遲求和、最小方差無失真響應(yīng)MVDR、線性約束最小方差LCMV及自適應(yīng)波束形成等?;贜umPy/SciPy實現(xiàn)通過修改陣元數(shù)、陣元間距、信噪比、期望角度等參數(shù)生成極坐標(biāo)波束圖、熱力圖與方向圖直觀呈現(xiàn)不同算法的波束指向、旁瓣抑制和零點控制效果。仿真中還考慮了多徑效應(yīng)、非理想陣元等因素使結(jié)果更具參考價值。已有155人學(xué)習(xí)下載適合雷達(dá)、聲納、無線通信及生物醫(yī)學(xué)成像方向的初學(xué)者和研究人員作為算法驗證與教學(xué)演示工具。壓縮包內(nèi)附README說明結(jié)合生成圖像與Python源碼可快速復(fù)現(xiàn)不同條件下的仿真結(jié)果并為后續(xù)硬件實現(xiàn)或算法改進(jìn)提供參考。1. 波束形成算法仿真這個zip裝著哪些值得復(fù)現(xiàn)的東西一個寫著“基于Python實現(xiàn)的不同波束形成算法仿真”的zip包解壓以后通常不會有什么驚喜幾個py文件、幾張方向圖、一份說明沒了。但真正值錢的從來不是那幾張畫出來的花瓣狀方向圖而是從接收信號生成到權(quán)矢量求解、再到性能對比的整條鏈路。波束形成要解決的核心問題很集中——在來波方向未知或只知道粗估方向時怎么用一組陣元把目標(biāo)方向信號放大、把其他方向的干擾壓下去。用Python做這件事的優(yōu)點很明顯numpy處理復(fù)數(shù)矩陣運(yùn)算幾乎是零成本MATLAB能寫的公式在Python里一樣能寫還不用糾結(jié)許可證。這篇文章面向的是正在做陣列信號處理課程設(shè)計、需要對比幾種波束形成算法、或者剛接觸雷達(dá)/聲吶/5G上行接收想搞清CBF和MVDR差別的工程學(xué)習(xí)者。我會按“從數(shù)據(jù)生成到算法實現(xiàn)再到參數(shù)掃描和排錯”的順序講保證你看完能自己復(fù)現(xiàn)出方向圖和信干噪比曲線。2. 先搭信號模型用NumPy生成一組能用于波束形成的接收數(shù)據(jù)2.1 均勻線陣與半波長間距為什么d0.5λ是默認(rèn)值波束形成仿真的第一步不是寫波束形成器而是先造一組接收數(shù)據(jù)。我見過不少人直接拿隨機(jī)矩陣開跑結(jié)果方向圖畫出來主瓣很漂亮換一個入射角就完全亂掉——十有八九是數(shù)據(jù)生成環(huán)節(jié)的陣列模型沒建對。最常見的陣型是均勻線陣ULAN個全向陣元沿直線等間距排列間距為d。信號源假設(shè)在遠(yuǎn)場波前到達(dá)陣列時近似為平面波所以每個陣元收到的信號只有相位差幅度近似相等。這個相位差就是陣列流型向量steering vector的來源。以第一個陣元為參考點第n個陣元相對參考點的時延是 n·d·sinθ/c換算成相位就是 -j·2π·n·d·sinθ/λ其中θ是來波方向與陣列法線的夾角。這里最容易被忽略的是角度定義如果你把θ定義成和陣列軸向x軸的夾角那么相位差里的sinθ會變成cosθ畫出來的方向圖整體偏移90度。我習(xí)慣統(tǒng)一用“與法線的夾角”并且在代碼里用注釋寫死。陣元間距d取半波長這個不是拍腦袋定的。均勻線陣等效于對空間連續(xù)孔徑做離散采樣d λ/2時會出現(xiàn)柵瓣——除了真實來波方向外其他角度也會出現(xiàn)同樣的響應(yīng)峰。仿真里這個坑很隱蔽因為輸入只有一個方向時柵瓣位置的譜峰看起來也合理只有掃描全角度才會暴露。d小于半波長不會引入柵瓣但會增大陣元間相位差的分辨難度主瓣變寬所以默認(rèn)就是d λ/2。2.2 接收信號生成代碼窄帶信號、加性噪聲與快拍數(shù)數(shù)據(jù)生成這一塊我一般會把陣元位置、流型向量、快拍生成拆成三個函數(shù)后面所有算法都用這套接口。這樣換陣型比如改成均勻圓陣時不用動算法代碼。import numpy as np # 仿真參數(shù)陣元數(shù)、載頻、間距、快拍數(shù)、信噪比 N 8 # 陣元數(shù) fc 2.4e9 # 載頻單位Hz取2.4GHz只是示例 c 3e8 # 光速單位m/s d 0.5 * c / fc # 陣元間距取半波長約0.0625m snapshots 1024 # 快拍數(shù) snr_db 10 # 信噪比單位dB def array_positions(N, d): 沿x軸排列的均勻線陣第一個陣元在原點 return np.arange(N) * d def steering_vector(theta_deg, positions, wavelength): 計算theta_deg方向?qū)?yīng)的陣列流型向量 theta_deg: 來波方向與法線y軸的夾角單位度 positions: 陣元坐標(biāo)數(shù)組單位m wavelength: 波長單位m theta np.deg2rad(theta_deg) # 每個陣元相對參考點第一個陣元的相位差 return np.exp(-1j * 2 * np.pi * positions * np.sin(theta) / wavelength) def generate_snapshots(theta_deg, N8, snapshots1024, snr_db10): 生成一個遠(yuǎn)場窄帶信號的接收數(shù)據(jù) 返回X: 陣元數(shù)×快拍數(shù)的復(fù)數(shù)矩陣 返回a: 該方向?qū)?yīng)的流型向量 wavelength c / fc pos array_positions(N, d) a steering_vector(theta_deg, pos, wavelength) # 窄帶復(fù)信號實部虛部都是標(biāo)準(zhǔn)正態(tài)分布功率歸一化 s (np.random.randn(snapshots) 1j * np.random.randn(snapshots)) / np.sqrt(2) signal_power np.mean(np.abs(s) ** 2) # 約等于1.0 noise_power signal_power / (10 ** (snr_db / 10)) # 陣元間噪聲獨立復(fù)噪聲功率要均分給實部和虛部 noise np.sqrt(noise_power / 2) * ( np.random.randn(N, snapshots) 1j * np.random.randn(N, snapshots) ) # X a * s^T N信號從theta_deg方向入射 X a.reshape(-1, 1) * s.reshape(1, -1) noise return X, a # 生成一個來自10度方向的信號 X, a_true generate_snapshots(theta_deg10, NN, snapshots1024, snr_db10) print(X shape:, X.shape) # (8, 1024)這段代碼的要點在最后一行X的shape是(陣元數(shù), 快拍數(shù))后面CBF、MVDR、LCMV全部以它作為輸入。如果你習(xí)慣把快拍放第一維那所有矩陣運(yùn)算的轉(zhuǎn)置關(guān)系都要跟著改非常容易出錯。幾個參數(shù)值得展開說。N決定陣列的自由度和能同時抑制的干擾數(shù)量N個陣元理論上最多形成N-1個零陷也決定主瓣能做多窄。快拍數(shù)snapshots是每次采樣的點數(shù)后面估計協(xié)方差矩陣需要它取值太小會導(dǎo)致矩陣奇異這在第5章會單獨講。snr_db用的是信號功率反推噪聲功率的寫法這樣信噪比是嚴(yán)格按照功率定義的不會出現(xiàn)“設(shè)了10dB實際算出來只有7dB”的情況。2.3 信號、噪聲和信噪比怎么定標(biāo)仿真可信度的第一步復(fù)數(shù)信號功率的定標(biāo)是新手最容易翻車的地方。上面代碼里信號s是復(fù)高斯序列單個樣本功率|s|2的均值約等于1所以signal_power約等于1。噪聲是N×snapshots的復(fù)高斯矩陣每個元素功率的期望是noise_power。生成復(fù)噪聲時實部和虛部各分配noise_power/2所以要乘以sqrt(noise_power/2)。如果直接寫np.sqrt(noise_power)再乘randn實部虛部總功率就是noise_power的兩倍實際信噪比會比設(shè)定值低3dB。快拍數(shù)的取值有一個經(jīng)驗法則協(xié)方差矩陣是N×N的滿秩至少需要N個線性無關(guān)的快拍工程上一般取4N以上1024個快拍對8陣元來說已經(jīng)很充足。快拍數(shù)不夠時MVDR這類依賴矩陣求逆的算法會表現(xiàn)出兩個極端要么協(xié)方差奇異直接報錯要么矩陣勉強(qiáng)可逆但譜峰位置亂跳出現(xiàn)偽峰。后面第5章會詳細(xì)講這種現(xiàn)象的排查方法。到這里接收數(shù)據(jù)這塊就可以收住了。把這套generate_snapshots函數(shù)保存好后面所有算法對比和參數(shù)掃描都復(fù)用它能保證不同算法之間的差異只來自算法本身而不是因為換了隨機(jī)種子導(dǎo)致數(shù)據(jù)對不上。3. 三種常用波束形成算法CBF、MVDR、LCMV的Python實現(xiàn)與代價函數(shù)3.1 常規(guī)波束形成CBF掃描功率譜的Python實現(xiàn)常規(guī)波束形成是最直觀的做法把每個陣元的輸出加權(quán)求和權(quán)矢量就是某個方向的流型向量本身。它的邏輯是“我把陣列的接收方向?qū)?zhǔn)θ如果信號真的從θ來那各陣元經(jīng)過相位補(bǔ)償后應(yīng)該同相相加輸出功率最大”。輸出功率計算公式為P(θ) a^H(θ)·R·a(θ)其中R是接收數(shù)據(jù)的協(xié)方差矩陣。CBF的分辨率受瑞利限約束主瓣寬度大約為λ/(N·d)弧度對8陣元、半波長間距的陣列來說主瓣寬度在12度左右。兩個來波方向夾角小于這個值CBF在譜上就分辨不出來。它的優(yōu)勢是穩(wěn)健不涉及矩陣求逆協(xié)方差矩陣估計差點也不至于崩適合做所有對比實驗的基線。def cbf_spectrum(X, positions, wavelength, scan_range(-90, 91), step0.5): 常規(guī)波束形成掃描角度計算各方向輸出功率 X: 陣元數(shù)×快拍數(shù) positions: 陣元坐標(biāo)數(shù)組 wavelength: 波長 L X.shape[1] R X X.conj().T / L angles np.arange(scan_range[0], scan_range[1], step) spectrum [] for theta in angles: a steering_vector(theta, positions, wavelength) power a.conj() R a spectrum.append(power.real) return angles, np.array(spectrum) # 使用第2章生成的X angles, spec_cbf cbf_spectrum(X, array_positions(N, d), c / fc) print(CBF譜峰位置(度):, angles[np.argmax(spec_cbf)])這里的R X X.conj().T / L 是樣本協(xié)方差矩陣除以快拍數(shù)L是為了讓功率估計無偏。后面MVDR也用同一個R區(qū)別只在權(quán)矢量的求解方式。CBF不需要對R求逆所以即使快拍數(shù)很少畫出來的譜也不會報錯只是方差變大、旁瓣起伏變多。3.2 MVDR/Capon最小方差無失真響應(yīng)和它的代價函數(shù)MVDR的出發(fā)點比CBF講究我希望在期望方向增益固定為1無失真的前提下讓輸出總功率最小。輸出功率等于w^H·R·w所以最優(yōu)化問題寫成 min w^H R w約束 w^H a(θ) 1。這個約束保證期望方向信號增益為1最小化輸出功率則迫使算法自動在干擾方向壓低響應(yīng)——因為干擾功率是輸出功率的一部分想讓總功率小最優(yōu)解就會在干擾方向形成零陷。這個問題的閉式解是 w R?1a / (a^H R?1a)把解代回功率表達(dá)式得到輸出功率 P(θ) 1 / (a^H R?1a)。之所以叫Capon波束形成是因為Capon在1969年把它用到方位估計上。和CBF相比MVDR對協(xié)方差矩陣的準(zhǔn)確性極其敏感快拍數(shù)不足或者信噪比過高時R的估計誤差會被R?1放大。def mvdr_spectrum(X, positions, wavelength, scan_range(-90, 91), step0.5, diagonal_loading1e-6): MVDR/Capon波束形成掃描角度輸出功率譜 diagonal_loading: 對角加載系數(shù)緩解低快拍下的矩陣奇異 L X.shape[1] R X X.conj().T / L # 對角加載給R加上一個小單位陣避免求逆時奇異 R_loaded R diagonal_loading * np.eye(R.shape[0]) R_inv np.linalg.inv(R_loaded) angles np.arange(scan_range[0], scan_range[1], step) spectrum [] for theta in angles: a steering_vector(theta, positions, wavelength) power 1.0 / (a.conj() R_inv a) spectrum.append(power.real) return angles, np.array(spectrum) # 注意這里用MVDR會得到一個比CBF更窄的主瓣 angles, spec_mvdr mvdr_spectrum(X, array_positions(N, d), c / fc) print(MVDR譜峰位置(度):, angles[np.argmax(spec_mvdr)])代碼里默認(rèn)加了diagonal_loading1e-6這是我在項目里的習(xí)慣不是MVDR公式的一部分。理論上R滿秩時不需要加載但仿真中信號加噪聲的R在低快拍下特征值分布很病態(tài)求逆結(jié)果會被小特征值主導(dǎo)加一個很小的對角加載相當(dāng)于給特征值加了個下限譜估計穩(wěn)定很多。加載系數(shù)不能太大太大會讓MVDR退化成CBF分辨率優(yōu)勢消失。3.3 LCMV與對角加載約束方向變多時權(quán)矢量怎么解LCMV是MVDR的推廣。MVDR只約束一個方向增益為1LCMV允許你同時約束多個方向比如θ?方向增益為1主瓣θ?方向增益為0零陷。把約束條件寫成矩陣形式 C^H w f其中C的每一列是一個約束方向的流型向量f是對應(yīng)的期望響應(yīng)。最優(yōu)化問題變成 min w^H R w約束 C^H w f解是 w R?1C(C^H R?1C)?1f。LCMV適合的場景是已知干擾方向或者需要展寬零陷的情況。比如通信抗干擾里來波方向估計有誤差時你希望零陷不是一個點而是一個角度范圍那就在干擾方向附近多約束幾個相鄰角度的響應(yīng)為0。約束越多消耗的陣列自由度越多可用的干擾抑制自由度就減少所以不能無腦加約束。def lcmv_weights(R, C, f): LCMV最優(yōu)權(quán)矢量求解 R: 樣本協(xié)方差矩陣 C: 約束矩陣每列是一個約束方向的流型向量 f: 期望響應(yīng)向量長度等于約束數(shù) R_inv np.linalg.inv(R) # 拉格朗日乘子法閉式解 w R_inv C np.linalg.inv(C.conj().T R_inv C) f return w # 約束10度方向增益為1-20度方向增益為0 positions array_positions(N, d) C np.column_stack([ steering_vector(10, positions, c / fc), steering_vector(-20, positions, c / fc) ]) f np.array([1.0, 0.0]) R X X.conj().T / X.shape[1] w_lcmv lcmv_weights(R, C, f) print(LCMV權(quán)矢量維度:, w_lcmv.shape)注意這個代碼里直接對R求逆沒有加對角加載。實際用的時候我建議和MVDR一樣加上特別是約束數(shù)接近陣元數(shù)時C^H R?1C 本身就可能是病態(tài)的。另外LCMV做DOA估計不如MVDR方便因為你需要逐個假設(shè)來波方向去驗證約束是否成立它更多是被用在“方向已知要同時抑制多個干擾”的場合。4. 對比仿真與參數(shù)調(diào)優(yōu)陣元數(shù)、快拍數(shù)、對角加載對輸出的影響4.1 定一個帶干擾的對比場景主瓣、零陷和計算量差在哪單信號場景只能看出主瓣寬度差異看不出波束形成最核心的“抗干擾”能力。我通常在對比實驗里放兩個信號一個目標(biāo)信號從10度入射信噪比10dB一個干擾從-20度入射干噪比20dB。這樣MVDR必須先在-20度壓出零陷才能讓目標(biāo)方向輸出功率不被干擾抬高。def generate_two_source(theta_signal, theta_interf, N8, snapshots1024, snr_db10, inr_db20): 生成目標(biāo)干擾的接收數(shù)據(jù)干擾功率一般設(shè)得比信號大 wavelength c / fc pos array_positions(N, d) a_s steering_vector(theta_signal, pos, wavelength) a_i steering_vector(theta_interf, pos, wavelength) # 目標(biāo)信號和干擾獨立功率分別為1和10^(INR/10) s (np.random.randn(snapshots) 1j * np.random.randn(snapshots)) / np.sqrt(2) i (np.random.randn(snapshots) 1j * np.random.randn(snapshots)) / np.sqrt(2) i i * (10 ** ((inr_db - snr_db) / 20)) # 按INR和SNR的差值縮放干擾幅度 noise_power 1.0 / (10 ** (snr_db / 10)) noise np.sqrt(noise_power / 2) * ( np.random.randn(N, snapshots) 1j * np.random.randn(N, snapshots)) X (a_s.reshape(-1, 1) * s.reshape(1, -1) a_i.reshape(-1, 1) * i.reshape(1, -1) noise) return X, a_s, a_i X2, a_s, a_i generate_two_source(10, -20, NN, snapshots1024, snr_db10, inr_db20)這段代碼里縮放干擾用的是幅度關(guān)系功率比INR-SNR dB 10log10(幅度比2)所以幅度要乘10^((INR-SNR)/20)。很多人直接乘10^((INR-SNR)/10)功率就多了20倍干擾把目標(biāo)信號完全淹沒MVDR都救不回來。跑完三種算法后對比表一眼就能看出差異指標(biāo)CBFMVDRLCMV約束10°/零陷-20°主瓣寬度8陣元0.5λ約12°約6°窄且尖銳約12°受約束限制-20°干擾處響應(yīng)無零陷旁瓣級約-13dB自動形成深零陷可達(dá)-60dB約束強(qiáng)制零陷深度有限計算量掃描180°只需做矩陣乘法快每個角度都要算1/(a^H R?1a)慢只需解一次權(quán)矢量最快穩(wěn)健性低快拍不受影響需要對角加載需要對角加載MVDR之所以主瓣比CBF窄一倍是因為它通過R?1做了白化等效于把陣列孔徑“變長”。但窄主瓣不是白來的——代價是旁瓣起伏大尤其在干擾附近會出現(xiàn)凹槽狀起伏譜峰位置對R的估計誤差非常敏感。4.2 快拍數(shù)不足時MVDR的“偽峰”對角加載系數(shù)怎么掃把快拍數(shù)從1024降到16MVDR的譜會變得很難看除了真實方向外其他角度出現(xiàn)好幾個尖峰甚至真實方向的峰被淹沒。原因很直接16個快拍估計出來的協(xié)方差矩陣特征值分布嚴(yán)重偏離真實值R?1等價于放大了噪聲子空間里的小特征值這些被放大的分量在全角度掃描時隨機(jī)產(chǎn)生峰值。解決手段就是對角加載。問題是加載系數(shù)取多少太小沒用太大把特征值抹平MVDR退化成CBF。我的做法是掃對數(shù)間隔loading_values [0, 1e-8, 1e-6, 1e-4, 0.01, 0.1, 1.0] peak_error [] for loading in loading_values: angles, spec mvdr_spectrum(X2, array_positions(N, d), c / fc, diagonal_loadingloading) peak_idx np.argmax(spec) peak_error.append(abs(angles[peak_idx] - 10)) print(加載系數(shù) 譜峰誤差(度)) for load, err in zip(loading_values, peak_error): print(f{load:.1e} {err:.2f})掃完你會看到loading0時譜峰可能偏到十幾度甚至亂跳loading1e-6附近誤差最小且穩(wěn)定loading0.1以上主瓣開始變寬誤差反而增大。這個規(guī)律在8陣元、1024快拍的配置下很穩(wěn)定但換了陣元數(shù)和快拍數(shù)最優(yōu)加載系數(shù)會變所以要掃不要猜。4.3 陣元數(shù)、信噪比與分辨率的參數(shù)表與批量實驗陣元數(shù)對三個算法的影響有兩條線N越大主瓣越窄CBF和MVDR的分辨率都提升但N增大后MVDR對快拍數(shù)的要求也提高因為協(xié)方差矩陣的維度變大了。信噪比則直接影響MVDR的譜峰尖銳程度——SNR低時信號特征值接近噪聲特征值R?1白化能力變?nèi)踔靼暾箤?。陣元?shù)NCBF主瓣寬度MVDR主瓣寬度最小所需快拍數(shù)MVDR經(jīng)驗值4約26°約13°16以上8約12°約6°32以上16約6°約3°64以上批量實驗代碼就按不同N循環(huán)調(diào)用generate_two_source和mvdr_spectrum把譜峰的位置和主瓣寬度記錄下來。這套流程跑完你基本能掌握一個項目的核心數(shù)據(jù)在什么陣元數(shù)和快拍數(shù)下這幾種算法誰更值得用。5. 仿真避坑方向圖指向偏了90度、偽峰與噪聲定標(biāo)的5個真實問題5.1 現(xiàn)象角度指向整體偏了90度方向圖“翻車”了把steering_vector里的sin(theta)改成cos(theta)或者角度定義從“與法線夾角”改成“與陣列軸向夾角”你會看到譜峰不在10度而在80度附近而且看起來還挺像一個正常峰。原因就是相位差公式里三角函數(shù)的幾何關(guān)系搞反了。解決方法是統(tǒng)一約定均勻線陣法線方向是y軸來波方向用與法線的夾角。在steering_vector函數(shù)里只用sin(theta)不要混用cos。如果一定要用“與陣列軸向的夾角”那公式改成cos(theta)后全程序所有調(diào)用處都要保持一致。我見過最折騰的翻車案例是畫方向圖用了一套角度定義DOA估計又用另一套兩個結(jié)果差了90度卻各自看著都合理。5.2 現(xiàn)象MVDR在快拍數(shù)小于陣元數(shù)時直接報錯或出現(xiàn)偽峰快拍數(shù)L小于陣元數(shù)N時樣本協(xié)方差矩陣的秩最多只有L必然奇異np.linalg.inv直接拋LinAlgError。即使L略大于N矩陣條件數(shù)也可能超過1e12求逆結(jié)果被數(shù)值誤差主導(dǎo)譜峰隨機(jī)亂跳。這不是算法寫錯了是信息量不足。解決分兩步。第一步讓L至少大于N經(jīng)驗值取4N以上。第二步給R加上對角加載最小化數(shù)值誤差的影響。如果項目里快拍數(shù)確實受限比如雷達(dá)的相參積累時間有限那優(yōu)先考慮對角加載而不是硬上高分辨率算法。5.3 現(xiàn)象設(shè)置snr_db10但統(tǒng)計出的信干噪比明顯對不上這類問題通常不在波束形成器而在噪聲生成。復(fù)高斯噪聲的功率是實部功率加虛部功率如果生成噪聲時只乘了sqrt(noise_power)而沒有再除以sqrt(2)噪聲實際功率就是設(shè)定值的兩倍等效信噪比低3dB。還有干擾縮放時誤用功率倍數(shù)代替幅度倍數(shù)干擾功率會高出幾十倍。解決方法是寫一個自檢函數(shù)生成數(shù)據(jù)后用np.mean(np.abs(X)**2, axis1)統(tǒng)計每個陣元接收總功率再用信號流型向量做相關(guān)投影估計信號功率驗證和理論值的偏差在0.5dB以內(nèi)。這個檢查10行就能寫完值得加進(jìn)仿真流程。5.4 現(xiàn)象掃描范圍出現(xiàn)對稱的“鬼峰”像是柵瓣如果你的陣元間距不是0.5λ而是1.5λ方向圖上除了真實峰還會在對稱角度出現(xiàn)一樣的峰。因為空間采樣不滿足奈奎斯特條件d λ/2時相位差2π·d·sinθ/λ在sinθ超過某個閾值后會混疊。解決方法是把陣元間距改回λ/2或者在代碼里加一個斷言if d wavelength/2: raise ValueError。柵瓣在真實陣列里可以通過陣元方向圖抑制仿真里陣列流型用的是全向陣元柵瓣會完整暴露所以必須從源頭上規(guī)避。5.5 現(xiàn)象譜峰變寬以為是算法問題其實是掃描步長太粗掃描步長取5度時方向圖譜峰附近只有一兩個采樣點看起來峰很胖MVDR的窄主瓣優(yōu)勢被完全掩蓋。反過來步長取0.01度時掃描角度數(shù)量上萬MVDR每個角度都要做一次矩陣運(yùn)算仿真時間從秒級變分鐘級。解決方法是先粗掃定位峰的大致范圍再在峰附近±10度做細(xì)掃步長0.1度。CBF用0.5度步長就夠了MVDR建議至少0.2度否則-3dB主瓣寬度都測不準(zhǔn)。6. 從方向圖到信干噪比曲線一個能寫進(jìn)報告的驗證流程單次仿真只能說“算法能跑”要說“算法有效”需要統(tǒng)計意義上的性能曲線。我一般在最后做一步蒙特卡洛固定目標(biāo)方向10度和干擾方向-20度在SNR從-10dB掃到20dB的每個點上重復(fù)200次隨機(jī)實驗統(tǒng)計MVDR輸出的信干噪比SINR均值和理論最優(yōu)SINR對比。def mc_sinr(theta_signal10, theta_interf-20, snr_listrange(-10, 21, 5), repeats200, N8, snapshots1024): results {} for snr in snr_list: sinr_mvdr [] for _ in range(repeats): X2, a_s, a_i generate_two_source( theta_signal, theta_interf, NN, snapshotssnapshots, snr_dbsnr, inr_dbsnr 20) # MVDR權(quán)矢量含對角加載 R X2 X2.conj().T / snapshots R_loaded R 1e-6 * np.eye(N) a steering_vector(theta_signal, array_positions(N, d), c / fc) w np.linalg.inv(R_loaded) a / (a.conj() np.linalg.inv(R_loaded) a) sinr np.abs(w.conj() a_s) ** 2 / np.abs(w.conj() a_i) ** 2 sinr_mvdr.append(sinr) results[snr] np.mean(sinr_mvdr) return results注意這里SINR的計算只用了信號和干擾的流型向量乘以權(quán)值沒有把噪聲功率放進(jìn)去因為對比的是MVDR對干擾的抑制能力。如果你要看輸出端的總信噪比要把噪聲項補(bǔ)上w^H R_noise w。這個流程跑完你會得到一條SINR隨SNR變化的曲線MVDR在SNR大于0dB后基本逼近理論最優(yōu)值CBF則一直低一截。這就是波束形成算法仿真從“畫圖好看”到“數(shù)據(jù)可信”的關(guān)鍵一步。我自己做這類仿真有個習(xí)慣固定隨機(jī)種子把所有中間結(jié)果協(xié)方差矩陣、譜數(shù)據(jù)、權(quán)矢量存成npy文件方便回頭換參數(shù)重新分析。方向圖這種東西看得見但摸不著存了數(shù)據(jù)才有后悔藥。希望這份筆記能幫你在波束形成仿真上少走幾段彎路。本文還有配套的精品資源點擊獲取