วิธีเขียนตัวกรอง lowpass สำหรับสัญญาณตัวอย่างใน Python


16

ฉันมีสัญญาณที่สุ่มตัวอย่างแต่ละ 1 ns (1e-9 วินาที) และมีสมมุติ 1e4 คะแนน ฉันต้องการกรองความถี่สูงจากสัญญาณนี้ สมมติว่าฉันต้องกรองความถี่ที่สูงกว่า 10 MHz ฉันต้องการที่สำหรับความถี่ต่ำกว่าสัญญาณความถี่ cutoff จะถูกส่งผ่านไม่เปลี่ยนแปลง มันหมายถึงการเพิ่มขึ้นของตัวกรองจะเป็น 1 สำหรับความถี่ที่ต่ำกว่าความถี่การตัด ฉันต้องการระบุลำดับตัวกรอง ฉันหมายถึงตัวกรองคำสั่งแรกมีความชัน 20 เดซิเบล / ทศวรรษ (กำลังม้วนออก) หลังจากความถี่ตัดตัวกรองลำดับที่สองมีความลาดชัน 40 เดซิเบล / เด ธ หลังจากความถี่ตัดออกและอื่น ๆ รหัสประสิทธิภาพสูงเป็นสิ่งสำคัญ

คำตอบ:


19

การตอบสนองความถี่สำหรับตัวกรองที่ออกแบบโดยใช้ ฟังก์ชันbutterคือ:

การตอบสนองตัวกรอง Butterworth

แต่ไม่มีเหตุผลที่จะ จำกัด ตัวกรองให้อยู่ในการออกแบบตัวกรองแบบโมโนโทนิกอย่างต่อเนื่อง หากคุณต้องการลดทอนสัญญาณที่สูงขึ้นในแถบหยุดและแถบช่วงการเปลี่ยนภาพชันตัวเลือกอื่น ๆ ก็มีอยู่ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการระบุตัวกรองใช้iirdesingเห็นนี้ ดังที่แสดงโดยแผนการตอบสนองความถี่สำหรับการออกแบบเนยความถี่คัตออฟ (-3 เดซิเบล) อยู่ไกลจากเป้าหมาย สิ่งนี้สามารถลดลงได้โดยการสุ่มตัวอย่างก่อนการกรอง (ฟังก์ชั่นการออกแบบจะมีช่วงเวลาที่ยากลำบากด้วยตัวกรองแคบ ๆ เช่น 2% ของแบนด์วิดท์) ให้ดูที่การกรองอัตราตัวอย่างดั้งเดิมด้วย cutoff ที่ระบุ

import numpy as np
from scipy import signal
from matplotlib import pyplot as plt

from scipy.signal import fir_filter_design as ffd
from scipy.signal import filter_design as ifd

# setup some of the required parameters
Fs = 1e9           # sample-rate defined in the question, down-sampled

# remez (fir) design arguements
Fpass = 10e6       # passband edge
Fstop = 11.1e6     # stopband edge, transition band 100kHz
Wp = Fpass/(Fs)    # pass normalized frequency
Ws = Fstop/(Fs)    # stop normalized frequency

# iirdesign agruements
Wip = (Fpass)/(Fs/2)
Wis = (Fstop+1e6)/(Fs/2)
Rp = 1             # passband ripple
As = 42            # stopband attenuation

# Create a FIR filter, the remez function takes a list of 
# "bands" and the amplitude for each band.
taps = 4096
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

# The iirdesign takes passband, stopband, passband ripple, 
# and stop attenuation.
bc, ac = ifd.iirdesign(Wip, Wis, Rp, As, ftype='ellip')  
bb, ab = ifd.iirdesign(Wip, Wis, Rp, As, ftype='cheby2') 

ตัวกรองอัตราตัวอย่างดั้งเดิม

ดังที่ได้กล่าวไว้เนื่องจากเราพยายามกรองแบนด์วิดท์เล็กน้อยเช่นนี้ตัวกรองจะไม่มีทางลัดที่คมชัด ในกรณีนี้ตัวกรอง lowpass เราสามารถลดแบนด์วิดท์เพื่อให้ได้ตัวกรองที่ดูดีขึ้น สามารถใช้ฟังก์ชัน python / scipy.signal resample เพื่อลดแบนด์วิดท์

หมายเหตุฟังก์ชั่น resample จะทำการกรองเพื่อป้องกันนามแฝง Prefiltering ยังสามารถนำไปใช้งานได้ (เพื่อลด aliasing) และในกรณีนี้เราสามารถลองใหม่อีกครั้งโดย 100 และทำได้แต่คำถามที่ถามเกี่ยวกับการสร้างตัวกรอง สำหรับตัวอย่างนี้เราจะลดตัวอย่าง 25 และสร้างตัวกรองใหม่

R = 25;            # how much to down sample by
Fsr = Fs/25.       # down-sampled sample rate
xs = signal.resample(x, len(x)/25.)

หากเราอัปเดตพารามิเตอร์การออกแบบสำหรับตัวกรอง FIR การตอบสนองใหม่คือ

# Down sampled version, create new filter and plot spectrum
R = 25.             # how much to down sample by
Fsr = Fs/R          # down-sampled sample rate
Fstop = 11.1e6      # modified stopband
Wp = Fpass/(Fsr)    # pass normalized frequency
Ws = Fstop/(Fsr)    # stop normalized frequency
taps = 256
br = ffd.remez(taps, [0, Wp, Ws, .5], [1,0], maxiter=10000) 

การตอบสนองของตัวกรองที่ลดลง

ตัวกรองดำเนินการกับข้อมูลตัวอย่างต่ำลงมีการตอบสนองที่ดีขึ้น ประโยชน์อีกประการของการใช้ตัวกรอง FIR ก็คือคุณจะมีการตอบสนองแบบเฟสเชิงเส้น


1
ขอขอบคุณ. คุณสร้างกราฟคลื่นความถี่อย่างไร
Alex

ขอบคุณมากสำหรับคำตอบที่ยอดเยี่ยม! ฉันสงสัยว่าคุณสามารถอธิบายวิธีการใช้ตัวกรอง FIR ตามค่าสัมประสิทธิ์ที่คำนวณโดยใช้ Remez ได้หรือไม่? ฉันมีปัญหาในการเข้าใจสิ่งที่filtfiltต้องการสำหรับaพารามิเตอร์
ali_m

เมื่อคุณมีค่าสัมประสิทธิ์จากการออกแบบตัวกรอง ( สำหรับ FIR และสำหรับ IIR) คุณสามารถใช้ฟังก์ชั่นที่แตกต่างกันคู่ที่จะดำเนินการกรอง: lfilter , convolve , filtfilt โดยทั่วไปฟังก์ชั่นเหล่านี้จะทำงานคล้ายกัน: y = filtfilt (b, a, x) หากคุณมีตัวกรอง FIR เพียงตั้งค่า= 1 , xคือสัญญาณอินพุตbคือสัมประสิทธิ์ FIR โพสต์นี้ อาจช่วยได้เช่นกัน
Christopher Felton

5

มันใช้ได้ไหม?

from __future__ import division
from scipy.signal import butter, lfilter

fs = 1E9 # 1 ns -> 1 GHz
cutoff = 10E6 # 10 MHz
B, A = butter(1, cutoff / (fs / 2), btype='low') # 1st order Butterworth low-pass
filtered_signal = lfilter(B, A, signal, axis=0)

อย่างไรก็ตามเอกสารของคุณยังไม่สมบูรณ์ ดูเหมือนว่าbutterเป็นเสื้อคลุมสำหรับiirfilterซึ่งเป็นเอกสารที่ดีกว่า :

N: int ลำดับของตัวกรอง Wn: array_like ลำดับสเกลาร์หรือความยาว 2 ที่ให้ความถี่วิกฤต

อย่างไรก็ตามสิ่งนี้ส่วนใหญ่จะถูกโคลนจาก matlab ดังนั้นคุณสามารถดูเอกสารของพวกเขาได้เช่นกัน:

ความถี่ cutoff ปกติที่ Wn ต้องเป็นตัวเลขระหว่าง 0 ถึง 1 โดยที่ 1 สอดคล้องกับความถี่ Nyquist, πเรเดียนต่อตัวอย่าง

ปรับปรุง:

ฉันเพิ่มเอกสารสำหรับฟังก์ชั่นเหล่านี้ :) Github ทำให้ง่าย


1

ไม่แน่ใจว่าแอปพลิเคชันของคุณคืออะไร แต่คุณอาจต้องการตรวจสอบ Gnuradio: http://gnuradio.org/doc/doxygen/classgr__firdes.html

บล็อกการประมวลผลสัญญาณเขียนด้วย C ++ (แม้ว่ากราฟการไหลของ Gnuradio นั้นเป็น Python) แต่คุณบอกว่าประสิทธิภาพสูงเป็นสิ่งสำคัญ


1

ฉันได้ผลลัพธ์ที่ดีด้วยตัวกรอง FIR นี้ สังเกตว่ามันใช้ตัวกรองสองครั้งไปที่ "ไปข้างหน้า" และ "ย้อนกลับ" เพื่อชดเชยสัญญาณชดเชย ( filtfiltฟังก์ชั่นไม่ทำงานไม่รู้ว่าทำไม):

def firfilt(interval, freq, sampling_rate):
    nfreq = freq/(0.5*sampling_rate)
    taps =  sampling_rate + 1
    a = 1
    b = scipy.signal.firwin(taps, cutoff=nfreq)
    firstpass = scipy.signal.lfilter(b, a, interval)
    secondpass = scipy.signal.lfilter(b, a, firstpass[::-1])[::-1]
    return secondpass

ทรัพยากรที่ดีในการออกแบบตัวกรองและการใช้งานจากที่ผมเอารหัสนี้และจากการที่ผ่านแถบและ Hi-Pass ตัวอย่างกรองสามารถนำมาเป็นแบบนี้


ฉันไม่เชื่อว่ามีประโยชน์มากไปข้างหน้าและย้อนกลับกรองตัวกรอง FIR ตัวกรอง IIR สามารถได้รับประโยชน์จากการส่งต่อ / ย้อนกลับ (filtfilt) เพราะคุณสามารถรับเฟสเชิงเส้นจากตัวกรองเฟสที่ไม่ใช่เชิงเส้นโดยการกรองย้อนกลับ
Christopher Felton

2
@ChristopherFelton ฉันแค่ย้อนกลับเพื่อซิงโครไนซ์สัญญาณคลื่นไฟฟ้า RAW กับรุ่นที่ราบรื่นของตัวเอง ฉันรู้ว่าฉันสามารถเปลี่ยนสัญญาณได้ แต่การกรองสองครั้งกลายเป็นปัญหาน้อยลง เป็นที่น่าสังเกตว่าบัตรที่สองเกือบจะไม่เปลี่ยนบัตรผ่านแรกที่ผ่านการกรองไปแล้ว ... ขอบคุณที่สังเกต!
heltonbiker

อ่าใช่ ในการลบการหน่วงเวลา (การหน่วงเวลากลุ่ม) จุดดี
Christopher Felton

1

ฉันไม่มีสิทธิ์แสดงความคิดเห็น ...

@endolith: ผมใช้เช่นเดียวกับคุณยกเว้นใช้scipy.signal.filtfilt (B, A, x)ที่xคือเวกเตอร์การป้อนข้อมูลที่จะกรอง - เช่นnumpy.random.normal (ขนาด = (N)) filtfiltทำให้การส่งต่อและการย้อนกลับของสัญญาณ เพื่อความสมบูรณ์ (ส่วนใหญ่เหมือนกับ @endolith):

import numpy as np
import scipy.signal as sps

input = np.random.normal(size=(N)) # Random signal as example
bz, az = sps.butter(FiltOrder, Bandwidth/(SamplingFreq/2)) # Gives you lowpass Butterworth as default
output = sps.filtfilt(bz, az, input) # Makes forward/reverse filtering (linear phase filter)

filtfiltตามที่แนะนำโดย @heltonbiker ต้องการอาร์เรย์ของสัมประสิทธิ์ที่ฉันเชื่อ ในกรณีที่คุณจำเป็นต้องทำการกรอง bandpass ที่ baseband ที่ซับซ้อนจำเป็นต้องมีการกำหนดค่าที่เกี่ยวข้องเพิ่มเติม แต่นี่ไม่ได้เป็นปัญหาที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.