2017-06-12 81 views
1

tldr;使用脈衝傳感器和mkr1000計算BPM的簡單/合理的方法(對於初學者)是什麼?我不希望任何圖表或處理草圖,但只是打印BPM值脈衝傳感器+ arduino mkr1000來計算BPM

請多多包涵,我在這一個新手,我已經盡我所能理解這一點,並解決這個問題,但不成功。

我正在使用帶有Arduino mkr1000的脈衝傳感器(SEN-11574)來計算BPM並將其打印在串行監視器中。我能夠使用他們的首發代碼

// Variables 
int PulseSensorPurplePin = 0;  // Pulse Sensor PURPLE WIRE connected to ANALOG PIN 0 
int LED13 = 13; // The on-board Arduion LED 


int Signal;    // holds the incoming raw data. Signal value can range from 0-1024 
int Threshold = 550;   // Determine which Signal to "count as a beat", and which to ingore. 


// The SetUp Function: 
void setup() { 
    pinMode(LED13,OUTPUT);   // pin that will blink to your heartbeat! 
    Serial.begin(9600);   // Set's up Serial Communication at certain speed. 

} 

// The Main Loop Function 
void loop() { 

    Signal = analogRead(PulseSensorPurplePin); // Read the PulseSensor's value. 
               // Assign this value to the "Signal" variable. 

    Serial.println(Signal);     // Send the Signal value to Serial Plotter. 


    if(Signal > Threshold){       // If the signal is above "550", then "turn-on" Arduino's on-Board LED. 
    digitalWrite(LED13,HIGH);   
    } else { 
    digitalWrite(LED13,LOW);    // Else, the sigal must be below "550", so "turn-off" this LED. 
    } 


delay(10); 
} 

然而,真正的問題是,我無法利用現有的示例代碼來計算BPM獲得原始讀數據我瞭解on their website here ,中斷定時器功能在中斷.ino文件與mkr1000不兼容。附上這段代碼供您參考。

// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE. 
// Timer 2 makes sure that we take a reading every 2 miliseconds 
ISR(TIMER2_COMPA_vect){       // triggered when Timer2 counts to 124 
    cli();          // disable interrupts while we do this 
    Signal = analogRead(pulsePin);    // read the Pulse Sensor 
    sampleCounter += 2;       // keep track of the time in mS with this variable 
    int N = sampleCounter - lastBeatTime;  // monitor the time since the last beat to avoid noise 

    // find the peak and trough of the pulse wave 
    if(Signal < thresh && N > (IBI/5)*3){  // avoid dichrotic noise by waiting 3/5 of last IBI 
    if (Signal < T){      // T is the trough 
     T = Signal;       // keep track of lowest point in pulse wave 
    } 
    } 

    if(Signal > thresh && Signal > P){   // thresh condition helps avoid noise 
    P = Signal;        // P is the peak 
    }          // keep track of highest point in pulse wave 

    // NOW IT'S TIME TO LOOK FOR THE HEART BEAT 
    // signal surges up in value every time there is a pulse 
    if (N > 250){         // avoid high frequency noise 
    if ((Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3)){ 
     Pulse = true;        // set the Pulse flag when we think there is a pulse 
     digitalWrite(blinkPin,HIGH);    // turn on pin 13 LED 
     IBI = sampleCounter - lastBeatTime;   // measure time between beats in mS 
     lastBeatTime = sampleCounter;    // keep track of time for next pulse 

     if(secondBeat){      // if this is the second beat, if secondBeat == TRUE 
     secondBeat = false;     // clear secondBeat flag 
     for(int i=0; i<=9; i++){    // seed the running total to get a realisitic BPM at startup 
      rate[i] = IBI; 
     } 
     } 

     if(firstBeat){       // if it's the first time we found a beat, if firstBeat == TRUE 
     firstBeat = false;     // clear firstBeat flag 
     secondBeat = true;     // set the second beat flag 
     sei();        // enable interrupts again 
     return;        // IBI value is unreliable so discard it 
     } 


     // keep a running total of the last 10 IBI values 
     word runningTotal = 0;     // clear the runningTotal variable 

     for(int i=0; i<=8; i++){    // shift data in the rate array 
     rate[i] = rate[i+1];     // and drop the oldest IBI value 
     runningTotal += rate[i];    // add up the 9 oldest IBI values 
     } 

     rate[9] = IBI;       // add the latest IBI to the rate array 
     runningTotal += rate[9];    // add the latest IBI to runningTotal 
     runningTotal /= 10;      // average the last 10 IBI values 
     BPM = 60000/runningTotal;    // how many beats can fit into a minute? that's BPM! 
     QS = true;        // set Quantified Self flag 
     // QS FLAG IS NOT CLEARED INSIDE THIS ISR 
    } 
    } 

    if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over 
    digitalWrite(blinkPin,LOW);   // turn off pin 13 LED 
    Pulse = false;       // reset the Pulse flag so we can do it again 
    amp = P - T;       // get amplitude of the pulse wave 
    thresh = amp/2 + T;     // set thresh at 50% of the amplitude 
    P = thresh;       // reset these for next time 
    T = thresh; 
    } 

    if (N > 2500){       // if 2.5 seconds go by without a beat 
    thresh = 530;       // set thresh default 
    P = 512;        // set P default 
    T = 512;        // set T default 
    lastBeatTime = sampleCounter;   // bring the lastBeatTime up to date 
    firstBeat = true;      // set these to avoid noise 
    secondBeat = false;     // when we get the heartbeat back 
    } 

    sei();         // enable interrupts when youre done! 
}// end isr 

