ECG Live Monitoring/Visualization + Data conversion on Matlab - matlab

I am working on ECG project for biometric identification: I use Arduino + Olimex ECG/EKG Shield and transmit the data to MatLab.
I am facing two issues:
Monitoring on real time: The X-Axis scale changes automatically and will compress the ECG after long time. I want to visualize a specific view so QRS complexes will be clearly visual. Any generic code?
Getting real voltage values: Analog.read() on Arduino converts voltages from 0-5 to 0..1023 and then we send them via serial communication. On Matlab, ECG does not have specific min and max to get the real values of the voltages to extract the characteristics. Any ideas about how that could be done.
Thank you so much in advance.
Waiting for your replies.
Kind regards,
Badreddine

edit disclaimer, I wrote this code from memory, so it may be buggy, apologies in advance
1.Lets say your data is stored in some array data_array and every time we get a sample we do something like this
data_array = [data_array, sample];
Now let's say in our viewer we only want to view 20 samples. we can do something like this
num_visible_samples = 20; %define this somewhere outside your data aquisition loop
%gets new data somehow
data_array = [data_array, new_data_point];
%plots all data
plot(data_array);
length_data_array = length(data_array);
if (length_data_array >= num_visible_samples)
%this makes the plot window only show the current data point and the
%previous few data points, the Yaxis will automatically be scaled
xlim([length_data_array -num_visible_samples, length_data_array ]);
end
another thing you could do (which will be faster) is to only plot the number of samples you desire
num_visible_samples = 20; %define this somewhere outside your data aquisition loop
%gets new data somehow
data_array = [data_array, new_data_point];
%plots all data
plot(data_array);
length_data_array = length(data_array);
if (length_data_array >= num_visible_samples)
%plots only the specific number of data points
plot(data_array(end-num_visible_samples,end));
%im not sure if you need to specify the axis, but i think you do
xlim([length_data_array -num_visible_samples, length_data_array ]);
else
plot(data_array);
end
2.Converting the values back and forth is pretty simple. This is jsut some psuedocode
maxV = 5.0;
minV = 0.0;
num_of_quantized_values = 1024;
%this says how much the lsb of the adc is, basically we just divide our
%input range (0-5 volts) by our output range (0-1023) => 4.88mV
adc_resolution = (maxV - minV) / num_of_quantized_values;
to convert the digital value back to an analog voltage we multiply our resolution by the value returned
val_in_volts = adc_digital_reading * adc_resolution;
so for example, say the adc read 256
256 * 4.88mV = 1.25
edit2
Since atmega chips usually have 10bit ADCs you get the data adc data in two bytes. The lower 8 and then the upper 2. The fix isn't too bad, say we have two variables
lower_byte is uint8
upper_byte is uint8
so I would do it this way (there are various methods depending on implementation)
adc_digital_reading = bitor(bitshift(uint16(upper_byte ),8), uint16(lower_byte));
%mirrors this c code
%adc_u16 = (upper_byte << 8) | lower_byte;
The important part is converting the uint8 types to uint16, or it wont work properly
to be really safe you could do
adc_digital_reading = bitand(bitor(bitshift(uint16(upper_byte ),8), uint16(lower_byte)),1023);
%adc_u16 = ((upper_byte << 8) | lower_byte) & 0x03FF;

Related

Problem understanding Loss function behavior using Flux.jl. in Julia

