聲源定位系統設計(二)——MUSIC算法以及Python代碼實現

聲源定位系統設計(二)——MUSIC算法以及Python代碼實現

一、前言

上篇博客中已經詳細介紹了聲源定位的一些概念以及MVDR波束形成法的原理,在本篇博客中,我將介紹另一種更爲精準的波束形成算法:MUSIC算法以及這兩種算法的Python代碼實現。

二、MUSIC算法

MUSIC(Multiple Signal Classification)算法的方法類似於MVDR算法,只是在最後計算的時候有些許不同。該算法建立在一下假設基礎上:
①、噪聲爲高斯分佈,每個麥克風之間的噪聲分佈爲隨機過程,之間相互獨立,空間平穩,即每個麥克風噪聲方差相等。
②、要求空間信號爲高斯平穩隨機過程,且與單元麥噪聲互不相干,相互獨立。
③、目標聲源數目小於陣列元數目。
④、陣元間隔不大於來波信號中頻率最高波長的1/2。
MUSIC算法是空間譜估計技術的代表之一,它利用特徵結構分析。其基本原理是將協方差矩陣進行特徵值分解。它通常把空間信號分爲兩種,一種是信號特徵向量對應的信號空間,另一種是噪聲向量對應的噪聲空間,利用噪聲空間和信號空間的正交性原理,構造空間譜函數進行搜索,從而預估出DOA信息。
按照上一篇博客的做法,陣列數據的協方差矩陣爲:
R=E[X(t)XH(t)]=ARSA+RN R=E[X(t)X^H(t)]=AR_SA+R_N
其中,RSR_SRNR_N分別爲信源的協方差矩陣和噪源的協方差矩陣。
通過對陣列協方差矩陣進行特徵值分解,將特徵值進行升序排列,其中有D個較大的特徵值,對應於聲源信號,而有M-D個較小的特徵值,對應於噪聲信號。
λi\lambda_i爲第i個特徵值,viv_i爲其對應的特徵向量,則:
Rvi=λivi Rv_i=\lambda_i v_i
λi=σ2\lambda_i=\sigma^2是R的最小特徵值,則:
Rvi=σ2vi,i=D+1,D+2,...,M Rv_i=\sigma^2v_i,i=D+1,D+2,...,M
R=ARSAH+σ2IR=AR_SA^H+\sigma^2I代入上式得:
σ2vi=(ARSAH+σ2I)vi \sigma^2v_i=(AR_SA^H+\sigma^2I)v_i
化簡可得:
ARSAHvi=0 AR_SA^Hv_i=0
由於AHAA^HA是滿秩矩陣,(AHA)1(A^HA)^{-1}存在,而RS1R_S^{-1}也存在,則上式同乘以RS1(AHA)1AHR_S^{-1}(A^HA)^{-1}A^H後變成:
RS1(AHA)1AHARSAHvi=0 R_S^{-1}(A^HA)^{-1}A^HAR_SA^Hv_i=0
於是有AHvi=0,i=D+1,D+2,...,MA^Hv_i=0,i=D+1,D+2,...,M
故可以用噪聲向量來求信號源的角度。先構造噪聲矩陣EnE_n
En=[vD+1,vD+2,...vM] E_n=[v_{D+1},v_{D+2},...v_M]
最後定義空間譜P(θ)P(\theta)
P(θ)=1aH(θ)EnEnHa(θ) P(\theta)=\frac{1}{a^H(\theta)E_nE_n^Ha(\theta)}
其中a爲上一篇博客中的方向導向向量,通過遍歷θ\theta,即可得到一個空間的功率譜,尋找其最值即可尋得DOA方向角。二維的估計也想同,增加一個遍歷的維度即可。

三、MVDR代碼實現

直接上代碼了:
我這裏用了一個16陣元的麥克風陣列信號採集板。設計兩層圓陣。

nmicro = 16
layers = 2
micros_every_layer = nmicro//layers
R = [0.082, 0.103]
theta_micro = np.zeros(nmicro)#所有麥克風陣元的角度
for layer in range(layers):
    theta_micro[micros_every_layer*layer:micros_every_layer*(layer+1)] = \
        2*np.pi/micros_every_layer*(np.arange(micros_every_layer)+0.5*layer)
#所有麥克風陣元的座標
pos = np.concatenate((np.stack([R[0] * np.cos(theta_micro[:8]), R[0] * np.sin(theta_micro[:8]), np.zeros(8)],axis = 1), \
    np.stack([R[1] * np.cos(theta_micro[8:]), R[1] * np.sin(theta_micro[8:]), np.zeros(8)],axis = 1)), axis=0)
