Rekaman sensor jarang sekali bersih. Sensor getaran pada bantalan mesin pabrik, misalnya, merekam komponen 10 Hz yang kita minati bercampur derau yang menyebar di semua frekuensi. Cara paling terarah menyaringnya adalah di domain frekuensi: sinyal yang dicari terkonsentrasi pada beberapa bin, sedangkan derau terbagi tipis ke seluruh bin.

Pola kerjanya tiga langkah: transformasikan rekaman dengan np.fft.fft, nolkan koefisien pada bin yang tidak diinginkan, lalu kembalikan sinyalnya dengan np.fft.ifft. Sumbu frekuensinya dari np.fft.fftfreq seperti pada fftfreq dan Spektrum Frekuensi, mekanisme inversnya dari Dari DFT ke FFT; yang baru di sini hanya langkah tengahnya — menyunting spektrum sebelum di-invers.

Yang kita kerjakan pada chapter ini:

  • Membaca sebaran derau lebar-band pada magnitude spektrum
  • Menyaring derau dengan mask low-pass lalu mengembalikan sinyal via np.fft.ifft
  • Menakar trade-off ambang batas dan mengenali artefact pemotongan tajam
  • Alur yang sama dengan rfft/irfft untuk sinyal riil

Membaca Derau di Spektrum #

Kita siapkan rekaman latihan: sinus murni 10 Hz beramplitudo 1 — komponen getaran yang ingin dipertahankan — ditambah derau normal ber-standar deviasi 0.3 dari generator ber-seed 42. Sinyal murninya disimpan terpisah sebagai acuan; RMS selisih terhadap acuan itu menjadi patokan mutu hasil filtering.

python
import numpy as np

fs = 1000
t = np.arange(1000) / fs
murni = np.sin(2 * np.pi * 10 * t)             # komponen yang diinginkan
rng = np.random.default_rng(42)
rekam = murni + rng.normal(0, 0.3, t.size)     # rekaman sensor berderau
spec = np.fft.fft(rekam)
mag = np.abs(spec)
print("mag[10]  =", mag[10].round(1), "(bin sinyal 10 Hz)")
print("mag[300] =", mag[300].round(1), "(bin derau)")
print("mag[450] =", mag[450].round(1), "(bin derau)")
print("RMS derau:", np.sqrt(np.mean((rekam - murni) ** 2)).round(4))

Output yang Dihasilkan:

text
mag[10]  = 500.1 (bin sinyal 10 Hz)
mag[300] = 1.3 (bin derau)
mag[450] = 3.8 (bin derau)
RMS derau: 0.2967

Penjelasan blok di atas:

  1. Baris 4–6: murni menjadi acuan penilaian; rekam adalah input yang akan difilter.
  2. Baris 7–10: derau menyebar ke semua bin — bin 10 memuat sinyal dengan magnitude ~500 (skala (N/2) × amplitudo), bin derau hanya 1–4.
  3. Baris 11: RMS = akar rata-rata kuadrat selisih; 0.2967 mendekati standar deviasi derau 0.3 — inilah error yang ingin ditekan.

Komponen yang dicari ratusan kali lebih besar daripada isi bin derau. Derau seperti ini disebut derau lebar-band (broadband noise): energinya tersebar tipis di seluruh bin, sehingga saringan di domain frekuensi sangat cocok untuknya.

Menolkan Bin dan Mengembalikan dengan np.fft.ifft #

Sinyal menumpuk di bin 10 dan cerminnya di bin 990, sedangkan derau tersebar. Ide saringan low-pass: pertahankan hanya bin dengan |freq| ≤ 50 Hz, nolkan sisanya — mengalikan spektrum dengan mask boolean melakukan persis itu. Sebelum menyaring, pastikan rekaman bebas np.nan: satu sampel NaN membuat seluruh koefisien FFT menjadi NaN dan sinyal hasil ikut NaN semuanya — kondisi yang dibahas di NaN dan Infinity.

python
import numpy as np

fs = 1000
t = np.arange(1000) / fs
murni = np.sin(2 * np.pi * 10 * t)
rng = np.random.default_rng(42)
rekam = murni + rng.normal(0, 0.3, t.size)
spec = np.fft.fft(rekam)
freq = np.fft.fftfreq(t.size, d=1 / fs)
mask = np.abs(freq) <= 50                # low-pass: lewatkan |f| <= 50 Hz
bersih = np.real(np.fft.ifft(spec * mask))
rms_sebelum = np.sqrt(np.mean((rekam - murni) ** 2))
rms_sesudah = np.sqrt(np.mean((bersih - murni) ** 2))
print("RMS sebelum:", rms_sebelum.round(4))
print("RMS sesudah:", rms_sesudah.round(4))

Output yang Dihasilkan:

text
RMS sebelum: 0.2967
RMS sesudah: 0.0982

