Penyaringan Data Sensor Arduino: Rata-Rata Bergerak, Median, EMA, dan Kalman
Penyaringan data sensor Arduino: rata-rata bergerak, filter median, EMA, histeresis, laju pengambilan sampel, perbandingan filter di Serial Plotter, dan pengantar filter Kalman.
Penyaringan data sensor Arduino berarti mengubah nilai yang berfluktuasi dan melonjak dari analogRead() atau sensor jarak menjadi angka stabil dan andal yang bisa digunakan proyekmu. Melihat 511, 513, 510, 514 di Serial Monitor padahal kamu tidak menyentuh potensiometer, HC-SR04 yang sesekali membaca 3 cm atau 400 cm tanpa alasan, lampu tidur yang berkedip pada batas antara gelap dan terang: semuanya merupakan bentuk dari masalah yang sama. Biasanya sensornya tidak rusak; derau listrik, galat langkah ADC, atau sesekali gema yang keliru masuk ke pembacaan. Kabar baiknya: filter beberapa baris bisa mengatasi sebagian besar masalah ini.
Dalam panduan ini, kamu akan terlebih dahulu melihat jenis derau dan tindakan awal yang perlu dilakukan pada rangkaian. Lalu kita akan menulis kode untuk lima metode, mulai dari yang paling sederhana: perataan beberapa sampel, rata-rata bergerak, filter median yang menghilangkan lonjakan, filter eksponensial (EMA) yang membutuhkan sangat sedikit memori, dan histeresis yang menstabilkan ambang. Kita akan membandingkan grafiknya di Serial Plotter Arduino IDE, membahas cara memilih laju pengambilan sampel, dan memberikan pengantar singkat filter Kalman. Bagian terakhir membahas ADC ESP32 yang berderau dan kode MicroPython untuk Raspberry Pi Pico.

Mengapa data sensor berderau?
Menganggap galat pengukuran sebagai satu jenis masalah saja bisa menyesatkan. Jenis derau yang berbeda membutuhkan filter yang berbeda, jadi pertama-tama kamu perlu mengetahui masalah yang kamu hadapi. Mencetak nilai mentah ke Serial Monitor selama beberapa waktu dan mengamati angkanya sudah cukup untuk itu. Jika nilai terus berfluktuasi di sekitar angka tertentu, kamu menghadapi derau acak; jika sebagian besar pembacaan benar tetapi sesekali muncul nilai yang sama sekali tidak masuk akal, kamu menghadapi lonjakan; jika nilai bergeser perlahan, masalahnya adalah pergeseran, bukan derau.
| Jenis derau | Bentuknya | Penyebab umum | Solusi yang sesuai |
|---|---|---|---|
| Derau acak | Nilai berfluktuasi ±2–10 hitungan | Riak catu daya, kabel panjang, derau ADC sendiri | Perataan, rata-rata bergerak, EMA |
| Lonjakan | Sesekali muncul nilai yang sangat menyimpang | Gema HC-SR04 tidak tertangkap, kontak longgar, gangguan motor | Filter median |
| Galat langkah (kuantisasi) | Nilai berganti antara dua angka berdekatan | Resolusi ADC (5 V / 1024 = 4,9 mV pada Uno) | Perataan, histeresis |
| Derau periodik | Riak teratur | Kedipan lampu 100 Hz, listrik PLN 50 Hz | Perataan selama kelipatan 20 ms |
| Pergeseran | Nilai berubah perlahan selama beberapa menit | Suhu, tegangan baterai menurun | Kalibrasi dan referensi stabil, bukan filter |
Rangkaian dahulu: kurangi derau dari sumbernya
Filter perangkat lunak tidak bisa mengembalikan informasi yang tidak pernah ditangkap rangkaian, jadi tugas pertama adalah mempersulit masuknya derau. Buat kabel sensor analog tetap pendek dan jauhkan dari kabel komponen yang membutuhkan arus besar seperti motor dan relay. Kapasitor keramik 100 nF antara input analog dan GND, yang dipasang dekat sensor, menyerap banyak gangguan cepat. Frekuensi cutoff filter lolos rendah RC yang terdiri dari resistor dan kapasitor dihitung dengan f = 1 / (2πRC): 10 kΩ dan 1 µF menghasilkan sekitar 16 Hz, sehingga perubahan yang lebih cepat diredam. Kamu bisa menghitung nilainya dengan alat konstanta waktu RC. Pada proyek motor, menghubungkan GND sensor ke board melalui jalur terpisah dari GND driver motor dan memberi daya ke board dari catu daya stabil juga membantu. Cara kerja ADC dan pengaruh tegangan referensi dijelaskan dalam Apa Itu ADC?.
Susunan pengujian
Kita akan mencoba kode dalam artikel ini dengan tiga sensor sederhana. Potensiometer ideal untuk melihat derau acak, LDR untuk derau periodik dan keputusan berdasarkan ambang, serta HC-SR04 untuk lonjakan. Hubungkan LDR antara 5 V dan pin analog serta resistor 10 kΩ antara pin analog dan GND, sehingga pembacaan meningkat ketika cahaya semakin terang. Untuk detailnya, lihat artikel sensor cahaya LDR dan potensiometer.
| Koneksi | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Kontak geser potensiometer | A0 | GPIO 34 | GP26 (ADC0) |
| Titik tengah pembagi LDR + 10 kΩ | A1 | GPIO 35 | GP27 (ADC1) |
| HC-SR04 TRIG | D9 | GPIO 5 | GP2 |
| HC-SR04 ECHO | D10 | GPIO 18 (dengan pembagi) | GP3 (dengan pembagi) |
| LED atau relay (contoh histeresis) | D8 | - | - |
| Catu daya | Potensiometer dan LDR 5V, HC-SR04 5V | Potensiometer dan LDR 3V3, HC-SR04 5V | Potensiometer dan LDR 3V3, HC-SR04 VBUS |
1. Perataan beberapa sampel
Filter paling sederhana adalah mengambil beberapa pembacaan berturut-turut dan menggunakan rata-ratanya. Derau acak menggeser setiap pembacaan ke arah yang berbeda, sehingga saling meniadakan dalam penjumlahan. Ada aturan statistik untuk ini: dalam rata-rata N pembacaan independen, derau berkurang sekitar √N kali. 4 sampel mengurangi derau menjadi setengah dan 16 sampel menjadi seperempat; 64 sampel menguranginya menjadi seperdelapan tetapi membutuhkan waktu empat kali lebih lama. Pada Arduino Uno, satu analogRead() membutuhkan sekitar 112 µs, jadi rata-rata 16 sampel selesai dalam kurang dari 2 ms. Kekurangan metode ini hanya membuat prosesor sibuk sebentar pada setiap pembacaan dan kurang efektif menghadapi lonjakan: jika salah satu dari 16 sampel tidak masuk akal, rata-ratanya juga menjadi keliru.
1const int POT_PIN = A0;
2
3// Art arda "adet" ölçüm al, ortalamasını döndür / take "adet" readings in a row, return the average
4int ortalamaOku(int pin, int adet) {
5 long toplam = 0; // taşmasın diye long / long to avoid overflow
6 for (int i = 0; i < adet; i++) {
7 toplam += analogRead(pin);
8 delayMicroseconds(100); // ölçümler arasında kısa ara / short gap between readings
9 }
10 return toplam / adet;
11}
12
13void setup() {
14 Serial.begin(115200);
15}
16
17void loop() {
18 int ham = analogRead(POT_PIN);
19 int ort4 = ortalamaOku(POT_PIN, 4);
20 int ort16 = ortalamaOku(POT_PIN, 16);
21 // Serial Plotter için etiket:değer biçimi / label:value format for the Serial Plotter
22 Serial.print("Ham:");
23 Serial.print(ham);
24 Serial.print(",Ort4:");
25 Serial.print(ort4);
26 Serial.print(",Ort16:");
27 Serial.println(ort16);
28 delay(20);
29}2. Rata-rata bergerak
Rata-rata bergerak menyimpan rata-rata N pembacaan terakhir dan, pada setiap pembacaan baru, menghapus yang terlama lalu menambahkan yang baru. Dengan begitu, kamu mengambil satu pembacaan per perulangan tetapi tetap mendapatkan manfaat rata-rata N pembacaan. Alih-alih menjumlahkan N angka setiap kali, kode menyimpan variabel sum: nilai yang keluar dikurangkan dan nilai yang masuk ditambahkan. Jadi, perhitungan berjalan dengan kecepatan yang sama berapa pun ukuran jendelanya. Konsekuensi rata-rata bergerak adalah keterlambatan: output tertinggal sekitar (N−1)/2 pembacaan dari nilai sebenarnya. Jendela 10 pembacaan dengan pengambilan sampel setiap 10 ms tertinggal sekitar 45 ms; ini tidak menjadi masalah untuk potensiometer atau suhu, tetapi terasa pada robot yang bergerak cepat.
1const int POT_PIN = A0;
2const int PENCERE = 10; // son 10 ölçüm / last 10 readings
3
4int tampon[PENCERE]; // dairesel tampon / circular buffer
5int sira = 0;
6long toplam = 0;
7bool dolu = false;
8
9int hareketliOrtalama(int yeni) {
10 toplam -= tampon[sira]; // en eski değeri çıkar / remove the oldest value
11 tampon[sira] = yeni;
12 toplam += yeni; // yeni değeri ekle / add the new value
13 sira = (sira + 1) % PENCERE;
14 if (sira == 0) dolu = true;
15 return dolu ? toplam / PENCERE : toplam / sira; // ilk turda bölen küçük / smaller divisor at start
16}
17
18unsigned long sonOlcum = 0;
19
20void setup() {
21 Serial.begin(115200);
22}
23
24void loop() {
25 if (millis() - sonOlcum >= 10) { // 100 Hz örnekleme / 100 Hz sampling
26 sonOlcum = millis();
27 int ham = analogRead(POT_PIN);
28 int filtreli = hareketliOrtalama(ham);
29 Serial.print("Ham:");
30 Serial.print(ham);
31 Serial.print(",Hareketli:");
32 Serial.println(filtreli);
33 }
34}| Jendela (N) | Pengurangan derau | Keterlambatan (satu pembacaan setiap 10 ms) | Cocok untuk |
|---|---|---|---|
| 4 | Sekitar 2× | 15 ms | Kendali yang membutuhkan respons cepat |
| 10 | Sekitar 3× | 45 ms | Potensiometer, joystick, LDR |
| 32 | Sekitar 5,7× | 155 ms | Suhu, kelembapan, tegangan baterai |
| 100 | 10× | 495 ms | Nilai yang berubah sangat lambat, output layar yang stabil |
3. Filter median: menghilangkan lonjakan
Perataan kurang efektif menghadapi lonjakan. Jika HC-SR04 membaca 50 cm empat kali lalu sekali gagal menangkap gema dan membaca 0, rata-rata kelimanya adalah 40 cm; nilai itu tidak benar dan juga tidak menunjukkan adanya galat. Filter median mengurutkan N pembacaan terakhir dari yang terkecil hingga terbesar lalu mengambil nilai tengahnya. Dalam contoh yang sama, daftar yang sudah diurutkan adalah 0, 50, 50, 50, 50 dan nilai tengahnya 50; pembacaan keliru tidak pernah masuk ke hasil. Filter median 5 elemen sepenuhnya menghilangkan hingga 2 pembacaan keliru berturut-turut. Sifat baik median yang kedua adalah tidak mengaburkan perubahan nyata yang mendadak seperti rata-rata: ketika rintangan benar-benar mendekat, nilai langsung beralih ke level baru dalam langkah yang jelas setelah beberapa pembacaan, bukan membentuk kurva landai. Kode di bawah mengambil pembacaan HC-SR04 setiap 60 ms dan mencetak median 5 pembacaan terakhir. Untuk detailnya, lihat artikel sensor jarak HC-SR04.
1const int TRIG_PIN = 9;
2const int ECHO_PIN = 10;
3const int N = 5; // medyan penceresi, tek sayı olmalı / median window, must be odd
4
5int olcumler[N];
6int sira = 0;
7
8int mesafeOlc() {
9 digitalWrite(TRIG_PIN, LOW);
10 delayMicroseconds(2);
11 digitalWrite(TRIG_PIN, HIGH);
12 delayMicroseconds(10);
13 digitalWrite(TRIG_PIN, LOW);
14 unsigned long sure = pulseIn(ECHO_PIN, HIGH, 25000); // ~4 m zaman aşımı / ~4 m timeout
15 return sure / 58; // yankı yoksa 0 / 0 if no echo
16}
17
18int medyan() {
19 int kopya[N];
20 for (int i = 0; i < N; i++) kopya[i] = olcumler[i];
21 for (int i = 1; i < N; i++) { // eklemeli sıralama / insertion sort
22 int deger = kopya[i];
23 int j = i - 1;
24 while (j >= 0 && kopya[j] > deger) {
25 kopya[j + 1] = kopya[j];
26 j--;
27 }
28 kopya[j + 1] = deger;
29 }
30 return kopya[N / 2]; // ortadaki eleman / middle element
31}
32
33void setup() {
34 Serial.begin(115200);
35 pinMode(TRIG_PIN, OUTPUT);
36 pinMode(ECHO_PIN, INPUT);
37 for (int i = 0; i < N; i++) { // tamponu ilk ölçümlerle doldur / fill the buffer first
38 olcumler[i] = mesafeOlc();
39 delay(60);
40 }
41}
42
43void loop() {
44 int ham = mesafeOlc();
45 olcumler[sira] = ham;
46 sira = (sira + 1) % N;
47 Serial.print("Ham:");
48 Serial.print(ham);
49 Serial.print(",Medyan:");
50 Serial.println(medyan());
51 delay(60); // HC-SR04 için en az 60 ms / at least 60 ms for HC-SR04
52}4. Filter eksponensial (EMA): perataan dengan sedikit memori
Rata-rata bergerak eksponensial (EMA) tidak menggunakan buffer; filter ini hanya mengingat hasil sebelumnya. Pada setiap pembacaan baru, hasil bergerak mendekati pembacaan sebesar sebagian kecil dari selisih keduanya: y = y + α × (x − y). Di sini x adalah pembacaan baru, y nilai yang sudah difilter, dan α (alfa) koefisien antara 0 dan 1. Semakin kecil α, semakin stabil tetapi lambat filternya. Disebut eksponensial karena pengaruh setiap pembacaan berkurang secara eksponensial seiring waktu. Ini adalah padanan perangkat lunak dari filter lolos rendah RC dalam elektronika. Konstanta waktu EMA kira-kira τ ≈ T / α (T adalah interval sampel): dengan pembacaan setiap 10 ms dan α = 0,1, filter mencapai 63 persen dari perubahan mendadak dalam sekitar 100 ms dan 95 persen dalam sekitar 300 ms. Kode memiliki versi bilangan pecahan dan versi bilangan bulat yang berjalan lebih cepat pada board kecil seperti Uno; versi bilangan bulat menggunakan α = 1/16 dan operasi geser (>>) sebagai pengganti pembagian.
1const int LDR_PIN = A1;
2const float ALFA = 0.1; // 0-1 arası; küçük = daha sakin / 0-1; smaller = smoother
3
4float ema = -1; // henüz başlamadı / not started yet
5long emaX16 = -1; // tamsayılı EMA x16 / integer EMA times 16
6
7unsigned long sonOlcum = 0;
8
9void setup() {
10 Serial.begin(115200);
11}
12
13void loop() {
14 if (millis() - sonOlcum >= 10) {
15 sonOlcum = millis();
16 int ham = analogRead(LDR_PIN);
17
18 if (ema < 0) ema = ham; // ilk değerle başlat / start from the first reading
19 ema += ALFA * (ham - ema); // y = y + a(x - y)
20
21 if (emaX16 < 0) emaX16 = (long)ham << 4;
22 emaX16 += ham - (emaX16 >> 4); // alfa = 1/16, float yok / alpha = 1/16, no float
23
24 Serial.print("Ham:");
25 Serial.print(ham);
26 Serial.print(",EMA:");
27 Serial.print(ema, 1);
28 Serial.print(",EMA_int:");
29 Serial.println(emaX16 >> 4);
30 }
31}| α (alfa) | Perkiraan jendela ekuivalen | Waktu respons 63% (pengambilan sampel 10 ms) | Karakteristik |
|---|---|---|---|
| 0.5 | 3 | 10–20 ms | Sangat cepat, sedikit perataan |
| 0.2 | 9 | 50 ms | Cepat, perataan ringan |
| 0.1 | 19 | 100 ms | Seimbang, titik awal yang baik untuk sebagian besar sensor |
| 0.05 | 39 | 200 ms | Stabil, lambat |
| 0.01 | 199 | 1 s | Sangat stabil, hanya untuk nilai yang berubah lambat |
“Jendela ekuivalen” dalam tabel adalah perkiraan ukuran rata-rata bergerak yang memberikan perataan sama dan dihitung sebagai N ≈ 2/α − 1. Keunggulan terbesar EMA adalah memori: rata-rata bergerak 100 elemen membutuhkan RAM 200 byte, sedangkan EMA hanya membutuhkan satu variabel. Jika kamu perlu memfilter beberapa sensor dalam RAM 2 KB Arduino Uno, perbedaan ini berarti. Kekurangannya adalah sama lemahnya dengan perataan dalam menghadapi lonjakan; lonjakan besar memengaruhi hasil selama beberapa pembacaan.
Membandingkan filter di Serial Plotter
Perbedaan antarfilter sulit dilihat dari angka, tetapi langsung tampak pada grafik. Kode di bawah membaca potensiometer pada 100 Hz, melewatkan pembacaan yang sama melalui empat jalur berbeda, dan mencetak semuanya dalam format label:value. Agar perbedaannya lebih jelas, kode menambahkan lonjakan buatan pada sekitar 3 persen pembacaan; hapus baris ini dalam proyek nyata. Pada grafik, kamu akan melihat median menghilangkan lonjakan sepenuhnya, sedangkan rata-rata bergerak dan EMA mengubahnya menjadi tonjolan kecil.
- Upload kode dan tutup Serial Monitor jika terbuka; Serial Monitor dan Serial Plotter tidak bisa dibuka bersamaan.
- Di Arduino IDE, buka Tools > Serial Plotter atau klik ikon grafik di kanan atas.
- Atur kecepatan di kanan bawah ke 115200 baud.
- Putar potensiometer perlahan lalu cepat, dan amati respons setiap garis.
- Gunakan kotak di sebelah label untuk menampilkan atau menyembunyikan garis dan membandingkan dua filter secara langsung.
1const int POT_PIN = A0;
2
3// Hareketli ortalama / moving average
4const int PENCERE = 10;
5int maTampon[PENCERE];
6int maSira = 0;
7long maToplam = 0;
8
9int hareketliOrtalama(int yeni) {
10 maToplam += yeni - maTampon[maSira];
11 maTampon[maSira] = yeni;
12 maSira = (maSira + 1) % PENCERE;
13 return maToplam / PENCERE;
14}
15
16// 5'lik medyan / median of 5
17int mdTampon[5];
18int mdSira = 0;
19
20int medyan5(int yeni) {
21 mdTampon[mdSira] = yeni;
22 mdSira = (mdSira + 1) % 5;
23 int a[5];
24 for (int i = 0; i < 5; i++) a[i] = mdTampon[i];
25 for (int i = 1; i < 5; i++) { // eklemeli sıralama / insertion sort
26 int d = a[i], j = i - 1;
27 while (j >= 0 && a[j] > d) { a[j + 1] = a[j]; j--; }
28 a[j + 1] = d;
29 }
30 return a[2];
31}
32
33// Üstel filtre / exponential filter
34float ema = 0;
35const float ALFA = 0.1;
36
37unsigned long sonOlcum = 0;
38
39void setup() {
40 Serial.begin(115200);
41 int ilk = analogRead(POT_PIN);
42 for (int i = 0; i < PENCERE; i++) maTampon[i] = ilk; // tamponları doldur / prefill buffers
43 maToplam = (long)ilk * PENCERE;
44 for (int i = 0; i < 5; i++) mdTampon[i] = ilk;
45 ema = ilk;
46 randomSeed(analogRead(A5));
47}
48
49void loop() {
50 if (millis() - sonOlcum < 10) return; // 100 Hz
51 sonOlcum = millis();
52
53 int ham = analogRead(POT_PIN);
54 if (random(100) < 3) { // TEST: %3 yapay sıçrama, projede sil / TEST: 3% fake spikes, remove in a project
55 ham = constrain(ham + random(-400, 400), 0, 1023);
56 }
57 ema += ALFA * (ham - ema);
58
59 Serial.print("Ham:");
60 Serial.print(ham);
61 Serial.print(",Hareketli:");
62 Serial.print(hareketliOrtalama(ham));
63 Serial.print(",Medyan:");
64 Serial.print(medyan5(ham));
65 Serial.print(",EMA:");
66 Serial.println(ema, 1);
67}Pada grafik, label muncul dalam warna berbeda di kanan atas. Jika kamu tidak menggunakan koma antarlabel dan titik dua antara label dan nilai, Serial Plotter tidak bisa memisahkan garis dengan benar. Plotter lama di Arduino IDE 1.8 memiliki dukungan label terbatas; jika mengalami masalah, kirim hanya angka yang dipisahkan koma. Untuk masalah port serial dan baud rate, lihat artikel Serial Monitor Arduino.
5. Histeresis: menghentikan keputusan yang berubah-ubah di ambang
Filter menstabilkan nilai, tetapi sendirian tidak menyelesaikan masalah keputusan. Lampu tidur yang menerapkan aturan “nyalakan lampu ketika nilai cahaya turun di bawah 350” akan menyala dan mati beberapa kali per detik ketika senja karena nilai bergerak antara 349 dan 351. Ini cepat mengauskan kontak relay. Solusinya adalah histeresis: dua ambang terpisah digunakan untuk menyalakan dan mematikan. Misalnya, lampu menyala ketika nilai turun di bawah 300, tetapi nilai harus naik di atas 400 agar lampu mati. “Dead band” 100 hitungan di antaranya mencegah fluktuasi kecil mengubah keputusan. Kode di bawah menggabungkan histeresis dengan EMA dan waktu konfirmasi singkat: kondisi harus bertahan tanpa terputus selama 1 detik, sehingga bayangan yang lewat atau sorotan lampu kendaraan yang singkat juga tidak mengubah keputusan.
1const int LDR_PIN = A1;
2const int LAMBA_PIN = 8; // LED ya da röle modülü / LED or relay module
3const int YAK_ESIK = 300; // bunun altı karanlık / below this is dark
4const int SONDUR_ESIK = 400; // bunun üstü aydınlık / above this is bright
5const unsigned long ONAY_MS = 1000; // koşul 1 sn sürmeli / condition must last 1 s
6
7float ema = -1;
8bool lambaAcik = false;
9unsigned long kosulBasladi = 0;
10bool kosulVar = false;
11
12void setup() {
13 Serial.begin(115200);
14 pinMode(LAMBA_PIN, OUTPUT);
15}
16
17void loop() {
18 int ham = analogRead(LDR_PIN);
19 if (ema < 0) ema = ham;
20 ema += 0.05 * (ham - ema); // yavaş EMA / slow EMA
21
22 // Durum değiştirmek için hangi koşul gerekiyor? / which condition flips the state?
23 bool degistir = lambaAcik ? (ema > SONDUR_ESIK) : (ema < YAK_ESIK);
24
25 if (degistir) {
26 if (!kosulVar) {
27 kosulVar = true;
28 kosulBasladi = millis(); // koşul şimdi başladı / condition just started
29 } else if (millis() - kosulBasladi >= ONAY_MS) {
30 lambaAcik = !lambaAcik; // 1 sn sürdü, kararı değiştir / lasted 1 s, flip the decision
31 digitalWrite(LAMBA_PIN, lambaAcik ? HIGH : LOW);
32 kosulVar = false;
33 }
34 } else {
35 kosulVar = false; // koşul bozuldu, sayacı sıfırla / condition broke, reset the timer
36 }
37
38 Serial.print("EMA:");
39 Serial.print(ema, 0);
40 Serial.print(",Yak:");
41 Serial.print(YAK_ESIK);
42 Serial.print(",Sondur:");
43 Serial.print(SONDUR_ESIK);
44 Serial.print(",Lamba:");
45 Serial.println(lambaAcik ? 600 : 100); // grafikte görünsün / visible on the plot
46 delay(10);
47}Sebelum menetapkan ambang, amati nilai ruangan sepanjang hari di Serial Plotter. Dead band harus setidaknya dua hingga tiga kali lebih lebar daripada fluktuasi nilai yang sudah difilter. Metode yang sama digunakan pada termostat (misalnya, matikan pemanas pada 22 °C dan nyalakan pada 20 °C), pompa tangki air, dan rangkaian proteksi baterai. Debounce tombol sebenarnya juga merupakan bentuk waktu konfirmasi; detailnya ada dalam Cara Menghubungkan Tombol.
Cara memilih laju pengambilan sampel
Agar filter bekerja dengan baik, sensor harus dibaca pada laju yang tepat. Pembacaan terlalu lambat membuat perubahan cepat terlewat; pembacaan terlalu cepat membuat sensor mengembalikan nilai yang sama berulang kali atau bahkan belum siap. Aturan dasarnya adalah aturan Nyquist: ambil sampel setidaknya dua kali lebih cepat daripada perubahan tercepat yang ingin kamu ikuti; dalam praktiknya, 5–10 kali lebih aman. Beberapa sensor juga memiliki batas minimum intervalnya sendiri, dan membacanya lebih cepat tidak memberikan manfaat. Jika kedipan cahaya berpengaruh, ingat bahwa pada jaringan 50 Hz lampu berkedip 100 kali per detik. Cara termudah untuk meniadakan kedipan ini pada LDR atau sensor analog lain adalah mengambil pembacaan berjarak waktu sama selama periode yang merupakan kelipatan 20 ms lalu merata-ratakannya; karena jendela mencakup periode listrik PLN secara utuh, kedipan saling meniadakan.
| Sensor | Interval pembacaan yang disarankan | Catatan |
|---|---|---|
| DHT11 / DHT22 | 1 s / 2 s | Pembacaan lebih sering mengembalikan nilai yang sama atau error |
| DS18B20 (12-bit) | 750 ms | Itulah waktu konversinya |
| HC-SR04 | Setidaknya 60 ms | Tunggu gema sebelumnya mereda |
| Potensiometer, joystick | 10–20 ms | 50–100 Hz lebih dari cukup untuk gerakan manusia |
| LDR (untuk meredam kedipan lampu) | Setiap 1 ms selama 20 ms, lalu rata-ratakan | Kedipan akibat listrik PLN 50 Hz saling meniadakan |
| MPU6050 | 5–10 ms | Rentang umum untuk pengukuran gerakan 100–200 Hz |
Pengantar singkat filter Kalman
Filter Kalman memperhitungkan besarnya derau pengukuran dan seberapa cepat nilai sebenarnya bisa berubah, lalu menyesuaikan penguatannya sendiri. Dalam bentuk satu dimensi yang paling sederhana, filter ini memiliki dua pengaturan: R adalah varians derau pengukuran (seberapa besar fluktuasi sensor); Q adalah derau proses (seberapa cepat kamu memperkirakan nilai sebenarnya berubah). Pada setiap langkah, filter terlebih dahulu meningkatkan ketidakpastiannya sebesar Q, lalu menghitung penguatan K = P / (P + R) dan menggeser estimasinya mendekati pengukuran sebesar fraksi K. Jika pengukuran berderau (R besar), K kecil dan filter lebih sedikit memercayai pengukuran; jika nilai diperkirakan berubah cepat (Q besar), K membesar dan filter merespons cepat. Hasil yang menarik: ketika Q dan R tetap, K menjadi konstan setelah beberapa waktu dan filter Kalman satu dimensi pada dasarnya menjadi EMA. Kekuatan Kalman yang sebenarnya terlihat ketika mengestimasi beberapa besaran bersama-sama, misalnya menggabungkan data akselerometer dan giroskop untuk menentukan sudut kemiringan. Kamu akan menemukan kerabatnya yang lebih sederhana, filter komplementer, dalam artikel MPU6050.
1const int POT_PIN = A0;
2
3struct Kalman1D {
4 float q; // süreç gürültüsü / process noise
5 float r; // ölçüm gürültüsü (varyans) / measurement noise (variance)
6 float p; // tahminin belirsizliği / estimate uncertainty
7 float x; // tahmin / estimate
8 bool ilk;
9};
10
11Kalman1D kf = {0.05, 9.0, 1.0, 0.0, true}; // r = 9: yaklaşık ±3 basamak titreşim / about ±3 counts of jitter
12
13float kalmanGuncelle(Kalman1D &k, float olcum) {
14 if (k.ilk) { k.x = olcum; k.ilk = false; return k.x; }
15 k.p += k.q; // 1. tahmin: belirsizlik artar / predict: uncertainty grows
16 float kazanc = k.p / (k.p + k.r); // 2. Kalman kazancı / Kalman gain
17 k.x += kazanc * (olcum - k.x); // 3. ölçüme doğru düzelt / correct toward the measurement
18 k.p *= (1.0 - kazanc); // 4. belirsizlik azalır / uncertainty shrinks
19 return k.x;
20}
21
22void setup() {
23 Serial.begin(115200);
24}
25
26void loop() {
27 int ham = analogRead(POT_PIN);
28 float tahmin = kalmanGuncelle(kf, ham);
29 Serial.print("Ham:");
30 Serial.print(ham);
31 Serial.print(",Kalman:");
32 Serial.println(tahmin, 1);
33 delay(10);
34}R mudah diperkirakan: ambil 100 pembacaan mentah tanpa menyentuh potensiometer dan lihat seberapa lebar sebaran nilainya. Jika nilainya bergerak sekitar ±3 hitungan dari rata-rata, simpangan bakunya sekitar 3 dan variansnya sekitar 9. Temukan Q melalui percobaan: Q kecil (0.001) menghasilkan output sangat stabil tetapi lambat, sedangkan Q besar (1) menghasilkan output cepat tetapi lebih berfluktuasi. Sebagai titik awal, pilih Q antara 1 dan 10 persen dari R lalu sesuaikan lebih lanjut di Serial Plotter sambil memutar potensiometer.
Derau ADC dan penyaringan pada ESP32
ADC 12-bit ESP32 memiliki langkah resolusi lebih kecil daripada ADC Arduino Uno, tetapi lebih berderau dan tidak linear, terutama pada ujung rentang 0–0,1 V dan 3,1–3,3 V. Ketika Wi-Fi aktif, derau semakin meningkat. Dua hal mempermudah pekerjaan: gunakan analogReadMilliVolts(), yang menerapkan kalibrasi pabrik chip alih-alih mengembalikan nilai mentah, dan rata-ratakan 16–64 sampel per pembacaan sebelum menerapkan EMA. Kode di bawah membaca potensiometer melalui dua langkah ini; dalam proyek yang menggunakan Wi-Fi, selalu hubungkan sensor ke pin ADC1 GPIO 32–39.
1const int POT_PIN = 34; // ADC1 pini, Wi-Fi ile çalışır / ADC1 pin, works with Wi-Fi
2const int ORNEK = 32; // her ölçümde 32 örnek / 32 samples per reading
3const float ALFA = 0.15;
4
5float ema = -1;
6
7int millivoltOku() {
8 uint32_t toplam = 0;
9 for (int i = 0; i < ORNEK; i++) {
10 toplam += analogReadMilliVolts(POT_PIN); // kalibre mV / calibrated mV
11 }
12 return toplam / ORNEK;
13}
14
15void setup() {
16 Serial.begin(115200);
17 analogReadResolution(12); // 0-4095
18}
19
20void loop() {
21 int ham = analogRead(POT_PIN); // karşılaştırma için tek ham ölçüm / single raw reading for comparison
22 int mv = millivoltOku();
23 if (ema < 0) ema = mv;
24 ema += ALFA * (mv - ema);
25 Serial.printf("Ham:%d,mV:%d,EMA_mV:%.1f\n", ham, mv, ema);
26 delay(20);
27}Penyaringan dengan Raspberry Pi Pico dan MicroPython
Pada Raspberry Pi Pico, ADC.read_u16() mengembalikan nilai antara 0 dan 65535, tetapi resolusi ADC yang sebenarnya adalah 12 bit dan bit-bit bawahnya berderau. Dalam MicroPython, fungsi sorted() membuat median sangat mudah diterapkan, dan sebuah daftar ditambah jumlah berjalan sudah cukup untuk rata-rata bergerak. Kode di bawah membaca potensiometer pada 100 Hz dan mencetak output tiga filter dalam format yang bisa digambar Thonny di jendela View > Plotter.
1from machine import ADC
2import time
3
4pot = ADC(26) # GP26 = ADC0
5
6PENCERE = 10
7tampon = [pot.read_u16()] * PENCERE # hareketli ortalama tamponu / moving average buffer
8toplam = sum(tampon)
9sira = 0
10son5 = tampon[:5] # medyan için son 5 değer / last 5 values for the median
11ema = float(tampon[0])
12ALFA = 0.1
13
14while True:
15 ham = pot.read_u16()
16
17 toplam += ham - tampon[sira] # hareketli ortalama / moving average
18 tampon[sira] = ham
19 sira = (sira + 1) % PENCERE
20 hareketli = toplam // PENCERE
21
22 son5.pop(0) # medyan / median
23 son5.append(ham)
24 medyan = sorted(son5)[2]
25
26 ema += ALFA * (ham - ema) # üstel filtre / exponential filter
27
28 print("Ham:", ham, "Hareketli:", hareketli, "Medyan:", medyan, "EMA:", int(ema))
29 time.sleep_ms(10)Plotter Thonny menampilkan angka pada setiap baris sebagai garis terpisah dan menggunakan teks di depan angka sebagai label. MicroPython berjalan lebih lambat daripada Arduino, jadi jangan menjalankan filter pada laju terlalu tinggi; sekitar 100 Hz dengan jendela kecil berjalan lancar pada Pico. Jika kamu baru mengenal MicroPython, mulailah dari artikel dasar-dasar MicroPython.
Filter mana yang sebaiknya dipilih?
| Situasi | Filter yang disarankan | Mengapa? |
|---|---|---|
| Potensiometer, joystick, atau sensor analog berfluktuasi | Rata-rata bergerak atau EMA (α = 0,1–0,2) | Sederhana, cepat, perataan cukup |
| Sesekali muncul nilai tidak masuk akal (HC-SR04, sensor aliran) | Median 3 atau 5 titik | Sepenuhnya menghilangkan galat yang sesekali muncul |
| Lonjakan sekaligus fluktuasi | Median dahulu, lalu EMA | Mengatasi kedua masalah secara berurutan |
| RAM sangat sedikit, banyak sensor | EMA bilangan bulat | Satu variabel per sensor |
| Keputusan nyala/mati berubah-ubah di ambang | Histeresis dan waktu konfirmasi | Mengatasi masalah keputusan; filter saja tidak cukup |
| Riak dari lampu atau listrik PLN | Rata-rata pembacaan berjarak waktu sama selama 20 ms | Mencakup satu periode penuh 50 Hz |
| Menggabungkan beberapa sensor (kemiringan, posisi) | Filter Kalman atau komplementer | Menggabungkan keunggulan setiap sensor |
Kesalahan umum dan solusinya
| Gejala | Kemungkinan penyebab | Solusi |
|---|---|---|
| Nilai yang sudah difilter merespons terlalu terlambat | Jendela terlalu besar atau α terlalu kecil | Perkecil jendela atau naikkan α; hitung keterlambatan dari tabel |
| Rata-rata bergerak naik perlahan dari nol ketika mulai | Buffer awalnya diisi nol | Isi buffer terlebih dahulu dengan pembacaan pertama, atau bagi dengan jumlah pembacaan sampai penuh |
| Rata-rata menjadi kacau pada 64 sampel | Jumlah bertipe int meluap (64 × 1023 = 65.472 > 32.767) | Jadikan variabel jumlah bertipe long |
| Filter median tidak mengubah apa pun | Ukuran jendela 2 atau bilangan genap, atau lonjakan muncul berturut-turut | Gunakan jendela ganjil (3, 5, 7) yang lebih besar daripada jumlah lonjakan berturut-turut |
| Garis Serial Plotter menyatu atau semuanya satu warna | Format label salah | Gunakan label:value dengan koma di antaranya dan akhiri baris dengan println |
| Nilai EMA tidak pernah berubah | Pembagian bilangan bulat: menulis 1/10 alih-alih 0.1 | Tulis koefisien sebagai float (0.1) atau gunakan rumus EMA bilangan bulat |
| Nilai tetap berfluktuasi meskipun sudah difilter | Masalahnya bukan derau, melainkan pergeseran atau catu daya lemah | Periksa catu daya, tambahkan kapasitor 100 nF, dan lakukan kalibrasi |
Penyaringan data sensor adalah cara termurah untuk membuat pengukuran andal: tidak membutuhkan komponen tambahan, dan beberapa baris kode membuat proyekmu berjalan lebih stabil. Setelah mengetahui cara menggabungkan perataan, rata-rata bergerak, median, EMA, dan histeresis, kamu bisa mengatasi tampilan yang berfluktuasi, alarm palsu, dan relay yang terus berbunyi karena beralih cepat. Di aplikasi BlueGrays, kamu bisa menjelajahi proyek seperti pengukuran cahaya dengan LDR, lampu tidur, pengukuran jarak dengan HC-SR04, sensor parkir, dan pengukur tinggi, lengkap dengan skema rangkaian langkah demi langkah dan kode siap pakai, lalu melihat hasilnya sendiri dengan menambahkan filter ini.
Pertanyaan umum
Mengapa nilai sensor pada Arduino terus berubah?
Dalam pembacaan analog, riak catu daya, kabel panjang, motor di dekatnya, dan derau ADC sendiri menggeser nilai beberapa hitungan. Ini normal dan tidak berarti sensor rusak. Menambahkan kapasitor 100 nF ke input analog dan menggunakan filter perataan atau EMA dalam perangkat lunak akan menstabilkan nilai.
Apa itu rata-rata bergerak?
Rata-rata bergerak adalah rata-rata N pembacaan terakhir; pada setiap pembacaan baru, nilai terlama dihapus dan yang baru ditambahkan. Filter ini mengurangi derau sekitar √N kali tetapi menunda output sebesar (N−1)/2 pembacaan. Pada Arduino, implementasinya hanya membutuhkan beberapa baris dengan buffer melingkar dan satu variabel jumlah.
Apa perbedaan filter median dan rata-rata?
Rata-rata memperhitungkan setiap nilai, sehingga satu pembacaan yang tidak masuk akal merusak hasilnya. Filter median mengurutkan nilai dan mengambil yang di tengah, sehingga sepenuhnya mengabaikan pembacaan keliru yang sesekali muncul. Karena itu, median lebih cocok untuk sensor seperti HC-SR04 yang terkadang menghasilkan nilai salah, sedangkan perataan lebih cocok untuk pembacaan analog yang berfluktuasi.
Bagaimana cara memilih koefisien filter eksponensial (EMA)?
Koefisien α berada antara 0 dan 1; α kecil memberikan output lebih stabil tetapi lambat, sedangkan α besar memberikan output lebih cepat tetapi lebih berfluktuasi. Dengan pembacaan setiap 10 ms, α = 0,1 memberikan konstanta waktu sekitar 100 ms dan merupakan titik awal yang baik untuk sebagian besar sensor. Kamu bisa menghitung waktu respons dengan τ ≈ T / α.
Bagaimana cara menggambar beberapa nilai di Serial Plotter Arduino?
Cetak nilai pada setiap baris dalam format label:value dengan koma di antaranya dan akhiri baris dengan println, misalnya Ham:512,EMA:509. Serial Plotter menampilkan setiap label sebagai garis berwarna yang terpisah. Tutup Serial Monitor dahulu jika terbuka dan sesuaikan baud rate dengan kodemu.
Bisakah filter Kalman digunakan pada Arduino?
Bisa, filter Kalman satu dimensi yang sederhana berjalan dengan beberapa baris kode bahkan pada Arduino Uno. Filter ini disetel melalui derau pengukuran R dan derau proses Q; untuk satu sensor, hasilnya biasanya sangat dekat dengan EMA yang disetel dengan baik. Manfaat sebenarnya terlihat ketika menggabungkan beberapa sensor, seperti akselerometer dan giroskop.
Proyek untuk dibuat setelah membaca ini
Lihat semua proyek
Mengukur Intensitas Cahaya dengan LDR
Ingin melihat seberapa terang kamarmu dalam bentuk angka? LDR, komponen kecil yang bereaksi terhadap cahaya, mengukur cahaya di sekitarnya, dan Arduino menulis nilainya ke layar komputermu setiap sete