So. First of all, I am new to Neural Network (NN).
As part of my PhD, I am trying to solve some problem through NN.
For this, I have created a program that creates some data set made of
a collection of input vectors (each with 63 elements) and its corresponding
output vectors (each with 6 elements).
So, my program looks like this:
Nₜᵣ = 25; # number of inputs in the data set
xtrain, ytrain = dataset_generator(Nₜᵣ); # generates In/Out vectors: xtrain/ytrain
datatrain = zip(xtrain,ytrain); # ensamble my data
Now, both xtrain and ytrain are of type Array{Array{Float64,1},1}, meaning that
if (say)Nₜᵣ = 2, they look like:
julia> xtrain #same for ytrain
2-element Array{Array{Float64,1},1}:
[1.0, -0.062, -0.015, -1.0, 0.076, 0.19, -0.74, 0.057, 0.275, ....]
[0.39, -1.0, 0.12, -0.048, 0.476, 0.05, -0.086, 0.85, 0.292, ....]
The first 3 elements of each vector is normalized to unity (represents x,y,z coordinates), and the following 60 numbers are also normalized to unity and corresponds to some measurable attributes.
The program continues like:
layer1 = Dense(length(xtrain[1]),46,tanh); # setting 6 layers
layer2 = Dense(46,36,tanh) ;
layer3 = Dense(36,26,tanh) ;
layer4 = Dense(26,16,tanh) ;
layer5 = Dense(16,6,tanh) ;
layer6 = Dense(6,length(ytrain[1])) ;
m = Chain(layer1,layer2,layer3,layer4,layer5,layer6); # composing the layers
squaredCost(ym,y) = (1/2)*norm(y - ym).^2;
loss(x,y) = squaredCost(m(x),y); # define loss function
ps = Flux.params(m); # initializing mod.param.
opt = ADAM(0.01, (0.9, 0.8)); #
and finally:
trainmode!(m,true)
itermax = 700; # set max number of iterations
losses = [];
for iter in 1:itermax
Flux.train!(loss,ps,datatrain,opt);
push!(losses, sum(loss.(xtrain,ytrain)));
end
It runs perfectly, however, it comes to my attention that as I train my model with an increasing data set(Nₜᵣ = 10,15,25, etc...), the loss function seams to increase. See the image below:
Where: y1: Nₜᵣ=10, y2: Nₜᵣ=15, y3: Nₜᵣ=25.
So, my main question:
Why is this happening?. I can not see an explanation for this behavior. Is this somehow expected?
Remarks: Note that
All elements from the training data set (input and output) are normalized to [-1,1].
I have not tryed changing the activ. functions
I have not tryed changing the optimization method
Considerations: I need a training data set of near 10000 input vectors, and so I am expecting an even worse scenario...
Some personal thoughts:
Am I arranging my training dataset correctly?. Say, If every single data vector is made of 63 numbers, is it correctly to group them in an array? and then pile them into an ´´´Array{Array{Float64,1},1}´´´?. I have no experience using NN and flux. How can I made a data set of 10000 I/O vectors differently? Can this be the issue?. (I am very inclined to this)
Can this behavior be related to the chosen act. functions? (I am not inclined to this)
Can this behavior be related to the opt. algorithm? (I am not inclined to this)
Am I training my model wrong?. Is the iteration loop really iterations or are they epochs. I am struggling to put(differentiate) this concept of "epochs" and "iterations" into practice.
loss(x,y) = squaredCost(m(x),y); # define loss function
Your losses aren't normalized, so adding more data can only increase this cost function. However, the cost per data doesn't seem to be increasing. To get rid of this effect, you might want to use a normalized cost function by doing something like using the mean squared cost.

Synchronous Data Transfer between Arduino and Matlab Serially

I am working on a project where i read real time current signal on an Arduino using a Split Core CT. I am able to read exact AC Current and replicate it in Arduino Serial Plotter using the code below.
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
}
But I have to do further calculations on it like FFT and THD, so I am sending that data to MATLAB over serial communication. Below is my Matlab Script which reads 1000 data samples, stores it in an array and performs calculations and finally plots it.
clc; close all;
if ~isempty(instrfind)
fclose(instrfind);
delete(instrfind);
end
s1=serial('COM5','Baudrate',115200);
fopen(s1);
Fs=1000;
LoS = 100;
T = 1/Fs;
t = (0:LoS-1)*T;
sig = zeros(1,LoS-1);
str='';
sen=0;
for j = 1:LoS
str=fscanf(s1);
sen=str2double(str);
sig(j)=sen;
end
subplot(2,1,1);
axis([0 LoS -4 4]);
plot(t,sig);
xlabel('Counts');
ylabel('Magnitude');
title('Signal');
Y=fft(sig);
P2 = abs(Y/LoS);
P1 = P2(1:LoS/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(LoS/2))/LoS;
subplot(2,1,2);
axis([0 100 0 10]);
plot(f,P1);
title('FFT(Signal)');
xlabel('f (Hz)');
ylabel('|Power(f)|');
fclose(s1);
delete(s1);
clear s1;
The issue is the frequency of actual signal is 60Hz, but my code outputs a peak at 31Hz. I checked the same code on matlab simulated sinusoids, it gives exact results. But on real data its miscalculating. I implemented the same logic on LABView as well the result remains 31Hz. Can anyone pinpoint my mistake? I am really stuck at this.
Thanks for your time.
You should fix the Arduino sample rate to 1000 samples per second.
As Daniel commented, the Arduino is working as fast as possible, instead of sampling at 1KHz (instead of sampling 1000 samples per second).
You can fix your Arduino loop to sample at 1KHz by making sure each iteration takes 1000 micro-seconds.
The loop below reads the time at the beginning and at the end of each iteration.
At the end of each iteration, there is a delay that completes the duration to 1000us.
void loop() {
unsigned long start_time_us, delta_time_us;
//Returns the number of microseconds since the Arduino board began running the current program
//https://www.arduino.cc/reference/en/language/functions/time/micros/
start_time_us = micros();
Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
//Passed time in microseconds.
delta_time_us = micros() - start_time_us;
if (delta_time_us < 1000)
{
//Delay the remaining time - complete to 1000us (keep sample rate at 1000 samples per second).
//https://www.arduino.cc/reference/en/language/functions/time/delaymicroseconds/
delayMicroseconds((unsigned long)1000 - delta_time_us);
}
}
Please note: I could not verify the solution because I don't have an Arduino board (or simulator).
Remark: sending samples as text strings is inefficient, and may saturate the serial port.
I suggest you do the following:
Send binary data (using Serial.write instead of Serial.println).
Instead of converting the sample to double before sending it from Arduino, send the sample in short format (two bytes): Send the value (short)(analogRead(A5) - analogRead(A0)).
In the MATLAB side, read 1000 binary samples: sig = fread(s1, 1000, 'int16');
Perform the conversion to double and scale samples in MATLAB: sig = double(sig) * 0.009765625.
Arduino code:
//Serial.println( (double)(analogRead(A5) - analogRead(A0))*0.009765625 );
short analog_read = (short)(analogRead(A5) - analogRead(A0));
Serial.write((uint8_t*)&analog_read, 2); //Send two bytes in int16 binary format
MATLAB code:
% for j = 1:LoS
% str=fscanf(s1);
% sen=str2double(str);
% sig(j)=sen;
% end
sig = fread(s1, 1000, 'int16'); % Read 1000 analog samples (each sample is int16).
sig = double(sig) * 0.009765625; % Convert 1000 samples to physical values in double format
The above code is more efficient for both Arduino and MATLAB.
Remark: Keep in mind that I didn't test the code - it's just for demonstrating the concept.

