Sampling frequency

Hello,

I am an undergraduate student at a public university in Maryland. I am currently doing a project with an arduino and I came across a problem that I could not figure out after several days of researching. Regarding my project, I am trying to collect 6 data (acceleration and gyration in x,y,z) from an accelerometer/gyroscope sensor at a desired constant rate (trying to make it 1000 Hz, so about 1000 sets of the 6 data per second) into a SD card. However, I could not make the arduino do this; I am able to collect the data, but not at a constant sampling frequency of 1000 Hz. Could someone help me set the sampling frequency at 1000 Hz please. Here are some of the information of the sensors and arduino I am using:

  1. Arduino nano - https://www.arduino.cc/en/Main/ArduinoBoardNano
  2. MPU-6050 gy-521 accelerometer/gyroscope - http://playground.arduino.cc/Main/MPU-6050
  3. SD card reader - http://www.aliexpress.com/item/SD-Card-Reader-Module-for-Arduino-ARM-Read-and-Write-Free-shipping/526750081.html?spm=2114.12010108.1000023.4.JkncUu&spm=2114.12010108.1000023.4.JkncUu

Also, I have attached my code and some of the libraries I am using. Thank you in advance for the help.

project.ino (2.85 KB)

I2Cdev.h (11.9 KB)

MPU6050.h (43.3 KB)

All that printing to the SD card takes time. Shorten what you print and you may be able to measure faster.

Remove all the lcd. and Serial. stuff in the sampling loop. They are slowing you down a lot.

Pete

Writing to SD is slower than you might expect. The Arduino uses a very simple communication with the card, unlike a camera which can do many megabytes per second.

Reduce the number of chars you write. Reduce the number of times you open or close the file. If that still doesn’t work fast enough, look for an optimized SD library. There are some big speed ups possible, depending on your usage pattern.

Writing to SD is slower than you might expect.

Actually, writing to the SD card is a two step process. Most write()/print()/println() statements write to a buffer. That is fast. Only when the buffer is full does a write to the card happen, and that is slow.

So, you will never get a consistent write rate, unless you flush() the buffer to the card after each record is buffered.

The rest of the advice presented, so far, is good.

I appreciate all the help and advice you all have given me. So, how should I store data into the SD card? Ultimately, I am trying to get these data I read at 1000 Hz and then analyze the data after on matlab or other softwares. Would I not be able to reach 1000 Hz sampling frequency if I am going to write data into the SD card?

1000Hz should be easily do-able.

Get an SD shield or adaptor, write a quick test program and you should have your results by the end of the evening.

so how would I do that? I want to set it at whatever frequency I want so that the sensor/SD does what its supposed to do at a set rate. If I just control their frequencies by only using delay then wouldnt they collect data at an inconsistent rate?

ssiltare: so how would I do that? I want to set it at whatever frequency I want so that the sensor/SD does what its supposed to do at a set rate. If I just control their frequencies by only using delay then wouldnt they collect data at an inconsistent rate?

Sorry, it's hard to see what advice you are responding to here. You have it as a given, that data is sampled at precise intervals. Usually the recording doesn't have this limitation, as long as it doesn't fall behind in the long run. You can buffer the data and use a producer-consumer model (FIFO queue) to record it, if the recording rate is variable. But you can not make it work if the SD driver brings everything to a halt while it records.

Hello,

I have a problem collecting data from my accelerometer/gyroscope sensor with a constant sampling rate. Is there a way to program the arduino to collect data from the sensor and store these values in a SD card with a constant frequency of 1kHz without using delay? The sensor that I am using is a MPU6050 gy-521 sensor that uses I2C with arduino. I will be reading 6 different datas from my sensor: acceleration in x,y,z directions and gyration in x,y,z directions. Thank you all in advance for the advice/help.

There is certainly no need to use delay, see the "blink without delay" example that comes with the Arduino.

Whether the sensor can deliver 6 readings, and these can be stored on the SD card at 1000 Hz is a different question.

http://forum.arduino.cc/index.php?topic=394659.0 Duplicate posts waste peoples time. Read the forum rules.

How long are you planning on sampling the data for?

You can make a string of data about 512 bytes long. That is the size of buffer the SD library uses to print to the SD. If you are using datafile.print() or datafile.flush() often with a length of data less than 512, you are wasting time.

Fill a string with your data then print that every second.

