Previous Entry Share Next Entry
Продолжаем ардуинить - датчик серцебиения.
snickser
Измерение пульса - прикольная тема. На просторах алиекспресс-а был найден вот такой оптический датчик сердечного ритма.



Не оригинальный естественно, а китайская подделка за тройку долларов, но сути это не меняет, и работает скорее всего так же (отличить их можно по цвету проводков). И естественно сразу же возникло желание опробовать его написав собственный код для ардуины. Дело в том, что на сайте "разработчика" (по ссылке с картинки) есть достаточно рабочий вариант скетча, и даже визуализатор. Но разбираться в чужом коде - не, я лучше свой наваяю :)



И оказалось, это не такая то уж и тривиальная задача, хотя типичной скорости работы Arduino UNO оказалось более чем достаточно для отслеживания сигнала с достаточной точностью. Утверждается что 500Гц хватает для определения пульса до 250 ударов в минуту. Чтож, посмотрим на мой график, снят в новом 1.6.9 Arduino IDE (Serial plotter).



Оказалось этот датчик очень чувствителен к помехам, не так палец прижат, не в правильном положении, вибрации, всё это вносит хаос в показания. Оптическая система реагирует даже на движения рядом с датчиком. Потому палец приходится прижимать очень аккуратно. Самое главное чтоб в область датчика попал какой нибудь из хороших капилляров, где движение крови наиболее интенсивно регистрируется. Лучше использовать безымянные или средние пальцы, и не кончиками, а подушечками прижимать.



Для вывода информации я использую самый простой, а как показывает практика и самый удобный, семисегментный дисплей на TM1637. Подключаем шины к цифровым портам и питание. А вот с датчиком сложность при этом возникла. Оказывается их существует несколько видов, и тот что у меня при подключении к 5В показывал только шум, а вот на 3.3В вполне адекватно работает. Наверно это связано с избыточной яркостью подсвечивающего диода, что на 5В просто адски светится.

Алгоритм учёта пульса я придумал самый простой, вычисляю среднее значение датчика (это синяя полоска на графике) но с небольшой коррекцией вверх на +20%, чтоб нижний шум убрать, а делее смотрим переход текущего значения через эту среднюю. Если тренд на увеличение - значит удар, если спад вниз - цикл закончен.
Сохраняем время первого удара и вычитаем из предыдущего, делим 60 секунд на ту разницу - получаем удары в минуту.
Но это не весело, ещё я включил в код буфер на четыре последних значения, чтоб учитывать среднее между ними, иначе цифры постоянно прыгают, и добавил алгоритм определения аритмии. Если следующий удар сердца приходит ранее чем 80% от прогнозируемого.




Код программы (если кому интересно):
[Нажмите, чтобы посмотреть]
#include <EEPROM.h>

#define LED 13

#define CLK 9
#define DIO 8

#include <TM1637Display.h>
TM1637Display display(CLK, DIO);

int sensorValue=0;
int sensorMax=0;

int lastP=0;
long sum=0;
int count=0,cnt=5;
int disp=0;
int avg=0;
long timeE;
long timeL[10];
int x,y,k,m,n,p;
int BPM=0;
int IBI=0;
int low,high=1;
uint8_t data[] = { 0x0, 0x0, 0x0, 0x0 };

int pr[]={
0b00000000,
};

void setup() {

  long a;
  EEPROM.get(8,a);
  display.setBrightness(7+3);
  a++;
  if(a>5)a=1;
//  EEPROM.put(8,a);

  pinMode(LED, OUTPUT);
  pinMode(A0, INPUT);

  Serial.begin(9600);

}

void loop() {

sensorValue = analogRead(0);

count++;
if( count>0 && sensorValue>0 && count<5000){
  sum+=sensorValue;
} else {
  sum=0;
  count=1;
  cnt=5;
}
avg = sum/count * 1.2;


if(sensorValue > lastP && lastP >= avg){
  digitalWrite(LED, HIGH);
  if(high==1){
    high=0;
    sensorMax=lastP;
    timeL[5] = timeL[4];
    timeL[4] = timeL[3];
    timeL[3] = timeL[2];
    timeL[2] = timeL[1];
    timeL[1] = timeL[0];
    timeL[0] = timeE;
    timeE=millis();
    p = timeL[4]-timeL[5];
    n = timeL[3]-timeL[4];
    m = timeL[2]-timeL[3];
    k = timeL[1]-timeL[2];
    x = timeL[0]-timeL[1];
    y = timeE-timeL[0];
    if ( y < 1000 && y > 0) IBI = y;
//    if(cnt==0 && y < 1000 ){
      BPM = 60000/((y+x+k+m)/4);
      if (BPM > 900 || BPM < 0 ) BPM = 0;
      disp=1;
//    } else {
//      data[1]=display.encodeDigit(cnt);
//      cnt--;
//      disp=0;
//    }
  }
  if( y < x*0.8 && cnt==0 ){
    data[0] = 0b01101011;
  } else {
    data[0] = 0b01100011;
  }
  display.setSegments(data);

} else if(sensorValue < lastP && lastP < avg) {
  data[0] = 0x0;
  data[1] = 0x0;
  digitalWrite(LED, LOW);
  high=1;
} else {
//  data[0] = 0x0;
//  digitalWrite(LED, LOW);
}

Serial.print(avg);
Serial.print(",");
Serial.print(IBI);
Serial.print(",");
Serial.print(sensorValue);
Serial.print(",");
Serial.println(BPM);

if(disp==1){
    int a = BPM/100;
    if(a==0){
      data[1]=0x0;
    } else {
      data[1] = display.encodeDigit(a);
    }
    data[2] = display.encodeDigit((BPM-(int)(BPM/100)*100)/10);
    data[3] = display.encodeDigit((int)BPM % 10);
}

if(sensorValue==0){
  data[1]=data[1] | 0b10000000;
}

display.setSegments(data);

lastP = sensorValue;

delay(3);

}


?

Log in

No account? Create an account