Skip to content

Feat 100 hz ppg#244

Merged
nitin710 merged 31 commits intomasterfrom
feat-100HzPPG
Mar 30, 2023
Merged

Feat 100 hz ppg#244
nitin710 merged 31 commits intomasterfrom
feat-100HzPPG

Conversation

@nitin710
Copy link
Collaborator

@nitin710 nitin710 commented Jan 17, 2023

Description

  • Adds provision for running PPG sensor at 100Hz
  • To compile the source code for 100Hz, you need to use platformIO
  • EmojiBit code has been moved to a separate repository
  • HeartRate was using a generic FIR filter.
    • The frequency response changed with the sampling rate of the input signal.
    • A new FIR filter for 100HZ caused memory resource issues, therefore a new library is being used to filter the signal accordingly using an IIR filter.

Notes for the Reviewer

  • The main code review is scoped to EmotiBit.cpp, EmotiBit.h and heartrate.cpp (in MAX30101 lib).
  • For testing, you may use the compiled binaries associated with this PR, linked here. 1.5.4.feat-100HzPPG.11 fw_PR_224.zip
  • To compile the code from source, you will have to clone 2 additional libraries (see requirements section below)
  • Unless you want to test the platformIO build chain, testing with the compiled binary should enough. (for this stage)
    • The code (except 100Hz PPG) can still be compiled in Arduino IDE.
    • On the same note, you may skip the review of file contents related to platformIO (to a later date when the build chain is moved to PIO).

Requirements

PRs

Issues Referenced

Documentation update

Link to a change in documentation (if any)

Moved to a Future PR

Testing

Test Configuration:

  • Firmware version:
  • Hardware:
  • EmotiBit software version

Hardware Test

  • ❌ EmotiBit V2
  • ✔️ EmotiBit V3
  • ✔️ Emotibit V4
  • ✔️ EmotiBit V5

Firmware Tests