so I should be able to print the 6 data from the accelerometer/gyroscope sensor at a frequency rate of 1000 Hz if I am not going to save it to the SD card? Even if I am able to do this, how can I know that it will constantly be at 1000 Hz? Thank you in advance

Even if I am able to do this, how can I know that it will constantly be at 1000 Hz?

Study the “blink without delay” example.

the thing about the blink without delay example, the sampling frequency is not constant. It makes it blink after it has passed 1 sec and not exactly one second. does that make sense?

This is the code they have in that example: if (currentMillis - previousMillis >= interval) as you can see, they have the >= to indicate that they are just seeing if it has passed the one second mark and not exactly one second.

What I am trying to achieve is to collect data exactly 1000 times in one second. If I did the same thing I did with this example, I would not be able to achieve it because the additional milliseconds will eventually add up. Hope this makes sense. I feel like I did a bad job explaining what was going through my head

It is completely impossible to collect data “exactly 1000 times in one second”. There are always some small delays that can’t be completely controlled. Furthermore, the Arduino clock is not very accurate, so you don’t even know how long “one second”, as measured by the Arduino, actually is.

Unless you are sending data out the serial port, this Arduino is fast enough that this test:

if (currentMillis - previousMillis >= interval)

is extremely unlikely to exceed “interval” by more that 1 millisecond. And the increment errors do not accumulate, because as long as the interrupts are enabled, millis() keeps ticking at 1000 Hz regardless of what the computer is doing.

Thus, your time uncertainty is around 0.1% of the measured interval, which is probably much better than any of the other uncertainties in your measurements.

ssiltare: the thing about the blink without delay example, the sampling frequency is not constant. It makes it blink after it has passed 1 sec and not exactly one second. does that make sense?

This is the code they have in that example: if (currentMillis - previousMillis >= interval) as you can see, they have the >= to indicate that they are just seeing if it has passed the one second mark and not exactly one second.

What I am trying to achieve is to collect data exactly 1000 times in one second. If I did the same thing I did with this example, I would not be able to achieve it because the additional milliseconds will eventually add up. Hope this makes sense. I feel like I did a bad job explaining what was going through my head

If a consistent sample frequency is very important in your application (and there are many where it is), then it is acceptable to do your A/D sampling in a timer ISR. Possibly the best way to understand that is to check out how millis() works. In my v1.6.6 installation of Arduino it is in this file -> C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\wiring.c

It's easy to see in this file how the number of ms is counted in a timer ISR. If you just do your A/D sampling there, you'll have as close to 1kHz sampling as possible. You should start to be careful, however. If you spend 200us in this ISR and it runs every 1ms, then you just used up 20% of your processor. Also, any other ISRs in your system may be delayed by up to 200us now if your timer ISR happens to fire immediately before. Likewise, any other interrupts that fire first may still affect your sample frequency. You may want to minimize the number of other ISRs in your system.

One way to make your ISR more efficient is just start the A/D sample there but read it in a different interrupt when the conversion is complete. I'd need to google how to do that on the Arduino, but I can see from Table 12-1 of the ATmega328P datasheet that ISR #22 = "ADC Conversion Complete" so the capability is there.

It's true what was said about the Arduino's timer itself not being very accurate. If your application can't tolerate the actual sample frequency being +/-5% (I'm estimating, I haven't looked at the datasheet) then you need some sort of an external reference to adjust your timer. One common method is to use the 50Hz/60Hz utility line. In most places you can count on that to be a steady frequency, so you can use it measure the error in your sample frequency and adjust the timer accordingly.

Hmm…I hope you don’t mind me using this as an opportunity to learn a bit more about the Arduino A/D capabilities.

Check out Table 24-6 of the ATmega328P datasheet:
table24_6.png

It seems like there’s a mode where you can automatically start a conversion when a Timer compare match occurs. Since we know that the timer ISR that updates millis() must be compare matching at 1kHz, it’d be awfully convenient if we could use that same timer to start your A/D sampling.

At that point you wouldn’t need anything in the ISR at all. You’l either look for new samples to be complete in loop() or you could still use the ADC ISR if you’d like. Another benefit of this method is that your A/D sampling frequency should not be affected by any other ISRs in your system.

I think the main reason for the >= in if (currentMillis - previousMillis >= interval) is that millis() is not guaranteed to increment by one, from time to time it steps by two to keep the timing error minimal.