Dasar · 12 mnt baca

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.

Sensor jarak ultrasonik HC-SR04
Sensor seperti HC-SR04 sesekali menghasilkan pembacaan keliru; filter median menghilangkan lonjakan seperti ini.

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 derauBentuknyaPenyebab umumSolusi yang sesuai
Derau acakNilai berfluktuasi ±2–10 hitunganRiak catu daya, kabel panjang, derau ADC sendiriPerataan, rata-rata bergerak, EMA
LonjakanSesekali muncul nilai yang sangat menyimpangGema HC-SR04 tidak tertangkap, kontak longgar, gangguan motorFilter median
Galat langkah (kuantisasi)Nilai berganti antara dua angka berdekatanResolusi ADC (5 V / 1024 = 4,9 mV pada Uno)Perataan, histeresis
Derau periodikRiak teraturKedipan lampu 100 Hz, listrik PLN 50 HzPerataan selama kelipatan 20 ms
PergeseranNilai berubah perlahan selama beberapa menitSuhu, tegangan baterai menurunKalibrasi 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.

KoneksiArduino UnoESP32Raspberry Pi Pico
Kontak geser potensiometerA0GPIO 34GP26 (ADC0)
Titik tengah pembagi LDR + 10 kΩA1GPIO 35GP27 (ADC1)
HC-SR04 TRIGD9GPIO 5GP2
HC-SR04 ECHOD10GPIO 18 (dengan pembagi)GP3 (dengan pembagi)
LED atau relay (contoh histeresis)D8--
Catu dayaPotensiometer dan LDR 5V, HC-SR04 5VPotensiometer dan LDR 3V3, HC-SR04 5VPotensiometer dan LDR 3V3, HC-SR04 VBUS
Pada ESP32 dan Pico, hubungkan potensiometer dan pembagi tegangan LDR ke 3V3; pin analog tidak boleh menerima tegangan melebihi 3,3 V. HC-SR04 bekerja pada 5 V dan output ECHO-nya menghasilkan 5 V, jadi pada ESP32 dan Pico turunkan tegangan pin ECHO ke 3,3 V dengan pembagi tegangan 1 kΩ dan 2 kΩ. Kamu bisa memeriksa nilai pembagi dengan alat Pembagi tegangan.

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.

C++ (Arduino IDE)
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.

C++ (Arduino IDE)
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 derauKeterlambatan (satu pembacaan setiap 10 ms)Cocok untuk
4Sekitar 2×15 msKendali yang membutuhkan respons cepat
10Sekitar 3×45 msPotensiometer, joystick, LDR
32Sekitar 5,7×155 msSuhu, kelembapan, tegangan baterai
10010×495 msNilai 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.

C++ (Arduino IDE)
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}
Kamu juga bisa menggabungkan median dan perataan: pertama hilangkan lonjakan dengan median 5 titik, lalu masukkan hasilnya ke rata-rata bergerak atau EMA untuk meratakan fluktuasi yang tersisa. Pasangan ini adalah solusi yang paling umum untuk pembacaan yang mengandung lonjakan sekaligus fluktuasi, seperti sensor jarak dan aliran. Jangan membuat jendela median terlalu besar; 3 atau 5 cukup untuk sebagian besar kebutuhan, sedangkan 9 atau lebih menambah keterlambatan tanpa manfaat.

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.

C++ (Arduino IDE)
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 ekuivalenWaktu respons 63% (pengambilan sampel 10 ms)Karakteristik
0.5310–20 msSangat cepat, sedikit perataan
0.2950 msCepat, perataan ringan
0.119100 msSeimbang, titik awal yang baik untuk sebagian besar sensor
0.0539200 msStabil, lambat
0.011991 sSangat 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.

  1. Upload kode dan tutup Serial Monitor jika terbuka; Serial Monitor dan Serial Plotter tidak bisa dibuka bersamaan.
  2. Di Arduino IDE, buka Tools > Serial Plotter atau klik ikon grafik di kanan atas.
  3. Atur kecepatan di kanan bawah ke 115200 baud.
  4. Putar potensiometer perlahan lalu cepat, dan amati respons setiap garis.
  5. Gunakan kotak di sebelah label untuk menampilkan atau menyembunyikan garis dan membandingkan dua filter secara langsung.
C++ (Arduino IDE)
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.

C++ (Arduino IDE)
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.