Reading Data from Serial Port and Plotting in Real Time

I am looking to take in data from a temperature sensor on an Arduino Uno board and have matlab store the data while also plotting the data on a graph in real time to monitor sensor changes. This will eventually be implemented with a different sensor and this more of a proof of concept. I have two issues currently troubling me:
1) I need to be gathering the data points 1000x a second (which my current code cannot do)
2) After several seconds of monitoring the data a great deal of noise enters the system
Here is my matlab code
SerialPort = 'com6';
s = serial(SerialPort, 'BaudRate', 250000, 'DataBits', 8);
fopen(s);
voltage = 0;
t = 0;
y = 1;
voltage = fscanf(s);
VoltageValue(y,1)=str2double(voltage);
h = animatedline(t,VoltageValue(y,1));
xlim([0 1000]);
ylim([100 200]);
tic
while t <= 1000
voltage = fscanf(s);
VoltageValue(y,1)=str2double(voltage);
addpoints(h, t, VoltageValue(y,1));
t= t+1;
y= y+1;
drawnow
end
toc
fclose(s);
delete(s);
clear s;
Here is my arduino code
int tmppin = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(250000);
pinMode(tmppin, INPUT);
}
void loop() {
// put your main code here, to run repeatedly:
int tempreading = analogRead(tmppin);
Serial.println(tempreading);
}
From what I have tried, the serial communication at 38400bd is enough to transmit an int at 1kHz. You can use 115200bds; your speed of 500000bds is not supported, even if it usually works.
The ADC will have no problem with 1000Hz. The Arduino command analogRead works at 10kHz; and if you would access register of atmel MCU directly, you could speed up analogRead to 100kHz.
Your code is slowed down with drawnow. You dont need to refresh at 1kHz; if you refresh at 10Hz, it will be real time for your eye.
In arduino sketch you should put a delay(1) (1ms), so you know the serial buffer wont get full.
also , to speedup the transfer speed, sending the data as binary will help a lot the Serial.println(tempreading); you used sends data as string. meaning it will send 6 characters each time. (5 characters + the CR). sending as binary will need only 2 bytes.
; a 3X speed increment already.
To send data as binary in arduino , use
serial.write(data_to_send>>8); //send most significant byte
serial.write( data_to_send && f); //send least significant byte

How to process multiple mics input Audio stream in matlab in real time