#PyAudio的信號採集參數
CHUNK = 1600
FORMAT = pyaudio.paInt16
CHANNELS = 18
RATE = 16000
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
#最後求得的聲源位置
xr = 0
yr = 0
#遍歷的x和y,假設z爲固定深度1m
X_STEP = 20
Y_STEP = 20
x = np.linspace(-0.4, 0.4, X_STEP)
y = np.linspace(-0.4, 0.4, Y_STEP)
z = 1
def beamforming():
    global xr, yr, x, y
    while True:
        data = stream.read(1600)
        data = np.frombuffer(data, dtype=np.short)
        data = data.reshape(1600,18)[:,:16].T
        p = np.zeros((x.shape[0], y.shape[0]))#聲強譜矩陣
        #傅里葉變換,在頻域進行檢測
        data_n = np.fft.fft(data)/data.shape[1]# [16,1600]
        data_n = data_n[:, :data.shape[1]//2]
        data_n[:, 1:] *= 2
        #寬帶處理,對於50個不同的頻率都進行計算
        #r存儲每個頻率下對應信號的R矩陣
        r = np.zeros((50, nmicro, nmicro), dtype=np.complex)
        for fi in range(1,51):
            rr = np.dot(data_n[:, fi*10-10:fi*10+10], data_n[:, fi*10-10:fi*10+10].T.conjugate())/nmicro
            r[fi-1,...] = np.linalg.inv(rr)
        #MVDR搜索過程
        for i in range(x.shape[0]):
            for j in range(y.shape[0]):
                dm = np.sqrt(x[i]**2+y[j]**2+z**2)
                delta_dn = pos[:,0]*x[i]/dm + pos[:,1]*y[j]/dm
                for fi in range(1,51):
                    a = np.exp(-1j*2*np.pi*fi*100*delta_dn/340)
                    p[i,j] = p[i,j] + 1/np.abs(np.dot(np.dot(a.conjugate(), r[fi-1]), a))

        xr = np.argmax(p)//Y_STEP
        yr = np.argmax(p)%Y_STEP
        print(x[xr],y[yr])

        #轉爲強度0-1
        p /= np.max(p)
        繪製聲強圖
        x1, y1 = np.meshgrid(x,y)
        ax = plt.axes(projection='3d')
        ax.plot_surface(x1,y1,np.abs(p.T))
        plt.pause(0.01)

if __name__='__main__':
    while True:
        beamforming()

四、MUSIC算法代碼實現

陣列與MVDR相同

nmicro=16
layers = 2
micros_every_layer = nmicro//layers
R = [0.082, 0.103]
theta_micro = np.zeros(nmicro)#所有麥克風陣元的角度
for layer in range(layers):
    theta_micro[micros_every_layer*layer:micros_every_layer*(layer+1)] = \
        2*np.pi/micros_every_layer*(np.arange(micros_every_layer)+0.5*layer)
#所有麥克風陣元的座標
pos = np.concatenate((np.stack([R[0] * np.cos(theta_micro[:8]), R[0] * np.sin(theta_micro[:8]), np.zeros(8)],axis = 1), \
    np.stack([R[1] * np.cos(theta_micro[8:]), R[1] * np.sin(theta_micro[8:]), np.zeros(8)],axis = 1)), axis=0)
#PyAudio的信號採集參數
CHUNK = 1600
FORMAT = pyaudio.paInt16
CHANNELS = 18
RATE = 16000
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
#最後求得的聲源位置
xr = 0
yr = 0
#遍歷的x和y,假設z爲固定深度1m
X_STEP = 20
Y_STEP = 20
x = np.linspace(-0.4, 0.4, X_STEP)
y = np.linspace(-0.4, 0.4, Y_STEP)
z = 1
def beamforming():
    global xr, yr, ifplot, x, y
    while True:
        data = stream.read(1600)
        data = np.frombuffer(data, dtype=np.short)
        data = data.reshape(1600,18)[:,:16].T

        data_n = np.fft.fft(data)/data.shape[1]# [16,1600]
        data_n = data_n[:, :data_n.shape[1]//2]
        data_n[:, 1:] *= 2

        r = np.zeros((50, nmicro, nmicro-1), dtype=np.complex)
        for fi in range(1,51):
            rr = np.dot(data_n[:, fi*10-10:fi*10+10], data_n[:, fi*10-10:fi*10+10].T.conjugate())/nmicro
            feavec,_,_ = np.linalg.svd(rr)
            r[fi-1,...] = feavec[:, 1:]
            
        p = np.zeros((x.shape[0], y.shape[0]))
        for i in range(x.shape[0]):
            for j in range(y.shape[0]):
                dm = np.sqrt(x[i]**2+y[j]**2+z**2)
                delta_dn = pos[:,0]*x[i]/dm + pos[:,1]*y[j]/dm
                for fi in range(1,51):
                    a = np.exp(-1j*2*np.pi*fi*100*delta_dn/340)
                    p[i,j] = p[i,j] + 1/np.abs(np.dot(np.dot(np.dot(a.conjugate(), r[fi-1]), r[fi-1].T.conjugate()), a))

        #n = np.argmin(np
        xr = np.argmax(p)//Y_STEP
        yr = np.argmax(p)%Y_STEP
        print(x[xr],y[yr])

        p /= np.max(p)
        x1, y1 = np.meshgrid(x,y)
        ax = plt.axes(projection='3d')
        ax.plot_surface(x1,y1,np.abs(p.T))
        plt.pause(0.01)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章