SensorInterval pembacaan yang disarankanCatatan
DHT11 / DHT221 s / 2 sPembacaan lebih sering mengembalikan nilai yang sama atau error
DS18B20 (12-bit)750 msItulah waktu konversinya
HC-SR04Setidaknya 60 msTunggu gema sebelumnya mereda
Potensiometer, joystick10–20 ms50–100 Hz lebih dari cukup untuk gerakan manusia
LDR (untuk meredam kedipan lampu)Setiap 1 ms selama 20 ms, lalu rata-ratakanKedipan akibat listrik PLN 50 Hz saling meniadakan
MPU60505–10 msRentang 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.

C++ (Arduino IDE)
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.

C++ (Arduino IDE)
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.

MicroPython
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?

SituasiFilter yang disarankanMengapa?
Potensiometer, joystick, atau sensor analog berfluktuasiRata-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 titikSepenuhnya menghilangkan galat yang sesekali muncul
Lonjakan sekaligus fluktuasiMedian dahulu, lalu EMAMengatasi kedua masalah secara berurutan
RAM sangat sedikit, banyak sensorEMA bilangan bulatSatu variabel per sensor
Keputusan nyala/mati berubah-ubah di ambangHisteresis dan waktu konfirmasiMengatasi masalah keputusan; filter saja tidak cukup
Riak dari lampu atau listrik PLNRata-rata pembacaan berjarak waktu sama selama 20 msMencakup satu periode penuh 50 Hz
Menggabungkan beberapa sensor (kemiringan, posisi)Filter Kalman atau komplementerMenggabungkan keunggulan setiap sensor

Kesalahan umum dan solusinya

GejalaKemungkinan penyebabSolusi
Nilai yang sudah difilter merespons terlalu terlambatJendela terlalu besar atau α terlalu kecilPerkecil jendela atau naikkan α; hitung keterlambatan dari tabel
Rata-rata bergerak naik perlahan dari nol ketika mulaiBuffer awalnya diisi nolIsi buffer terlebih dahulu dengan pembacaan pertama, atau bagi dengan jumlah pembacaan sampai penuh
Rata-rata menjadi kacau pada 64 sampelJumlah bertipe int meluap (64 × 1023 = 65.472 > 32.767)Jadikan variabel jumlah bertipe long
Filter median tidak mengubah apa punUkuran jendela 2 atau bilangan genap, atau lonjakan muncul berturut-turutGunakan jendela ganjil (3, 5, 7) yang lebih besar daripada jumlah lonjakan berturut-turut
Garis Serial Plotter menyatu atau semuanya satu warnaFormat label salahGunakan label:value dengan koma di antaranya dan akhiri baris dengan println
Nilai EMA tidak pernah berubahPembagian bilangan bulat: menulis 1/10 alih-alih 0.1Tulis koefisien sebagai float (0.1) atau gunakan rumus EMA bilangan bulat
Nilai tetap berfluktuasi meskipun sudah difilterMasalahnya bukan derau, melainkan pergeseran atau catu daya lemahPeriksa catu daya, tambahkan kapasitor 100 nF, dan lakukan kalibrasi
Selalu lihat data mentah sebelum memilih filter. Mengamati nilai mentah di Serial Plotter selama beberapa menit langsung menunjukkan apakah masalahnya berupa fluktuasi, lonjakan, riak periodik, atau pergeseran. Lalu mulailah dengan filter paling sederhana dan beralih ke berikutnya hanya jika diperlukan. Dalam sebagian besar proyek, median 5 titik ditambah EMA dengan α = 0,1 bekerja lebih baik daripada filter Kalman yang rumit dan jauh lebih mudah dipahami.

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.

Lihat semua proyek
Mengukur Intensitas Cahaya dengan LDR
#11Pemula

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

4.6(81)
141
Pengukuran Jarak HC-SR04 (Tampilan Serial)
#27Pemula

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

4.6(47)
74
Lampu Tidur dengan LDR
#12Pemula

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

4.8(103)
210
Sensor Parkir dengan HC-SR04
#28Menengah
Premium

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

4.8(64)
85
Proyek Pengukur Tinggi Badan
#33Menengah
Premium

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

4.5(25)
34
Menggerakkan Motor Servo dengan Potensiometer
#36Pemula
Premium

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

4.6(37)
53
Membaca Intensitas Cahaya dengan Modul LDR ESP32
#113Pemula

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.

4.6(36)
55
Sensor Jarak Ultrasonik HC-SR04 ESP32
#116Menengah

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.

4.9(28)
46