Pulse meter

Autor: Radu Caragea 335CA

Introducere

  • Proiectul se vrea a fi un masurator nonintruziv al ritmului cardiac. Ulterior se extrage si valoarea efectiva a tensiunii.
  • Ideea pe scurt este aceea ca la ciclurile sistolice si diastolice se schimba transmisivitatea luminii prin piele.

Descriere generala

  • Se foloseste un LED rosu pe o portiune a corpului suficient de translucida care sa fie si suficient de vascularizata de exemplu un deget sau un lob al urechii. In partea opusa se pune un fotodetector care recepteaza mai multa sau mai putina lumina.
  • Astfel, daca privim semnalele luminoase venite de la sursa vom observa varfuri corespunzatoare ritmului cardiac. Se va observa un varf principal alaturi de un varf secundar (sistola si diastola).
  • Datorita zgomotului cauzat de lumina ambianta si respectiv mobilitatea partii expuse la lumina, nu se poate obtine pulsul cu o acuratete prea mare fara o analiza statistica si o calibrare a senzorului plus algoritmi specifici de procesarea semnalelor digitale.

Descriere senzor

  • Senzorul folosit este TSL230R. [2] El transforma iradianta ( energia luminoasa primita pe suprafata sa) in frecventa. Avantajul major este ca nu trebuie complementat de circuite analogice de amplificare. Toata informatia este la dispozitia noastra direct de la iesirea circuitului. Acesta se mai foloseste pentru a masura expunerea la camere, la control adaptiv al brightnessului, etc
  • TSL230 are doua tipuri de iesiri: tren de impulsuri sau square wave. Datorita duratelor foarte scurte (de ordinul nanosecundelor) primul tip este mai putin fezabil. Astfel, se foloseste iesirea de tip square wave. Aceasta se cupleaza la Counterul 1 al microprocesorului astfel incat sa putem citi la un interval scurt de timp iesirea senzorului. Folosim counterul 1 deoarece este de 16 biti iar valorile citite sunt in intervalul 0-10000.
  • Senzorul are mai multe optiuni de functionare prin setarea sensibilitatii si impartirii outputului:
S1 S0 Sensibilitate
Low Low Off
Low High x1
High Low x10
High High x100
S3 S2 Impartire output la
Low Low 1
Low High 2
High Low 10
High High 100
  • Datorita diferentelor foarte mici de luminozitate masurate in cadrul acestui proiect se seteaza sensibilitatea la 100x si nu se imparte outputul la vreun factor. Deci S0=High, S1=High, S2=Low, S3=Low.
  • Se citeste apoi registrul TCNT1 la interval fix de 20 de ms.

  • Se observa un nivel al zgomotului foarte mare. De aceea se foloseste un filtru digital software de tip “Moving average filter” [1] cu fereastra reglabila. Rezultatele cele mai bune le-am obtinut pentru dimensiunea 12 a ferestrei.

Hardware Design

  • Pentru schema am folosit Eagle Cad.
  • Pentru a vizualiza in timp real am folosit o adaptare a unui script FeedGnuPlot [3].
  • Comunicatia se face prin USART, cu valorile standard: 9600 bps, 8 biti de date, etc

Software Design

pulse.c
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#define F_CPU 16000000
#include <util/delay.h>
#include <util/atomic.h>
 
 
int uart_putchar(char c, FILE *unused);
int uart_getchar(FILE* f);
FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
FILE mystdin =  FDEV_SETUP_STREAM(NULL, uart_getchar, _FDEV_SETUP_READ);
 
 
 
#define WINDOWSIZE 12 
#define MEDINTERVAL 20 //20 ms
unsigned int vwindow[WINDOWSIZE];
 
 
void USART_init(void)
{
	UBRRH=0;
	UBRRL=103; //comunicare pe 9600 bps
 
	UCSRB=(0<<UCSZ2)|(1<<RXEN)|(1<<TXEN);
	UCSRC=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); //8 biti de date
}
 