Check EmotiBit Feature Testing Results for more details on test results

  • ISR Intervals
  • ✔️ Free Memory
  • ✔️ Sampling rates
    • emprirical from recorded data
    • from oscilloscope (press i)
  • ✔️ DO
  • ✔️ buffer overflow test
  • ✔️ timestamp check using python script (from sean. check Increase PPG sampling rate to 100Hz #176 )

Heart rate filter details

  • The following plots were created using the python tools in the Arduino-Filters library
  • The dotted lines mark the cutoff frequency of 3Hz and -3dB attenuation
Sampling Rate Lowpass IIR filter Response
25 Hz fw_25hz_iir_filter_response
100hz fw_100hz_iir_filter_response
  • On a high level, the algorithm perform the following
    • removes low frequency DC respiration artifacts using a HPF (As seen by RM plot : respiration removed)
    • Then it low pass filters the above signal to create a smooth waveform to perform zero crossings and peak detection (As seen in the FF plot
    • Note: Ignore the DE and DM plots below as they are non-existant intermediaries.
    • image

Checklist (before merging to master):

  • Set testingMode to TestingMode::NONE
  • Set const bool DIGITAL_WRITE_DEBUG = false (if set true while testing)
  • Update version in EmotiBit.h
  • Update library.properties to the correct version (should match EmotiBit.h)

Screenshots:

produceconsumerobot and others added 22 commits September 17, 2022 07:07
string to base name based on variant build flags.
updated name of variant build flag in platformIO.
fixed charlie plex example to reflect updated location and name of EmotiBit Emojis
@nitin710
Copy link
Collaborator Author

nitin710 commented Mar 9, 2023

Testing with ESP32

  • 25Hz left and 100Hz right (you can notice the empirical frequency estimate in the screenshot)
[{"info":{
"source_id":"EmotiBit FeatherWing",
"hardware_version":"V05c",
"sku":"MD",
"device_id":"MD-V5-0000006",
"feather_version":"Adafruit Feather HUZZAH32",
"feather_wifi_mac_addr":"60:0c:6b:7e:b9:94",
"firmware_version":"1.5.4.feat-100HzPPG.10-TC",
"firmware_variant":"EmotiBit_stock_firmware_PPG_100Hz",
}}]
[{"info":{
"source_id":"EmotiBit FeatherWing",
"hardware_version":"V05c",
"sku":"MD",
"device_id":"MD-V5-0000002",
"feather_version":"Adafruit Feather HUZZAH32",
"feather_wifi_mac_addr":"10:65:90:be:5b:c4",
"firmware_version":"1.5.4.feat-100HzPPG.10-TC",
"firmware_variant":"EmotiBit_stock_firmware",
}}]

image

@nitin710
Copy link
Collaborator Author

nitin710 commented Mar 9, 2023

Testing with M0

  • 25Hz left and 100Hz right (you can notice the empirical frequency estimate in the screenshot)
[{"info":{
"source_id":"EmotiBit FeatherWing",
"hardware_version":"V05c",
"sku":"MD",
"device_id":"MD-V5-0000010",
"feather_version":"Adafruit Feather M0 WiFi",
"feather_wifi_mac_addr":"f8:f0:05:90:08:59",
"firmware_version":"1.5.4.feat-100HzPPG.10-TC",
"firmware_variant":"EmotiBit_stock_firmware_PPG_100Hz",
}}]
[{"info":{
"source_id":"EmotiBit FeatherWing",
"hardware_version":"V04a",
"sku":"MD",
"device_id":"MD-V4-0000648",
"feather_version":"Adafruit Feather M0 WiFi",
"feather_wifi_mac_addr":"f8:f0:05:90:10:10",
"firmware_version":"1.5.4.feat-100HzPPG.10-TC",
"firmware_variant":"EmotiBit_stock_firmware",
}}]

image

@nitin710
Copy link
Collaborator Author

nitin710 commented Mar 20, 2023

25Hz vs 100Hz HR plot

  • Experiment setup: 3-mins sitting -> 3-mins standing -> 3 mins sitting
    25Hz vs 100Hz

(minor ticks on X-Axis every 60 secs)

Conclusion

  • Looks like the HR detected at 25Hz and 100Hz is similar in both "sitting" segments
  • In the standing segments, the PPG sensor seems to be miss-detecting beats. As the observer of raw data stream, the ppg data looked very noisy.
    • Can it be because of the arm orientation being vertical, instead of flat (while sitting) ?

Raw data

@nitin710
Copy link
Collaborator Author

nitin710 commented Mar 29, 2023

@produceconsumerobot Can you test this new FW that prints the AC_signal value on the serial monitor?
1.5.4.feat-100HzPPG.12.zip

@produceconsumerobot
Copy link
Collaborator

produceconsumerobot commented Mar 29, 2023

AC Testing

Conclusions

  • It seems like setting hard thresholds is very fraught because the infiltration of dicrotic notches in some cases (loose) can exceed the size of a full beat in other cases (super tight)
  • Perhaps a better option to manage this would be to create a heartbeat reliability score (0-100) returned from checkForBeat(). This could be calculated using the following formulation:

acLpFreq = 0.1; // This sets how quickly acAmp renormalizes to new levels
filtAcAmp = lpFilter(acAmp, acLpFreq);
acAmpFactor = 4; // This sets the AC amplitude deviation that results in zero weight
hbReliabilityScore = constrain(1- abs(acAmp - filtAcAmp) / filtAcAmp ) / acAmpFactor , 0, 1);

Side note: someday could be useful to have a non-linear, e.g. a sigmoid, transformation on hbReliabilityScore

hbReliabilityScore could then be added to addPacket(beatTime, EmotiBitPacket::TypeTag::INTER_BEAT_INTERVAL, ..., (int8_t) (hbReliabilityScore * 100))
and used to create a HR weighted by hbReliabilityScore as follows:

ibiWeightFactor = 0.1; // This sets how quickly HR can change
ibiWeight = constrain(hbReliabilityScore * prevHbReliabilityScore * ibiWeightFactor, 0, 1);
ibiAvg = ibiAvg* (1 - ibiWeight ) + interBeatInterval * (ibiWeight );
HR = 1 / ibiAvg;

It could also be useful to calculate hrReliabilityScore = hrReliabilityScore * (1 - ibiWeight ) + hbReliabilityScore * (ibiWeight );
and include this with addPacket(beatTime, EmotiBitPacket::TypeTag::HEART_RATE, ..., (int8_t) (hrReliabilityScore * 100)) ; to codify the HR reliability as poor when the hbReliabilityScore is persistently bad and likely affecting HR estimation.

Super Tight

Summary

  • As the signal gets weaker from over-tightening, noise can infiltrate and get detected as short heartbeats

Data

image

image

image

image

Super Loose No Motion

Summary

  • Taking a deep breath commonly causes a dicrotic notch to get detected as a heartbeat and double the HR

Data

image

image

@produceconsumerobot produceconsumerobot self-requested a review March 30, 2023 13:12
Copy link
Collaborator

@produceconsumerobot produceconsumerobot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review 02

@nitin710
Copy link
Collaborator Author

nitin710 commented Mar 30, 2023

Steps to merge PR

Moved to future PR

  • Improve HR algorithm by adding dynamic bounds on beat detection as suggested in this comment.

@nitin710 nitin710 merged commit 8b681f5 into master Mar 30, 2023
@nitin710 nitin710 deleted the feat-100HzPPG branch October 16, 2025 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants