Improve HR detection based on dynamic bounds#10
Conversation
|
@produceconsumerobot Can you test it for the spurious dicrotic notch detection if you get the time? |
|
@nitin710 We're still missing the beats following a dicrotic notch detection. My hunch is that we need to slow down both the average and the range filters. It would actually likely be really useful to create a bank of short serial output snippets for different physiological and non-physiological perturbations. gsheets can implement a basic single pole IIR pretty easily, so if we had some data sets to test against, I think it would be a lot easier to hone in on what our compromise should look like. |
Calm 01calm-02deep breath-01stand-01stand-02motion artifactholding breathholdingBreath-02sit-01 |
…r algorithm parameters accordingly
|
@produceconsumerobot Added the new code changes. This PR is ready for review. I will remove the |
produceconsumerobot
left a comment
There was a problem hiding this comment.
Review 01
I'd like to see results showing physiological performance on PPG 100Hz.
Otherwise generally looks good except for a little bit of possible cruft.
src/heartRate.cpp
Outdated
| { | ||
|
|
||
| IR_AC_Max = IR_AC_Signal_max; //Adjust our AC max and min |
There was a problem hiding this comment.
I'm not sure I understand why IR_AC_Max and IR_AC_Min exist. It seems redundant.
src/heartRate.cpp
Outdated
| if ((IR_AC_Max - IR_AC_Min) > 20 & (IR_AC_Max - IR_AC_Min) < 2000) | ||
|
|
||
| filteredAcAmp = acSignalAmplitudeFilter.filter(IR_AC_amplitude); | ||
| acRange = acRangeFitler.filter(filteredAcAmp); |
There was a problem hiding this comment.
I think we want the following
acRange = acRangeFitler.filter(IR_AC_amplitude);
because filtering a filtered signal adds clunky interdependencies. Unless there's a reason to do otherwise, it's better to base each separately on the original signal.
src/heartRate.cpp
Outdated
| acAmpLowerBound = filteredAcAmp - (acRange * AC_RANGE_MULTIPLIER); // Lower bound is adjusted based on acRange and range multiplier | ||
|
|
||
| Serial.print(millis()); Serial.print(","); | ||
| if (IR_AC_amplitude > IR_AC_MIN_AMP & IR_AC_amplitude < IR_AC_MAX_AMP) |
There was a problem hiding this comment.
Use && unless bit operation is intended (or you're a debugging masochist)
There was a problem hiding this comment.
good catch, fixed.
for future reference, definitely not a debugging masochist
src/heartRate.cpp
Outdated
| @@ -109,19 +121,45 @@ bool checkForBeat(int32_t sample, int16_t &iirFiltData, bool dcRemoved) | |||
| // Detect positive zero crossing (rising edge) | |||
| if ((IR_AC_Signal_Previous < 0) & (IR_AC_Signal_Current >= 0)) | |||
There was a problem hiding this comment.
Use && unless bit operation is intended (or you're a debugging masochist)
There was a problem hiding this comment.
good catch, fixed.
testing on 100 hz PPGConclusions
Calm 01deep breath 01stand 01sit 01holding breath 01motion 01 |
…nput. version bump
|
@produceconsumerobot I made the changes as requested in the PR.
I also tested 100Hz PPG with these HR improvements. You can find that in this comment |
































Description
Introduces dynamic bounds check for AC component of filtered PPG:IR channel. This patch improves the overall beat detection.
Requirements
Issues Referenced
Documentation update
Algorithm explanation
Testing
Steps for testing
Heartbeat Det AC Filter Calculatorsheet for testing results + design and analysis of algorithm.AC content filtering
AC content expailned
AC contentin the signal which is determined by heart beats, as well as other physiological and non-physiological factors.Effect of filter cutoff frequency
fn=0.2,0.3,0.4,0.5,0.6As seen, more "strict" filters (0.2 and 0.3) fail to adjust fast enough, causing some of the valid AC signal changes to be flagged as "not a beat"
As seen, the sharp presses cause the AC signal content to spike (as evident in the original IR data). Here low pass filters with cutoff of 0.2/0.3/0.4 perform better than 0.6 (which is not strict enough) and allows the AC signal spikes to be counted as valid "beats
As seen, motion artifacts create a spike in AC content, which are dealt with by the filters pretty efficiently
All filters deal with changes introduced by sit<>stand pretty effectively. Although, the AC changes here are not large and maybe that contributes to the good response of the filters, it may vary for different people, if a bigger physiological response is created when they sit<>stand.
Conclusion
Shared files
flash the Emotibit with the following binaries:
1.8.0.fix-improveHr.1.zip1.8.0.fix-improveHr.2.zip1.8.0.fix-improveHr.3.zipThe code, data and PDFs of the graphs are linked here: improve_hr.zip
For the python script to work "as is", the directory structure for the files should be
Checklist to allow merge
masterScreenshots: