Measuring Low Frequencies of Input Signals

I am working on a project, specifically a tuned mass damper, where I am trying to change some physical parameters based on the input frequency. The physical setup that I have is only suited for sinusoidal inputs from zero to about 2 Hz. I have looked at a number of the existing libraries, many of which only work at higher frequencies, and some of which rely on using specific Arduino pins. The pins I can use are pre-determined because I am trying to get the frequency from an accelerometer (the MPU 6050). Does anybody know of a way, via programming or libraries, to determine the frequency of a signal based solely on data that is being read?

Run your sinusoidal input into a comparator - like the autovoltage switcher/LM358 on the Uno - so you have a nice squarewave output to work with. Then use pulseIn() to measure its width, high & low, and use time period to do whatever. 1/period = frequency.

The pins I can use are pre-determined because I am trying to get the frequency from an accelerometer (the MPU 6050).

Do you already have a functioning program that reads data from the accelerometer?

If so, then you can sample the accelerometer data for a period of time and use the Fourier transform to determine the frequencies.

For 0-2 Hz, you need to sample the acceleration data faster than 4 times per second.

Discrete Fourier Transform? Actually a good idea... even just learned about it in class. OK, I will try that first. Thanks!

The OpenMusicLabs FFT routines work well, but the examples provided aren’t very helpful.

Here is a short program that generates a known sine wave signal, then transforms it. That way you know what to expect, and that everything is working. The output amplitudes are off by a factor of 2 because the “negative frequency” contributions are ignored.

/*
 fft_test_sine
 example sketch for testing the fft library.
 This generates a simple sine wave data set consisting
 of two frequences 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 32 // set to 32 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 32 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_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(fft_lin_out[i]);
  }
  Serial.println("Done");
  while(1); //wait here
}

Ok, so I worked through the code you provided. I have also gone through the FFT examples, but have a hard time understanding them. What I have tried to do is (in reference to your code), simply replace "k" as the input signal with the output from one of my digital accelerometers. I run the code, and I get a magnitude that seems plausible. But how can I use this code to find the frequency of the signal?

Yes, put the accelerometer data into the array elements "fft_input[2*i]".

The actual frequency associated with a particular output bin depends on the sample rate (samples/second). See this discussion, among others.

What sample rate are you using?

I was reminded in another forum that use of a windowing function can help to prevent artifacts and clean up the spectrum. I left it out from my example because it was not needed. However if the frequencies are not centered in the frequency bin (as in my example), the spectrum will be messy.

Change the example to add fft_window(); as follows and see how it affects the results, particularly if you choose a frequency like 5.5 in the example:

  fft_window(); //apply Hann window
  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

Finally, use a larger array for more accurate results. The OpenMusicLabs library supports FFT_N = 256.

Ok, so I wonder if I am misunderstanding something about determining the frequency resolution, or if I am running into limitations of the FFT? Anyway, what I want is a frequency resolution of about 0.1 ~ 0.2 Hz if possible. So I set the number of bins (FFT_N/2) to 128, the max. It looks like the frequency resolution is equal to the Sampling Rate divided by the number of bins. So with FR = 0.1, I get a sampling rate of 12.8 Hz.

The problem is that I cannot set the sample rate from the accelerometer by writing to a register, so I try and monitor the time that passes in microseconds, and only read a value if more than 78125 microseconds have passed (1/12.8seconds).

When I do this, and run the FFT, the highest amplitudes are always in bins 0 and 1, no matter the frequency. From my limited knowledge of how Fourier Transforms work, shouldn't the Fundamental frequency be represented at different bins as the frequency changes? Representative of the dominant frequency?

Maybe understanding my experiment a little better will help too.... I have a tower (about 30" tall) to which I have affixed an accelerometer to the top and the base. I am inducing oscillations by a motor/linkage connection to the base, and the input frequency is usually between .3 and 1.3 Hz. I need to find the frequency (or at least the change in frequency) so that I can use a stepper motor to change the length of a pendulum that is supposed to act as a mass damper.

I doubt you are using the accelerometer correctly.

Use of the FFT requires that the time spacing of the data points is constant and accurately known.

I don’t know what happens when you read the accelerometer only occasionally – who knows when the presented value was actually measured?

You should write the program so that the accelerometer informs you (by interrupt, for example) when a data point is ready, then read it in immediately. That way the time spacing should be fairly uniform.

What update rate, sensitivity scale and low pass filter setting are you using?

I have the scale set at +-2g and the DLPF set to 5 Hz. As far as I can tell, the output rate of the accelerometer is fixed to 1 kHz.

  1. The MPU6050 has a programmable sample rate, from 8000 per second to 3.9 per second.

  2. For such low sample rates, it's simpler to just use millis() on the Arduino to decide when to request a sample from the accelerometer.

  3. The MPU6050 internal clock is only accurate to 1%, so you can do a lot of messing around on the Arduino side and still get 1% accuracy.

  4. 2G range? That's awfully low. From the amplitude and frequency of your system, work out what the accelerations are. I would be surprised to hear it stays under 2G.

The accelerations are actually pretty low for the most part. I keep looking at the data sheet, and I know that the sample rate for the gyroscope is adjustable, but all I see for the accelerometer is that it is set at 1 kHz. Can you tell me where you found that it is adjustable? Or just the register so that I can look it up?

My recommendation is option #2 above. Sorry that wasn't clear the first time.

The sample rate and output data rate of the accelerometer in the MPU6050 depend on the clock, which can be external crystal or internal, accurate or not. What breakout board do you have?

You are correct that the sample rate of the MPU6050 is fixed, at approximately 1 kHz (depending on the clock selection), but the output data rate is adjustable over a wide range. My approach, if I had to use the MPU6050, would be to set the output data rate to a useful value, and read the accelerometer with an interrupt. That way the measurement timing should be roughly constant, as required.

On the other hand, I wouldn't use that accelerometer, because it has the extra complications of the internal processor (DMP), a FIFO buffer, etc. A very simple ADXL, even with analog output, would be easier to use.

Are you sure that the vibrations that you are picking up with the accelerometer are not faster than your sample rate. If they are you will be getting aliasing and yes everything will end up in the first two bins.