WorldFamousElectronics / PulseSensor_Amped_Arduino

PulseSensor Arduino code for BPM and Processing-Visualizer
MIT License
235 stars 206 forks source link

BPM sometimes shoots up without a change in heart beat #73

Open jens-k opened 6 years ago

jens-k commented 6 years ago

I have a problem similar to at least two others that have been reported (but I couldn't find a solution so far). The bpm output of the sensor sometimes goes up to unrealistic values (~150) while the times series of the heartbeat appears to be transmitted truthfully. The problem becomes evident already when I look at the 'raw' output in the serial monitor (so its not my later processing of that output). Even without being attached to a finger the bpm shoots up once in a while, while the heart beat time series remains quite constant and certainly below my provided threshold.

I'm using an Arduino Uno Rev 3. To the same board I have attached a GSR and an EMG sensor (which is handled in the code below, too). I use version 1.2.3 of the PulseSensor Playground library (since later versions gave me compiler errors).

I know that other people had the same problem too, but in one case the sensor was fake, in the other I think no solution was found. I'm using the sensor to teach medical students how mental states influence physiological responses by visualizing several physiological parameters with matlab while they perform mentally stressful tasks. I and they would be really grateful if there was a solution to the problem.

Find attached a photo of my sensor and my code.

Thanks! Jens


define USE_ARDUINO_INTERRUPTS true

include

const int OUTPUT_TYPE = SERIAL_PLOTTER;

const int PIN_INPUT = A0; const int PIN_BLINK = 13; // Pin 13 is the on-board LED const int THRESHOLD = 720; // tried this with values up to 950 int h1; int h2; int h3; int g; int m;

PulseSensorPlayground pulseSensor; // all the PulseSensor Playground functions.

void setup() {

Serial.begin(115200);

// Configure the PulseSensor manager. pulseSensor.analogInput(PIN_INPUT); pulseSensor.blinkOnPulse(PIN_BLINK);

pulseSensor.setSerial(Serial); pulseSensor.setOutputType(OUTPUT_TYPE); pulseSensor.setThreshold(THRESHOLD);

// Now that everything is ready, start reading the PulseSensor signal. if (!pulseSensor.begin()) { / If your Sketch hangs here, try ProcessEverySample.ino, which doesn't use interrupts. / for (;;) { // Flash the led to show things didn't work. digitalWrite(PIN_BLINK, LOW); delay(50); digitalWrite(PIN_BLINK, HIGH); delay(50); } } }

void loop() { delay(20); // was 20

// Get the sensor data h1 = pulseSensor.getLatestSample(); h2 = pulseSensor.getBeatsPerMinute(); h3 = pulseSensor.getInterBeatIntervalMs(); g = analogRead(A1); m = analogRead(A2);

// Send data to serial port Serial.print(h1); // Full heart time series Serial.print(F(",")); Serial.print(h2); // BPM Serial.print(F(",")); Serial.print(h3); // IBI Serial.print(F(",")); Serial.print(g); // GSR Serial.print(F(",")); Serial.print(m); // Muscle Serial.println();

if (pulseSensor.sawStartOfBeat()) { pulseSensor.outputBeat(); } }

img_20180525_182644

biomurph commented 6 years ago

First off, I am using Arduino 1.8.5, along with the PulseSensor Playground version 1.4.5, and I am able to compile for the UNO board. What is the error message you are getting? BTW, I will be updating to 1.4.6 today.

I just tried your code (targeting a Leonardo, which shouldn't make a difference). I don't have any GSR or EMG sensors, so I see cross-talk on the A1 and A2 pins (lower amplitude pulse wave). I don't get any of the high value BPM that you're talking about... Granted I did not run it for a long time.

You can comment out or remove the lines if (pulseSensor.sawStartOfBeat()) { pulseSensor.outputBeat(); } Since you are already reporting the values of BPM and IBI in the code you wrote. The outputBeat function will simply report those values.

When the PulseSensor is idle, it is possible to get erroneous pulses. We don't have any stock noise cancelling code, because each individual setup is different, but if you look at the Tools page, there is a function call that returns the amplitude of the pulse wave. You may be able to use that to remove erroneous beats.

Please share a screen shot of the issue that you are seeing. Also, this is the deprecated code repo. This conversation really belongs on the Playground repo.

jens-k commented 6 years ago

Hi biomurph, thanks for the reply. If I remember correctly, there was some function that now took a different number of input parameters but was still called with the old number. I tried to debug it for some time but then gave up. Do you think the issue could be solved by an update?

The issue comes up about every 10 sec for me, so you don't have to test for long.

K thx, I will remove those lines.

I gave the idle scenario because it shows best how strong the dissociation of reported heart beat and reported bpm is. It is not simply that there is high-frequency noise (assuming the bpm calculation is done on the same signal that is provided by getLatestSample and not on a signal that is filtered differently).

I can send a screenshot once I'm back in the lab but its really just one column that shows reasonable heartbeat values over time and a second column that shows reasonable bpms but intermittently goes up to 150 or so.

Sorry for posting in the wrong repo, I just looked for issues and opened a new one. I cannot transfer it now, though. Should I open a new one and close this?

jens-k commented 6 years ago

After updating to PulseSensor Playground 1.4.5 I get the following compilation error (with my script but also all playground example scripts):

C:\Users\XXX\Documents\Arduino\libraries\PulseSensor_Playground\src\PulseSensorPlayground.cpp: In member function 'void PulseSensorPlayground::setThreshold(int, int)': C:\Users\XXX\Documents\Arduino\libraries\PulseSensor_Playground\src\PulseSensorPlayground.cpp:194:13: error: return-statement with a value, in function returning 'void' [-fpermissive] return -1; // out of range. ^ Error compiling.

I'm currently using Arduino 1.6.5 (company-provided computer), if this might be an issue, I can try to update next week.

biomurph commented 6 years ago

@jens-k please update to the latest library version. That should fix the problem of compilation.

jens-k commented 6 years ago

Thanks biomurph. I now updated to Arduino 1.8.5 and PulseSensorPlayground library version 1.4.9 (interestingly, my old Arduino version did not show me library versions > 1.4.5). I also took out the pulseSensor.outputBeat(); command.

The code compiles, but the main problem still persists. Here is a screenshot of the serial plotter. Please note that pulse (red) and IBI (green) appear to vary surprisingly independently (seem to have different update cycles) and both change drastically while the heart beat time series looks rather stationary. The threshold was set to 850 and thus far higher than the secondary pulse peak.

plotter

biomurph commented 6 years ago

OK, I think I can see what the problem might be. It looks like there is a second upward going wave in the signal that you are getting. That second wave seems to be the culprit. What happens inside the library is that the threshold value is dynamic, and it adjust with every beat to ensure that it is basically triggering on the middle of the upward going pulse wave. It looks like your signal is tricking the library into thinking there is a pulse when there is not a pulse... here's a possible fix for your situation.

On line 147 of the PulseSensor.cpp file inside the library, there is a line if ( (Signal > thresh) && (Pulse == false) && (N > (IBI / 5) * 3) ) { What that is doing is it's checking the signal to see if it has risen above the threshold. Then, it checks to see if we are not already inside of a 'pulse', then it checks to see if it has been long enough. In this case, 'long enough' means 3/5 of the last IBI time. Here's where you could make an adjustment, in the timing. 3/5 is 0.6 of the last IBI 3/4 is 0.75 of the last IBI 4/5 is 0.8 of the last IBI

Try these alternatives to the formula

(N > (IBI/4) 3) or (N > (IBI/5) 4)

That could help you 'clear' the pulse wave timing thing. Does that make sense?

I offer this suggestion, because the way that the threshold is set and then 'travels' with the pulse wave is more tedious to 'fix'. And, if this works, then we know what the problem is.

jens-k commented 6 years ago

I tried all of those, without much of an effect, even with a threshold of 920 and setting the 146 to N>500. The maximum BPM is lower now, at around 130 instead of 160, but most of the time I'm shown to be at ~100 (while actually I have a resting heart rate in the 70s).

By the way, I tried the same with another arduino and another one of your PulseSensors, same problem.

Would you have any other suggestions?

biomurph commented 6 years ago

Huh. Well, in my experience with pulse signals that are very large or small I don't get this effect. Even running for a long time.

I think the next thing to try is to get more verbose. For example, serial print the thresh variable...

Also, looking at your serial monitor data, I can see that every time the green IBI wave changes, Arduino thinks there is a beat. Notice that it corresponding with peaks that are not actual pulse. This is the problem.

Try commenting out the line

thresh = amp / 2 + T;

In the PulseSensor.cpp file. That will disable the self adjusting threshold

jens-k commented 6 years ago

Oh yeah, that gives me much more realistic and consistent readings (see below). It's probably not optimal to use a static threshold but, considering others had the same problem, could you maybe add a method to turn the threshold off without getting into the library code? ..e.g.: pulseSensor.dynamicThreshold(false);

Thanks for your help. I'll try that out with students soon.

image