void COUNTER_init(void)
{
	TCCR1A=0;
	TCCR1B=0;
	TCCR1B=(1<<CS12)|(1<<CS11)|(1<<CS10);
	TCNT1=0;
}
int main(void)
{
	unsigned long currentpulses;
	unsigned int i=0;
	unsigned int counter=0;
	unsigned int j,lastpeak=0;
	unsigned long signalout=0,signalsum=0,signalmean=0,prevsignal=0;
	unsigned long bpm1=0,bpm2=0,bpm3=0,bpm=0;
 
	USART_init();
	stdin = &mystdin;
	stdout = &mystdout;
 
 
	DDRB=0xFF; //toate iesiri
	DDRA=0xFF; //toate iesiri
	DDRC=0xFF; //toate iesiri
 
	DDRB&=~(1<<1); //PB1 intrare -> T1 -> external counter
 
	PORTB=0;
	PORTA=0;
 
	PORTA &= ~(1<<0); //setam bitul 0 LOW;
	PORTB |= (1<<0); //setam bitul 0 HIGH;
 
	// S0 = H S1 = H
	PORTA |= (1<<3); // PA3 = S_0
	PORTA |= (1<<2); // PA2 = S_1 
 
	// S2 = L S3 = L
 
	//PORTB |= (0<<2); // PB2 = S2 = L;
	//PORTB |= (0<<3); // PB3 = S3 = L;
 
 
	COUNTER_init();
 
 
	PORTC=(1<<1); //Activare RED LED
 
	currentpulses = 0;
	while (1) {
		_delay_ms(MEDINTERVAL);
		currentpulses=TCNT1;
		TCNT1=0; //resetare si numarare din nou
 
		if(i < WINDOWSIZE) //umplere initiala
			vwindow[i]=currentpulses;
		else{
			signalout=0;
 
			//filtru Moving average
			for(j=0;j<WINDOWSIZE-1;j++){
				signalout += vwindow[j];
				vwindow[j]=vwindow[j+1];
			}
			signalout += vwindow[WINDOWSIZE-1];
			vwindow[WINDOWSIZE-1]=currentpulses;
 
			signalout=signalout/WINDOWSIZE;
 
			//se face media si pe semnalul filtrat pentru a avea o referinta la varfuri
			if(counter==MEDINTERVAL){
				signalmean=signalsum/MEDINTERVAL;
				signalsum=0;
				counter=0;
			}
 
			//se iau in considerare trecerile in jos de deasupra mediei semnalului 
			//datorita zgomotului se introduce o limitare hardcodata la 20
			//si pentru a pastra o continuitate se face media ultimelor 3 valori, atenuand fluctuatiile
			if(prevsignal>signalout && signalmean>prevsignal&&i-lastpeak>20){
				bpm3=bpm2; 
				bpm2=bpm1;
				//noua valoare:
				//60000 ms intr-un minut
				//se extrapoleaza distanta intre varfuri pentru 1 minut
				//rezulta BPM
				bpm1=(60000/MEDINTERVAL)/(i-lastpeak);
				bpm=(bpm1+bpm2+bpm3)/3;
				#ifdef BPM_OUT
					printf("Peak at %u. BPM is %lu\n",i,bpm);
				#endif
				lastpeak=i;
			}
			#ifndef BPM_OUT
				printf("%u %lu \n",i,signalout);
			#endif
			prevsignal=signalout;
		}
		i++;
		counter++;
		signalsum += signalout;
	}
	return 0;
}
 
 
// Functie ce trateaza trimiterea unui caracter pe seriala
int uart_putchar(char c, FILE *unused)      
{
	if (c == '\n')
		uart_putchar('\r', 0);
	loop_until_bit_is_set(UCSRA, UDRE);
	UDR = c;
	return 0;
}
 
// Functie ce trateaza primirea unui caracter pe seriala
int uart_getchar(FILE* f)
{
	char c;
 
    loop_until_bit_is_set(UCSRA, RXC);
    c = UDR;
    if(c=='\r')
		c='\n';
    uart_putchar(c,NULL); 
    return c;
}

Concluzii

  • Device-ul functioneaza destul de bine. Chiar ar putea fi folosit pentru investigarea in timp real a ritmului cardiac
  • Ar fi fost necesara o carcasa pentru o izolare a zgomotului luminos ambiant
  • Este mult mai greu decat pare sa faci un peak detector. In final am folosit o adaptare a unui algoritm gasit intr-un jurnal de bioinginerie. [4]
  • Pe viitor, vreau sa extind proiectul pentru a masura si nivelul de oxigen in sange. Pentru aceasta se foloseste si ledul UV care da un baseline, din care se scad fluctuatiile de pe ledul Rosu. Apoi se fac cateva calcule putin mai complexe si in final se face un procentaj al variatiei luminii rosii si al variatiei luminii uv care da exact nivelul de SpO2.[5]
  • Sursa si scripturile folosite se gasesc aici: pulse_meter.zip

Bibliografie

pm/prj2011/rtataroiu/pulse_meter.txt · Last modified: 2021/04/14 17:07 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0