Mengukur Detak Jantung dengan Sensor Arduino KY-039
Komponen dan perlengkapan
Arduino UNO
×
1
Sensor detak jantung KY-039
×
1
Kabel jumper (umum)
×
1
Tentang proyek ini
Dalam rangkaian 37 sensor untuk Arduino, tersedia sensor detak jantung. Namanya terlalu berjanji. Banyak orang berpikir sensor ini memberikan angka digital melalui I2C atau sejenisnya, sebuah angka yang merupakan rate detak jantung. Apa yang diberikan sensor hanyalah nilai "analog" dari 0 hingga 1023, yang menunjukkan seberapa banyak cahaya inframerah yang diterima sensor cahaya, atau sebenarnya seberapa besar sesuatu yang menutupi sensor cahaya. Nilai semakin tinggi, semakin sedikit cahaya inframerah.
Singkatnya: letakkan jari Anda di antara LED inframerah dan transistor cahaya sensor. Detak jantung Anda meregangkan pembuluh darah di jari, yang akan menyaring sinyal inframerah. Ini menciptakan sinyal berdenyut.
Dalam proyek ini saya menjelaskan bagaimana sinyal ini dikonversi menjadi rate detak jantung seperti 66 BPM (detak per menit).
Langkah sederhana
Jika Anda hanya membaca dan menampilkan nilai dari sensor KY-039, Anda akan mendapatkan sesuatu seperti ini:
Nilai-nilai adalah bilangan bulat. Tidak sangat akurat. Sebaliknya, hitung rata-rata dari sekelompok nilai dan tampilkan rata-ratanya. Anda akan mendapatkan ini:
Di sini Anda sudah bisa melihat denyutan jantung. Dapatkan selisih waktu antara setiap kenaikan signifikan denyutan. Dari sana Anda dapat menghitung rate detak jantung dalam BPM.
(Pola zig-zag kecil pada gambar di atas disebabkan oleh cahaya buatan, 50 Hz, hal lain yang harus diatasi.)
Penjelasan langkah
Berikut adalah kode sederhana untuk hanya mengoutput apa saja yang Anda baca dari sensor KY-039:
Apa yang mungkin Anda dapatkan adalah sesuatu seperti ini:
Karena ini adalah keterangan dari jendela serial monitor yang membaca output serial dari Arduino Anda pada 9600 baud, seluruh proses diatur oleh fungsi Serial.print(), yang akan menetapkan rate untuk membaca dan menampilkan nilai. Bagaimanapun, kurva sangat bergerigi, karena bervariasi antara 360 dan 383 dan hanya memiliki nilai bilangan bulat.
Penghalusan
Untuk mendapatkan output yang lebih halus, ambil rata-rata dari katakanlah 20 pembacaan terakhir dari sensor. Berikut caranya. Saya mendefinisikan konstanta yang menunjukkan berapa banyak pembacaan yang saya inginkan:
#define samp_siz 20
Lalu saya memiliki array yang menampung sejumlah pembacaan tersebut:
int reads[samp_siz];
Untuk setiap pembacaan baru, saya mengurangi pembacaan terlama dari jumlah dan menambahkan pembacaan terbaru ke jumlah. Dalam array saya mengganti pembacaan terlama dengan pembacaan terbaru.
reader = analogRead (sensorPin); // baca sensor sum -= reads[ptr]; // kurangi pembacaan terlama dari jumlah sum += reader; // tambahkan pembacaan terbaru ke jumlah reads[ptr] = reader; // simpan pembacaan terbaru dalam array last = float(sum) / samp_siz; // hitung rata-rata sekarang ptr++; // perbarui indeks untuk array, have ptr %= samp_siz; // restart dari 0 saat diperlukan
Dengan ukuran array 20 dan baud rate 9600 di serial monitor, saya mungkin mendapatkan plotting seperti ini:
Di sini Anda melihat detak jantung sebenarnya sebagai kurva kenaikan tajam. Namun Anda juga melihat pola zig-zag kecil. Zig-zag kecil berasal dari lampu dapur saya, tiga lampu LED yang menerangi kamar. Listrik rumah saya adalah 240 V, 50 Hz AC. Jadi 50 kali per detik terjadi kenaikan intensitas cahaya, tampaknya juga dalam band inframerah. Saya ingin menghaluskan kebisingan 50 Hz itu. Ini seharusnya berhasil, jika saya membaca nilai dari sensor selama periode 20 ms dan mengambil rata-rata dari semua nilai. Mari kita lihat...
n = 0; start = millis(); reader = 0.; do { reader += analogRead (sensorPin); // baca dan tambahkan nilai... n++; now = millis(); } while (now < start + 20); // ...selama 20 ms telah berlalu reader /= n; // dan ambil rata-rata dari nilai
Dengan cuplikan ini saya mengambil pembacaan sensor dalam potongan 20 ms, yang akan meratakan kebisingan berkedip 50 Hz yang disebabkan oleh cahaya buatan. Jika Anda tinggal di negara dengan 60 Hz, gunakan potongan 16.67 ms sebagai gantinya. Atau 16667µs.
Karena saya sudah menghaluskan kurva dalam bagian 20 ms, saya sebenarnya tidak memerlukan array yang saya gunakan sebelumnya, namun karena sudah ada dan mudah diubah ukurannya, saya biarkan di sana. Dan menggunakan ukuran array 5 sepertinya meratakan kebisingan terakhir yang mengganggu. Ini adalah yang saya miliki sekarang:
Hal terakhir yang perlu saya lakukan adalah mengenali bagian mana pun dalam pola berulang. Karena lereng kenaikan lebih teratur, saya memilih itu. Perhatikan bahwa nilai sumbu y sangat berbeda dalam semua grafik. Saya benar-benar tidak bisa bergantung pada nilai mutlak. Saya hanya bisa bergantung pada kenaikan dan penurunan kurva. Matematikawan akan membicarakannya tentang turunan. Saya puas jika saya menemukan n nilai kenaikan berturut-turut, di mana n bisa menjadi nilai yang bisa disesuaikan dengan mudah. Saya mulai dengan 5. Untuk ini saya memiliki konstanta rise_threshold yang didefinisikan dalam kode. Ketika saya menemukan 5 nilai kenaikan berturut-turut, saya tahu saya berada di dasar kurva yang naik. Saya ambil waktu. Saya tunggu kurva turun, lalu saya tunggu 5 nilai kenaikan berikutnya dan catat waktu. Kemudian saya mencetak BPM yang sesuai.
Saya melakukan tesdanmenghitungberapabanyaknilaikenaikanberturut-turutyangadadalamkurvadanmenemukanbahwaadaantara10dan15.Jadijikasayamenghitunghingga5,sayaakanpastitahusayatelahmenemukanawaldaridetak jantung.
Karena saya hanya mencetak setelah setiap detak jantung, tidak akan ada banyak pencetakan. Akan ada lebih banyak waktu untuk membaca sensor. Saya mungkin menangkap lebih banyak kebisingan frekuensi tinggi, yang tidak akan saya lihat, karena plotter tidak aktif. Mari kita lihat bagaimana caranya.
Kode akhir
#define samp_siz 4 #define rise_threshold 5 // Pulse Monitor Test Script int sensorPin = 0; void setup() { Serial.begin(9600); } void loop () { float reads[samp_siz], sum; long int now, ptr; float last, reader, start; float first, second, third, before, print_value; bool rising; int rise_count; int n; long int last_beat; for (int i = 0; i < samp_siz; i++) reads[i] = 0; sum = 0; ptr = 0; while(1) { // calculate an average of the sensor // during a 20 ms period (this will eliminate // the 50 Hz noise caused by electric light n = 0; start = millis(); reader = 0.; do { reader += analogRead (sensorPin); n++; now = millis(); } while (now < start + 20); reader /= n; // we got an average // Add the newest measurement to an array // and subtract the oldest measurement from the array // to maintain a sum of last measurements sum -= reads[ptr]; sum += reader; reads[ptr] = reader; last = sum / samp_siz; // now last holds the average of the values in the array // check for a rising curve (= a heart beat) if (last > before) { rise_count++; if (!rising && rise_count > rise_threshold) { // Ok, we have detected a rising curve, which implies a heartbeat. // Record the time since last beat, keep track of the two previous // times (first, second, third) to get a weighed average. // The rising flag prevents us from detecting the same rise // more than once. rising = true; first = millis() - last_beat; last_beat = millis(); // Calculate the weighed average of heartbeat rate // according to the three last beats print_value = 60000. / (0.4 * first + 0.3 * second + 0.3 * third); Serial.print(print_value); Serial.print('\n'); third = second; second = first; } } else { // Ok, the curve is falling rising = false; rise_count = 0; } before = last; ptr++; ptr %= samp_siz; } }
Kerja dengan cukup baik. Inilah video-nya.
Perhatikan bahwa LED RX kecil berkedip di Arduino secara sinkron dengan jantung saya. Karena ketika ada detak jantung, rate dihitung dan dicetak ke serial, yang menyebabkan LED berkedip. Ketika jari bergerak sedikit, akan terjadi pembacaan salah.
Pengembangan lebih lanjut
Saat ini rate yang dicetak dihitung berdasarkan tiga detak terakhir. Namun mungkin lebih tepat untuk menghitungnya berdasarkan periode katakanlah 15 detik. Saya bisa menyimpan 15 nilai rate berturut-turut, menghitung rata-rata, lalu menghilangkan lima nilai yang terjauh dari rata-rata dan menghitung rata-rata baru. Ini akan memberikan pengukuran rate detak jantung yang andal dan stabil.
Saya hanya menguji sensor pada istri dan saya sendiri. Setiap langkah dalam meningkatkan sinyal yang saya lakukan berdasarkan pembacaan sebelumnya. Orang lain mungkin memiliki jenis detak jantung yang berbeda yang menyebabkan kurva dengan bentuk berbeda, yang membutuhkan pendekatan lain untuk menemukan detak. Mungkin lereng turun yang lebih mudah dikenali. Atau puncaknya yang sangat tinggi. Dan apa yang terjadi jika detak nadi adalah 180 - 200 BPM? Menemukan lereng kenaikan mungkin lebih sulit.
Kode
Versi akhir
Versi akhirC/C++
Program membaca detak jantung dan mencetak rate di jendela serial.
#define samp_siz 4 #define rise_threshold 4 // Pulse Monitor Test Script int sensorPin = 0; void setup() { Serial.begin(9600); } void loop () { float reads[samp_siz], sum; long int now, ptr; float last, reader, start; float first, second, third, before, print_value; bool rising; int rise_count; int n; long int last_beat; for (int i = 0; i < samp_siz; i++) reads[i] = 0; sum = 0; ptr = 0; while(1) { // calculate an average of the sensor // during a 20 ms period (this will eliminate // the 50 Hz noise caused by electric light n = 0; start = millis(); reader = 0.; do { reader += analogRead (sensorPin); n++; now = millis(); } while (now < start + 20); reader /= n; // we got an average // Add the newest measurement to an array // and subtract the oldest measurement from the array // to maintain a sum of last measurements sum -= reads[ptr]; sum += reader; reads[ptr] = reader; last = sum / samp_siz; // now last holds the average of the values in the array // check for a rising curve (= a heart beat) if (last > before) { rise_count++; if (!rising && rise_count > rise_threshold) { // Ok, we have detected a rising curve, which implies a heartbeat. // Record the time since last beat, keep track of the two previous // times (first, second, third) to get a weighed average. // The rising flag prevents us from detecting the same rise more than once. rising = true; first = millis() - last_beat; last_beat = millis(); // Calculate the weighed average of heartbeat rate // according to the three last beats print_value = 60000. / (0.4 * first + 0.3 * second + 0.3 * third); Serial.print(print_value); Serial.print('\n'); third = second; second = first; } } else { // Ok, the curve is falling rising = false; rise_count = 0; } before = last; ptr++; ptr %= samp_siz; } }