在中斷備註文件,他們提到另一個變通對於不是與此代碼兼容的處理器,但即使按照說明資訊小時後,該代碼沒有與計時器錯誤工作,再次中斷功能。

接下來,我使用了this guide,但是它又不起作用,只是打印不斷變化的原始信號值(S1023)。代碼附加(2個選項卡):

/* Pulse Sensor Amped 1.4 by Joel Murphy and Yury Gitman http://www.pulsesensor.com 
Adapted by sdizdarevic 
---------------------- Notes ---------------------- ---------------------- 
This code: 
1) Blinks an LED to User's Live Heartbeat PIN 6 
2) Fades an LED to User's Live HeartBeat 
3) Determines BPM 
4) Prints All of the Above to Serial 
Read Me: 
https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino/blob/master/README.md 
----------------------  ---------------------- ---------------------- 
*/ 


// Variables 
int pulsePin = 0;     // Pulse Sensor purple wire connected to analog pin 0 
int blinkPin = 6;    // pin to blink led at each beat 
//int fadePin = 5;     // pin to do fancy classy fading blink at each beat 
//int fadeRate = 0;     // used to fade LED on with PWM on fadePin 

// Volatile Variables, used in the interrupt service routine! 
volatile int BPM;     // int that holds raw Analog in 0. updated every 2mS 
volatile int Signal;    // holds the incoming raw data 
volatile int IBI = 600;    // int that holds the time interval between beats! Must be seeded! 
volatile boolean Pulse = false;  // "True" when User's live heartbeat is detected. "False" when not a "live beat". 
volatile boolean QS = false;  // becomes true when Arduoino finds a beat. 



volatile int rate[10];     // array to hold last ten IBI values 
volatile unsigned long sampleCounter = 0;   // used to determine pulse timing 
volatile unsigned long lastBeatTime = 0;   // used to find IBI 
volatile int P =512;      // used to find peak in pulse wave, seeded 
volatile int T = 512;      // used to find trough in pulse wave, seeded 
volatile int thresh = 525;    // used to find instant moment of heart beat, seeded 
volatile int amp = 100;     // used to hold amplitude of pulse waveform, seeded 
volatile boolean firstBeat = true;  // used to seed rate array so we startup with reasonable BPM 
volatile boolean secondBeat = false;  // used to seed rate array so we startup with reasonable BPM 