Pengukuran Jarak HC-SR04 (Tampilan Serial)
Sensor HC-SR04 bekerja persis seperti kelelawar yang mencari jalan dalam gelap: ia mengirim suara yang tidak bisa kamu dengar, mendengarkan pantulannya, dan mengukur seberapa jauh benda di depannya. A

Lampu Tidur dengan LDR
Lampu tidur kecil yang menyala sendiri ketika gelap dan mati ketika pagi tiba. Sensor cahaya mengukur seberapa terang ruangan, dan Arduino menyalakan LED ketika cukup gelap lalu mematikannya lagi keti

Sensor Parkir dengan HC-SR04
Versi mini sensor parkir mobil: makin dekat sesuatu, makin cepat bunyi bipnya. Sensor jarak mengukur benda di depannya; jika jaraknya lebih dari 20 sentimeter LED hijau tetap menyala, dan begitu lebih

Proyek Pengukur Tinggi Badan
Pengukur tinggi yang digantung di kusen pintu dan memberi tahu, dalam sentimeter, tinggi orang yang berdiri di bawahnya. Pertama kamu menekan tombol pertama tanpa ada orang di bawahnya, dan sensor jar

Menggerakkan Motor Servo dengan Potensiometer
Ketika kamu memutar kenop potensiometer, lengan motor servo ikut berputar: satu ujung kenop berarti nol derajat, ujung lainnya berarti setengah putaran. Arduino terus membaca posisi kenop dan servo la