Mari kita bedah bagian pentingnya:

  1. Baris 9 (freq = np.fft.fftfreq(t.size, d=1 / fs)): sumbu frekuensi selaras dengan susunan bin spec; d = 1/fs membuat satuannya hertz.
  2. Baris 10 (mask = np.abs(freq) <= 50): mask boolean atas sumbu frekuensi. np.abs krusial karena fftfreq memuat frekuensi negatif.
  3. Baris 11 (bersih = np.real(np.fft.ifft(spec * mask))): perkalian broadcast menolkan 899 bin di luar mask; ifft mengembalikan sinyal, dan np.real mengambil bagiannya yang riil — residu imajiner ~10⁻¹⁶ dibuang, kehati-hatian yang sama seperti pada Dari DFT ke FFT.
  4. Baris 14–15: RMS turun dari 0.2967 ke 0.0982 — error menyusut sekitar tiga kali lipat.

Angka 0.0982 bukan nol karena derau di bawah ambang ikut lolos: dari 1.000 bin, 101 bin berada pada |freq| ≤ 50, dan derau pada bin-bin itu tetap tersimpan dalam sinyal hasil.

Memilih Ambang Batas #

Ambang 50 Hz tadi pilihan, bukan keharusan: ambang kecil menekan lebih banyak derau tetapi berisiko memotong komponen sinyal di dekatnya, ambang besar sebaliknya. Kita uji beberapa nilai pada rekaman yang sama:

python
import numpy as np

fs = 1000
t = np.arange(1000) / fs
murni = np.sin(2 * np.pi * 10 * t)
rng = np.random.default_rng(42)
rekam = murni + rng.normal(0, 0.3, t.size)
spec = np.fft.fft(rekam)
freq = np.fft.fftfreq(t.size, d=1 / fs)
for ambang in [15, 50, 150, 300]:
    bersih = np.real(np.fft.ifft(spec * (np.abs(freq) <= ambang)))
    rms = np.sqrt(np.mean((bersih - murni) ** 2))
    print(f"ambang {ambang:>3} Hz -> RMS sesudah {rms.round(4)}")

Output yang Dihasilkan:

text
ambang  15 Hz -> RMS sesudah 0.0507
ambang  50 Hz -> RMS sesudah 0.0982
ambang 150 Hz -> RMS sesudah 0.1732
ambang 300 Hz -> RMS sesudah 0.2403

Penjelasan blok di atas:

  1. Baris 8–9: spec dan freq dihitung sekali di luar loop; yang diulang hanya mask dan ifft — semua variasi memakai rekaman identik.
  2. Baris 10–11: tiap ambang menghasilkan sinyal bersih yang berbeda, dan RMS-nya dihitung terhadap murni yang sama.
  3. Baris 12: format {ambang:>3} meratakan angka ambang ke kanan agar kolomnya lurus.

Polanya: makin kecil ambang, makin banyak bin derau yang dinolkan, makin kecil RMS-nya — 15 Hz paling bersih dan komponen 10 Hz tetap utuh di dalam rentangnya. Namun ambang tidak boleh dipilih dari angka RMS saja; sifat pemotongannya juga menentukan mutu hasil.

Alur yang Sama dengan rfft dan irfft #

Rekaman sensor adalah sinyal riil, jadi setengah spektrum sudah cukup — penghematannya seperti dibahas pada rfft untuk Sinyal Riil. Alur filteringnya identik; yang berubah hanya sumbunya: np.fft.rfftfreq menyediakan frekuensi tak-negatif sehingga mask tidak perlu menangani sisi negatif.

python
import numpy as np

fs = 1000
t = np.arange(1000) / fs
murni = np.sin(2 * np.pi * 10 * t)
rng = np.random.default_rng(42)
rekam = murni + rng.normal(0, 0.3, t.size)
spek = np.fft.rfft(rekam)
freq_r = np.fft.rfftfreq(t.size, d=1 / fs)     # hanya frekuensi >= 0
bersih = np.fft.irfft(spek * (freq_r <= 50), n=t.size)
rms = np.sqrt(np.mean((bersih - murni) ** 2))
print("RMS sesudah (rfft/irfft):", rms.round(4))

Output yang Dihasilkan:

text
RMS sesudah (rfft/irfft): 0.0982

Uraian singkatnya:

  1. Baris 7–8: rfft menghasilkan 501 koefisien dan rfftfreq menyediakan sumbunya yang sama panjang — 0 sampai 500 Hz.
  2. Baris 9: mask tinggal satu kondisi positif; irfft menyusun sisi cermin secara internal dan mengembalikan float64 langsung, tanpa perlu np.real.
  3. RMS-nya 0.0982 — persis sama dengan alur ifft pada ambang 50 Hz karena bin yang dipertahankan sama.

Filter nol-kan-bin ini saringan paling sederhana; aplikasi serius memakai filter digital yang respons frekuensinya dirancang khusus (misalnya lewat modul scipy.signal). Tetapi pola tiga langkahnya — transformasi, sunting spektrum, invers — tetap sama.