// Regards Serial OutPut -- Set This Up to your needs 
static boolean serialVisual = false; // Set to 'false' by Default. Re-set to 'true' to see Arduino Serial Monitor ASCII Visual Pulse 


void setup(){ 
    pinMode(blinkPin,OUTPUT);   // pin that will blink to your heartbeat! 
    //pinMode(fadePin,OUTPUT);   // pin that will fade to your heartbeat! 
    Serial.begin(115200);    // we agree to talk fast! 
    //interruptSetup();     // sets up to read Pulse Sensor signal every 2mS 
    // IF YOU ARE POWERING The Pulse Sensor AT VOLTAGE LESS THAN THE BOARD VOLTAGE, 
    // UN-COMMENT THE NEXT LINE AND APPLY THAT VOLTAGE TO THE A-REF PIN 
// analogReference(EXTERNAL); 
} 


// Where the Magic Happens 
void loop(){ 

// 
// 
Signal = analogRead(pulsePin);    // read the Pulse Sensor 
    sampleCounter += 2;       // keep track of the time in mS with this variable 
    int N = sampleCounter - lastBeatTime;  // monitor the time since the last beat to avoid noise 

    // find the peak and trough of the pulse wave 
    if(Signal < thresh && N > (IBI/5)*3){  // avoid dichrotic noise by waiting 3/5 of last IBI 
    if (Signal < T){      // T is the trough 
     T = Signal;       // keep track of lowest point in pulse wave 
    } 
    } 

    if(Signal > thresh && Signal > P){   // thresh condition helps avoid noise 
    P = Signal;        // P is the peak 
    }          // keep track of highest point in pulse wave 

    // NOW IT'S TIME TO LOOK FOR THE HEART BEAT 
    // signal surges up in value every time there is a pulse 
    if (N > 250){         // avoid high frequency noise 
    if ((Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3)){   
     Pulse = true;        // set the Pulse flag when we think there is a pulse 
     digitalWrite(blinkPin,HIGH);    // turn on pin 13 LED 
     IBI = sampleCounter - lastBeatTime;   // measure time between beats in mS 
     lastBeatTime = sampleCounter;    // keep track of time for next pulse 

     if(secondBeat){      // if this is the second beat, if secondBeat == TRUE 
     secondBeat = false;     // clear secondBeat flag 
     for(int i=0; i<=9; i++){    // seed the running total to get a realisitic BPM at startup 
      rate[i] = IBI;      
     } 
     } 

     if(firstBeat){       // if it's the first time we found a beat, if firstBeat == TRUE 
     firstBeat = false;     // clear firstBeat flag 
     secondBeat = true;     // set the second beat flag 

     return;        // IBI value is unreliable so discard it 
     } 


     // keep a running total of the last 10 IBI values 
     word runningTotal = 0;     // clear the runningTotal variable  

     for(int i=0; i<=8; i++){    // shift data in the rate array 
     rate[i] = rate[i+1];     // and drop the oldest IBI value 
     runningTotal += rate[i];    // add up the 9 oldest IBI values 
     } 

     rate[9] = IBI;       // add the latest IBI to the rate array 
     runningTotal += rate[9];    // add the latest IBI to runningTotal 
     runningTotal /= 10;      // average the last 10 IBI values 
     BPM = 60000/runningTotal;    // how many beats can fit into a minute? that's BPM! 
     QS = true;        // set Quantified Self flag 
     // QS FLAG IS NOT CLEARED INSIDE THIS ISR 
    }      
    } 

    if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over 
    digitalWrite(blinkPin,LOW);   // turn off pin 13 LED 
    Pulse = false;       // reset the Pulse flag so we can do it again 
    amp = P - T;       // get amplitude of the pulse wave 
    thresh = amp/2 + T;     // set thresh at 50% of the amplitude 
    P = thresh;       // reset these for next time 
    T = thresh; 
    } 

    if (N > 2500){       // if 2.5 seconds go by without a beat 
    thresh = 512;       // set thresh default 
    P = 512;        // set P default 
    T = 512;        // set T default 
    lastBeatTime = sampleCounter;   // bring the lastBeatTime up to date   
    firstBeat = true;      // set these to avoid noise 
    secondBeat = false;     // when we get the heartbeat back 
    } 





    serialOutput() ;  

    if (QS == true){  // A Heartbeat Was Found 
         // BPM and IBI have been Determined 
         // Quantified Self "QS" true when arduino finds a heartbeat 
     // fadeRate = 255;   // Makes the LED Fade Effect Happen 
           // Set 'fadeRate' Variable to 255 to fade LED with pulse 
     serialOutputWhenBeatHappens(); // A Beat Happened, Output that to serial.  
     QS = false;      // reset the Quantified Self flag for next time  
    } 