Membaca Intensitas Cahaya dengan Modul LDR ESP32
Memantau secara real-time di Serial Monitor nilai ADC mentah modul LDR dan persentase cahaya relatif yang dihitung dari nilai acuan gelap dan terang. Persentase ini bukan pengukuran lux. Mengajarkan logika kalibrasi modul. Ideal dikerjakan sebelum Proyek 10, karena dipakai untuk menentukan nilai ambang.

Sensor Jarak Ultrasonik HC-SR04 ESP32
Sensor ultrasonik HC-SR04 menghitung jarak dengan mengukur waktu yang dibutuhkan gelombang suara yang dipancarkannya untuk memantul dari benda lalu kembali (Time of Flight). Di proyek ini, kita mengirim pulsa 10 mikrodetik dari ESP32 ke pin TRIG sensor dan membaca durasi pantulan dari pin ECHO dengan pulseIn(). Jarak hasil hitung ditampilkan dalam sentimeter di LCD I2C 16x2. Karena HC-SR04 bekerja di 5V, pembagi tegangan wajib dipasang di pin ECHO.
Artikel lainnya
Aplikasi BlueGrays
Rakit rangkaian ini langkah demi langkah di ponselmu
Skema pengkabelan, kode, dan daftar komponen dalam satu layar. 200+ proyek Arduino, ESP32, dan Pico siap dibuat.
Pindai dengan ponselmu
Arahkan kamera ke kode ini dan toko aplikasi di ponselmu akan terbuka.
atau buka toko aplikasi

