I'm pretty new to Arduino and am looking for some help for a solution to analyze low frequency (0 - 50Hz) vibration with a 1Hz resolution, i.e. each bin represents a single Hz frequency. I have been reading about several entries in this forum on the subject of FFT and I tried several libraries / codes myself. None of them do give me the desired result. I am using the Arduino Uno and an ADXL-335 analogue accelerometer.
I understand from the post by PS991 in FFT Library - Audio - Arduino Forum that the OpenMusicLabs library resolution is a function of sampling frequency (Fs) and number of bins and this solution is presumably driven by the limited Arduino Uno RAM.
Now my question is does anyone know of a Frequency Analysis library (like FFT) to give me desired 0 - 50Hz spectrum at the desired resolution?
I think the limitation of the OpenMusicLabs is a hardware limitation driven solution, not a math's one. So theoretically you could adapt the existing OpenMusicLabs code such that you can define a bandwidth of interest and only return the amplitudes for that bandwidth. With a user defined bandwith, instead of a sampling frequency driven bandwidth, and the same number of bins you should now have a higher resolution. Has anyone tried this?
Huh? This sounds like a piece of cake job for any FFT. 50 bins is not many. What is the problem with the result exactly? Please explain what you mean, "high resolution".
the OpenMusicLabs library resolution is a function of sampling frequency (Fs) and number of bins
Correct
and this solution is presumably driven by the limited Arduino Uno RAM
No. It's an unavoidable mathematical/physical limitation. The sampling frequency (Fs) has to be at least twice the highest frequency component in the signal (whether you are interested in that specific frequency or not) otherwise you get aliasing (look it up). The number of bins then determines the resolution of each bin (or vice versa - the required resolution determines the minimum number of bins). For a number of bins N, the resolution of each bin is Fs/N.
If you don't low pass filter the incoming signal to, say, below 100Hz, the number of bins required will be very high. E.g. A sampling rate of 8000Hz will require a minimum of 8000 bins to get 1Hz resolution
So theoretically you could adapt the existing OpenMusicLabs code such that you can define a bandwidth of interest and only return the amplitudes for that bandwidth.
Thank you for your quick reply el_supremo! So if I understand correctly you say it is possible to get the high resolution by applying a low pass filter at 100 Hz before feeding the signal into the Arduino. This way I should get the 0 - 50 Hz bandwidth analyzed at 1Hz resolution. Interesting - I clearly need to read-up on low pass filters and how they work.
aarg - the problem I had is that due to the high sampling frequency of the Arduino (default settings ~10 kHz) the bins cover a bandwidth of ~20 Hz. So almost the entire frequency range that I am interested sits inside 1 bin. The signal I got has a high peak at the first few bins and close to nothing on the other bins. Each bin contains amplitudes for about a bandwith of 38Hz. I'm interested in the amplitude distribution of the first 50Hz, i.e. I have to split out the signal of the first 2 bins.
In the image attched you see the first 3 bins (each of ~20Hz each). The blue is the bin that includes the DC signal (0 Hz). You can see a big drop in amplitude going from the first to the second and third bin. I suspect to see a similar drop within the first bin when I split out the signal, and that is what I need.
The big drop you see in the signal towards the end is where I turned the accelerometer up-side-down, hence the DC signal changed.
I am a bit surprised though to see that shaking my hand does still lead to such a strong signal in the second bin (40 - 80 Hz).
OSEM:
Thank you for your quick reply el_supremo! So if I understand correctly you say it is possible to get the high resolution by applying a low pass filter at 100 Hz before feeding the signal into the Arduino. This way I should get the 0 - 50 Hz bandwidth analyzed at 1Hz resolution. Interesting - I clearly need to read-up on low pass filters and how they work.
You also need to down-sample or sample at a slower rate. The frequency resolution is the sample rate divided by the number of bins (limited to 256 on an Atmega328), thus the sampling rate has to be no greater than 256 samples per second as input to the FFT. The low pass filtering is required to attenuate any frequency content above 1/2 the sampling rate so that it does not show up aliased to a lower frequency in your spectral result.
As mentioned above, the file attachment in post #5 does not seem to match the text description in the post.
Basically I took the OpenMusicLabs library and modified it to print the signal for the first 3 bins:
/*
fft_adc_serial.pde
guest openmusiclabs.com 7.7.14
example sketch for testing the fft library.
it takes in data on ADC0 (Analog0) and processes them
with the fft. the data is sent out over the serial
port at 115.2kb.
*/
#define LIN_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#include <FFT.h> // include the library
void setup() {
Serial.begin(115200); // use the serial port
TIMSK0 = 0; // turn off timer0 for lower jitter
ADCSRA = 0xe5; // set the adc to free running mode
ADMUX = 0x40; // use adc0
DIDR0 = 0x01; // turn off the digital input for adc0
}
void loop() {
while(1) { // reduces jitter
cli(); // UDRE interrupt slows this way down on arduino1.0
for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
while(!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = 0xf5; // restart adc
byte m = ADCL; // fetch adc data
byte j = ADCH;
int k = (j << 8) | m; // form into an int
k -= 0x0200; // form into a signed int
k <<= 6; // form into a 16b signed int
fft_input[i] = k; // put real data into even bins
fft_input[i+1] = 0; // set odd bins to 0
}
fft_window(); // window the data for better frequency response
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data in the fft
fft_mag_lin(); // take the output of the fft
sei();
Serial.println("start");
// for (byte i = 2 ; i < 3 ; i++) {
Serial.print(fft_lin_out[0]); // send out the data
Serial.print(" ");
Serial.print(fft_lin_out[1]); // send out the data
Serial.print(" ");
Serial.print(fft_lin_out[2]); // send out the data
Serial.println(" ");
delay(10000);
// }
}
}
(I didn't include the libraries but can post them if necessary)
The 3 lines in the image I posted are the signal from these first 3 bins. I agree it looked like an ordinary signal to me first as well, but later I started interpreting it as the change in amplitude in each bin.
Because I am continuously sampling whilst shaking the accelerometer I assumed it was a change in signal, although I have to admit it takes me a lot of convincing as well. The only reason I could convince myself these are the FFT bin signals is due to the signal dying out after the 3rd bin (image attached is that of the 90th bin), i.e. you don't see a signal anymore and all that is left is some noise. I interpreted that as my shaking not containing a frequency component > 120 Hz.
MrMark - how do I down sample? I read about the scaling factor on the ADC to increase Fs above the default 10kHz, but not lower. You could possibly do it with a delay, but how reliable does that make the sampling frequency?
Using delay(40) between samples would be an easy way to try it out.
Another simple approach would be to sample at a high rate and simply throw away samples. This might result in less jitter. If the sampling rate is 10,000 samples/second and you want about 250 per second then retaining only every 40th sample would do it.
Better would be to sample on a periodic interrupt every 40 mS
el_supremo:
A DFT is limited by the same factors that affect FFT or FHT.
Mathematically yes, from the implementation there are significant differences. With a DFT, you don't need to store all the samples, which is great for memory constrained systems. Obviously a DFT is a lot slower to calculate than an FFT. If you have a steady state signal, you can split the DFT into segments, so you don't have to analyse the whole spectrum.
To get high resolution on low frequencies, you need to collect a lot of samples.
In fact, I have used DFT successfully to process low frequency signals for vibration analysis, on a very RAM limited processor (about 2kB).
OSEM:
Because I am continuously sampling whilst shaking the accelerometer I assumed it was a change in signal, although I have to admit it takes me a lot of convincing as well. The only reason I could convince myself these are the FFT bin signals is due to the signal dying out after the 3rd bin (image attached is that of the 90th bin), i.e. you don't see a signal anymore and all that is left is some noise. I interpreted that as my shaking not containing a frequency component > 120 Hz.
Rather than shaking the accelerometer, I would strongly suggest setting up a frequency generator to create some known frequencies. I used a DDS algorithm running on an Arduino to feed into an ADC, so I could set up some known frequencies with known amplitudes. I also introduced some random noise into the signal to give more realistic input.
Errors like aliasing, and the need to sample the data at a rate at least twice as high as the frequency of interest are associated with sampling itself, not the FFT, DFT or any data processing that comes afterward.
If you want to analyze 0-50 Hz data, you must sample at least once every 10 ms, and no frequencies higher than twice the sample frequency may be present in your data.
If you do sample at 100 Hz, then you need an extremely sharp "brick wall" 50 Hz low pass filter on the input. This is usually impractical, so you must sample at twice the frequency of anything that gets through the input filter, then throw away the higher frequency bins in the transform.
To understand how all the FFT works, rather than use an accelerometer, generate test data yourself and submit it for transformation. Here is a program I wrote some time ago to test the OpenMusicLabs FFT:
/*
fft_test_sine
example sketch for testing the fft library.
This generates a simple sine wave data set consisting
of two frequencies f1 and f2, transforms it, calculates
and prints the amplitude of the transform.
*/
// do #defines BEFORE #includes
#define LIN_OUT 1 // use the lin output function
#define FFT_N 64 // set to 64 point fft
#include <FFT.h> // include the library
void setup() {
Serial.begin(9600); // output on the serial port
}
void loop() {
int i,k;
float f1=2.0,f2=5.0; //the two input frequencies (bin values)
for (i = 0 ; i < FFT_N ; i++) { // create samples
// amplitudes are 1000 for f1 and 500 for f2
k=1000.*sin(2.*PI*f1*i/FFT_N)+500.*sin(2.*PI*f2*i/FFT_N);
fft_input[2*i] = k; // put real data into even bins
fft_input[2*i+1] = 0; // set odd bins to 0
}
fft_window(); //Try with and without windowing, it smears
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data using the fft
fft_mag_lin(); // calculate the magnitude of the output
// print the frequency index and amplitudes
Serial.println("bin amplitude");
for (i=0; i<FFT_N/2; i++) {
Serial.print(i);
Serial.print(" ");
Serial.println(2*fft_lin_out[i]); //*2 for "negative frequency" amplitude
}
Serial.println("Done");
while(1); //wait here
}