// ledFadeToBeat();      // Makes the LED Fade Effect Happen 
    delay(20);        // take a break 
} 





/*void ledFadeToBeat(){ 
    fadeRate -= 15;       // set LED fade value 
    fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers! 
    //analogWrite(fadePin,fadeRate);   // fade LED 
    } 
*/ 

SerialHandling文件:

////////// 
///////// All Serial Handling Code, 
///////// It's Changeable with the 'serialVisual' variable 
///////// Set it to 'true' or 'false' when it's declared at start of code. 
///////// 

void serialOutput(){ // Decide How To Output Serial. 
if (serialVisual == true){ 
    arduinoSerialMonitorVisual('-', Signal); // goes to function that makes Serial Monitor Visualizer 
} else{ 
     sendDataToSerial('S', Signal);  // goes to sendDataToSerial function 
}   
} 


// Decides How To OutPut BPM and IBI Data 
void serialOutputWhenBeatHappens(){  
if (serialVisual == true){   // Code to Make the Serial Monitor Visualizer Work 
    Serial.print("*** Heart-Beat Happened *** "); //ASCII Art Madness 
    Serial.print("BPM: "); 
    Serial.print(BPM); 
    Serial.print(" "); 
} else{ 
     sendDataToSerial('B',BPM); // send heart rate with a 'B' prefix 
     sendDataToSerial('Q',IBI); // send time between beats with a 'Q' prefix 
} 
} 



// Sends Data to Pulse Sensor Processing App, Native Mac App, or Third-party Serial Readers. 
void sendDataToSerial(char symbol, int data){ 
    Serial.print(symbol); 

    Serial.println(data);     
    } 


// Code to Make the Serial Monitor Visualizer Work 
void arduinoSerialMonitorVisual(char symbol, int data){  
    const int sensorMin = 0;  // sensor minimum, discovered through experiment 
const int sensorMax = 1024; // sensor maximum, discovered through experiment 

    int sensorReading = data; 
    // map the sensor range to a range of 12 options: 
    int range = map(sensorReading, sensorMin, sensorMax, 0, 11); 

    // do something different depending on the 
    // range value: 
    switch (range) { 
    case 0:  
    Serial.println("");  /////ASCII Art Madness 
    break; 
    case 1: 
    Serial.println("---"); 
    break; 
    case 2:  
    Serial.println("------"); 
    break; 
    case 3:  
    Serial.println("---------"); 
    break; 
    case 4: 
    Serial.println("------------"); 
    break; 
    case 5: 
    Serial.println("--------------|-"); 
    break; 
    case 6: 
    Serial.println("--------------|---"); 
    break; 
    case 7: 
    Serial.println("--------------|-------"); 
    break; 
    case 8: 
    Serial.println("--------------|----------"); 
    break; 
    case 9:  
    Serial.println("--------------|----------------"); 
    break; 
    case 10: 
    Serial.println("--------------|-------------------"); 
    break; 
    case 11: 
    Serial.println("--------------|-----------------------"); 
    break; 

    } 
} 

串行監視器只顯示這些數字,是不斷變化的:

S797 
S813 
S798 
S811 
S822 
S802 
S821 
S819 
S818 
S806 
S797 
S797 
S812 
S816 
S794 
S820 
S821 
S808 
S816 
S820 
S803 
S810 
S811 
S806 
S822 
S817 
S811 
S822 
S800 
S820 
S799 
S800 
S815 
S809 
S820 
S822 
S821 
S809 
S796 
S821 
S816 
S798 
S820 

總而言之,我希望如果有人能夠幫助我以更簡便的方式計算BPM,而無需處理BPM的可視化問題。

對不起,很長的文章,謝謝!

+0

Arduino是不是C和不完全C++兩種。不要垃圾標籤。 – Olaf

回答

0

這是我做到了,立交橋沒有中斷對我的板:

#define pulsePin A0 

// VARIABLES 
int rate[10];      
unsigned long sampleCounter = 0; 
unsigned long lastBeatTime = 0; 
unsigned long lastTime = 0, N; 
int BPM = 0; 
int IBI = 0; 
int P = 512; 
int T = 512; 
int thresh = 512; 
int amp = 100;     
int Signal; 
boolean Pulse = false; 
boolean firstBeat = true;   
boolean secondBeat = true; 
boolean QS = false;  

void setup() { 
    Serial.begin(9600); 

} 

void loop() { 

       if (QS == true) { 
       Serial.println("BPM: "+ String(BPM)); 
       QS = false; 
       } else if (millis() >= (lastTime + 2)) { 
       readPulse(); 
       lastTime = millis(); 
       }  
} 



void readPulse() { 

    Signal = analogRead(pulsePin);    
    sampleCounter += 2;       
    int N = sampleCounter - lastBeatTime; 

    detectSetHighLow(); 

    if (N > 250) { 
    if ((Signal > thresh) && (Pulse == false) && (N > (IBI/5) * 3)) 
     pulseDetected(); 
    } 

    if (Signal < thresh && Pulse == true) { 
    Pulse = false; 
    amp = P - T; 
    thresh = amp/2 + T; 
    P = thresh; 
    T = thresh; 
    } 

    if (N > 2500) { 
    thresh = 512; 
    P = 512; 
    T = 512; 
    lastBeatTime = sampleCounter; 
    firstBeat = true;    
    secondBeat = true;   
    } 

} 

void detectSetHighLow() { 

    if (Signal < thresh && N > (IBI/5) * 3) { 
    if (Signal < T) {      
     T = Signal;       
    } 
    } 

    if (Signal > thresh && Signal > P) {  
    P = Signal;       
    }          

} 

void pulseDetected() { 
    Pulse = true;       
    IBI = sampleCounter - lastBeatTime;  
    lastBeatTime = sampleCounter;   

    if (firstBeat) {      
    firstBeat = false;     
    return;        
    } 
    if (secondBeat) {      
    secondBeat = false;     
    for (int i = 0; i <= 9; i++) { 
     rate[i] = IBI; 
    } 
    } 

    word runningTotal = 0;     

    for (int i = 0; i <= 8; i++) {   
    rate[i] = rate[i + 1];    
    runningTotal += rate[i];   
    } 

    rate[9] = IBI;      
    runningTotal += rate[9];    
    runningTotal /= 10;     
    BPM = 60000/runningTotal;   
    QS = true;        
} 
+0

非常感謝。這絕對有效。我看到了BPM值,當串行監視器開始打印時,它以40s的正常範圍開始,10秒內開始增加到200以上?我在想,如果這是我的mkr1000或代碼故障?我有兩個不同的脈衝傳感器,它們都有相同的問題! 此外,我還計劃使用數字引腳來計算溫度,並想知道是否有我應該小心的事情?再次,謝謝你。這真的有幫助! – rp2402

+0