I need to get 3 input audio stream to matlab simultaneously using 3 USB mics and find out the highest amplitude in real time.
this is the mics initialization
mic1= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (10- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic2= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (9- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
mic3= dsp.AudioRecorder('DeviceName','ÇáãíßÑæÝæä (8- USB PnP Sound Device)', 'SampleRate', 48000, 'NumChannels', 1);
frame1=step(mic1);
frame2=step(mic2);
frame3=step(mic3);
what are the next steps?
By examining your code, you have three audio signals with one channel each. If I understand what you want correctly, you want to find the highest sound made by any one signal overall over time. However, if I understand you correctly, you can't do this in real time as step for the AudioWriter can only capture one frame at a time. Because you're specifically trying to capture from all three audio devices, and the only way for you to capture audio is with step, you'll have to serially call step for each signal that you have.
Therefore, you'll need to capture all three audio signals separately, and then do your analysis. You'll also want to clip the sound after a certain point, so perhaps something like 5 seconds. Therefore, you'd do something like:
time_end = 5;
%// Capture audio signal 1
tic;
frame1 = [];
while toc < time_end
audio_in = step(mic1);
frame1 = [frame1; audio_in(:)];
end
%// Capture audio signal 2
tic;
frame2 = [];
while toc < time_end
audio_in = step(mic2);
frame2 = [frame2; audio_in(:)];
end
%// Capture audio signal 3
tic;
frame3 = [];
while toc < time_end
audio_in = step(mic3);
frame3 = [frame3; audio_in(:)];
end
After this point, because the sounds will probably all be uneven length, you'll want to zero pad all of them so they all match the same length. After this, it's a matter of first finding the maximum amplitude for each sample for all three signals, and then finding the maximum out of all of this.
I'm not quite sure how the signals are shaped... if they are row or column vectors, so let's just make sure they're all column vectors. Then, use max and operate along the columns and find the maximum for each point in time, then find the maximum out of all of these.
Therefore:
%// Find lengths for all three signals
l1 = numel(frame1);
l2 = numel(frame2);
l3 = numel(frame3);
max_length = max([l1, l2, l3]);
%// Zero pad signals to make same length
frame1_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame2_pad = zeros(max_length,1);
frame1_pad(1:l1) = frame1;
frame2_pad(1:l2) = frame2;
frame3_pad(1:l3) = frame3;
%// Find maximum among each sample
max_signal = max([frame1_pad, frame2_pad, frame3_pad], [], 1);
%// Find the maximum amplitude overall and location
[max_amplitude, loc] = max(max_signal);
max_amplitude will contain the highest point overall at a particular time point for each of the three signals, and loc will tell you the location in the array of where it was found. If you want to find the actual time it occured, simply take loc and multiply by your sampling time (1/48000). Bear in mind that loc will be 1-indexed instead of 0-indexed, and so you need to subtract by 1 before multiplying by the sampling rate.
Therefore:
time_it_happened = (loc-1)*(1/48000);
time_it_happened will contain that time which the highest amplitude happened.
Good luck!

Looping over matrix elements more efficiently in Matlab

I am writing some matlab code and have written an algorithm that works but I don't think its particularly efficient. Since I am trying to improve my programming skills I would like to know if there is a more efficient way of doing this.
I have a (reasonably large ~ E07) matrix of values which are unordered, but fall within the range [-100, 100]. I want to create a second matrix based on the first, by using the following rules:
If the value of the point is > 70, then the value of the point should be set to 70.
If the value of the point is < -70, then the value of the point should be set to -70.
All other values should be rounded to the nearest multiple of 5.
Here is what I am currently doing:
data = 100*(-1+2*rand(1,10000000)); % create random dataset for stackoverflow
new_data = zeros(1,length(data));
for i = 1:length(data)
if (data(i) > 70)
new_data(i) = 70;
elseif (data(i) < -70)
new_data(i) = -70;
else
new_data(i) = round(data(i)/5.0)*5.0;
end
end
Is there a more efficient method? I think there should be a way to do this using logical indexes but those are a new discovery for me...
You do not need a loop at all:
data = 100*(-1+2*rand(1,10000000)); % create random dataset for stackoverflow
new_data = zeros(1,length(data)); % note that this memory allocation is not necessary at this point
new_data = round(data/5.0)*5.0;
new_data(data>70) = 70;
new_data(data<-70) = -70;
Even easier is to use max and min. Do it in one simple line.
new_data = round(5*max(-70,min(70,data)))/5;
The two answers by H.Muster and woodchips are of course the way to do it, but there still are small improvements to be found. If you are after performance you might want to exploit specifics of your problem. For example, your output data is integers -100 <= x <= 100. This obviously qualifies for 8-bit signed integer data type. This code (note explicit cast to int8 from arbitrary double precision data)
% your double precision input data
data = 100*(-1+2*rand(1,10000000));
% cast to int8 - matlab does usual round here
data = int8(data);
new_data = 5*(max(-70,min(70,data))/5);
is the fastest for two reasons:
1 data element takes 1 byte, not 8. Memory bandwidth is a limiting factor here, so you get a lot of improvement
round is no longer necessary
Here are some timings from the codes of H.Muster, woodchips, and my small modification:
H.Muster Elapsed time is 0.235885 seconds.
woodchips Elapsed time is 0.167659 seconds.
my code Elapsed time is 0.023061 seconds.
The difference is quite striking. Although MATLAB uses doubles everywhere, you should try to use integer data types when possible..
Edit This works because of how matlab implements integer arithmetic. Differently than in C, a cast of double to int implies a round operation:
a = 0.1;
int8(a)
ans =
0
a = 0.9;
int8(a)
ans =
1