歡迎您投票並接受答案。當添加其他傳感器時,確保在計算BPM時增加延遲,當QS爲真時,可以在第一個if語句內添加延遲 – Wadaane

+0

關於跳躍式讀數,這與傳感器的質量以及您如何使用它有關,因爲它使用反射光線,所以你需要確保它是隔離的。另外,我是如何添加一個if語句來檢查BPM是否在一定範圍內(40-100),當它不是我打印的「無效讀取」時,再次,所有額外的代碼都需要在第一個if語句中。 – Wadaane

-1

我使用的傳感器是DFRobot Piezo Disc振動傳感器模塊。

void setup() { 



Serial.begin(57600); 
} 

void loop() { 
    int avg = 0; 
    for(int i=0;i<64;i++){ 
    avg+=analogRead(A2); 
    } 
    Serial.println(avg/64,DEC); 
    delay(5); 
} 


void setup() { 
    Serial.begin(57600); 
} 

void loop() { 
    int avg = 0; 
    for(int i=0;i<64;i++){ 
    avg+=analogRead(A2); 
    } 
    Serial.println(avg/64,DEC); 
    delay(5); 
} 



When defining an arbitrary threshold (e.g. half of the maximum measured value), the rising edge of the signal will pass the threshold once per heartbeat, making measuring it as simple as measuring the time between two successive beats. For less jitter, I chose to calculate the heart rate using the average of the last 16 time differences between the beats. 

代碼計算的心臟速率,並在每一個節拍輸出平均心臟率在過去16次:

int threshold = 60; 
int oldvalue = 0; 
int newvalue = 0; 
unsigned long oldmillis = 0; 
unsigned long newmillis = 0; 
int cnt = 0; 
int timings[16]; 

void setup() { 
    Serial.begin(57600); 
} 

void loop() { 
    oldvalue = newvalue; 
    newvalue = 0; 
    for(int i=0; i<64; i++){ // Average over 16 measurements 
    newvalue += analogRead(A2); 
    } 
    newvalue = newvalue/64; 
    // find triggering edge 
    if(oldvalue<threshold && newvalue>=threshold){ 
    oldmillis = newmillis; 
    newmillis = millis(); 
    // fill in the current time difference in ringbuffer 
    timings[cnt%16]= (int)(newmillis-oldmillis); 
    int totalmillis = 0; 
    // calculate average of the last 16 time differences 
    for(int i=0;i<16;i++){ 
     totalmillis += timings[i]; 
    } 
    // calculate heart rate 
    int heartrate = 60000/(totalmillis/16); 
    Serial.println(heartrate,DEC); 
    cnt++; 
    } 
    delay(5); 
} 



int threshold = 60; 
int oldvalue = 0; 
int newvalue = 0; 
unsigned long oldmillis = 0; 
unsigned long newmillis = 0; 
int cnt = 0; 
int timings[16]; 

void setup() { 
    Serial.begin(57600); 
} 

void loop() { 
    oldvalue = newvalue; 
    newvalue = 0; 
    for(int i=0; i<64; i++){ // Average over 16 measurements 
    newvalue += analogRead(A2); 
    } 
    newvalue = newvalue/64; 
    // find triggering edge 
    if(oldvalue<threshold && newvalue>=threshold){ 
    oldmillis = newmillis; 
    newmillis = millis(); 
    // fill in the current time difference in ringbuffer 
    timings[cnt%16]= (int)(newmillis-oldmillis); 
    int totalmillis = 0; 
    // calculate average of the last 16 time differences 
    for(int i=0;i<16;i++){ 
     totalmillis += timings[i]; 
    } 
    // calculate heart rate 
    int heartrate = 60000/(totalmillis/16); 
    Serial.println(heartrate,DEC); 
    cnt++; 
    } 
    delay(5); 
} 

如果你想在家裏嘗試這個,只需連接模擬輸出將傳感器連接到A2(或更改代碼)並連接傳感器的5V和GND線。