Получение кода FFT arduino для работы более 9 часов с использованием micros()
Я новичок в Arduino, программировании и электротехнике (учусь в свободное время) и работаю над созданием ЭЭГ. Я отключил аппаратное обеспечение и сумел получить числовые данные от аналога (A0) Arduino. Следующим шагом является преобразование этих чисел для вывода доминирующей частоты, и мне удалось найти код, который использует БПФ для определения доминирующей частоты на входе A0. Ниже приведен код:
#include "arduinoFFT.h"
#define SAMPLES 128 //Должна быть степенью 2
#define SAMPLING_FREQUENCY 1000 //Гц, должно быть меньше 10000 из-за АЦП
arduinoFFT FFT = arduinoFFT();
unsigned int sampling_period_us;
unsigned long microseconds;
double vReal[SAMPLES];
double vImag[SAMPLES];
void setup() {
Serial.begin(115200);
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
}
void loop() {
/*SAMPLING*/
for(int i=0; i<SAMPLES; i++)
{
microseconds = micros(); //Переполняется примерно через 70 минут!
vReal[i] = analogRead(0);
vImag[i] = 0;
while(micros() < (microseconds + sampling_period_us)){
}
}
/*FFT*/
FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
double peak = FFT.MajorPeak(vReal, SAMPLES, SAMPLING_FREQUENCY);
/*PRINT RESULTS*/
Serial.println(peak); //Распечатываем, какая частота является наиболее доминирующей.
for(int i=0; i<(SAMPLES/2); i++)
{
/*View all these three lines in serial terminal to see which frequencies has which amplitudes*/
//Serial.print((i * 1.0 * SAMPLING_FREQUENCY) / SAMPLES, 1);
//Serial.print(" ");
//Serial.println(vReal[i], 1); //Просмотр только этой строки в последовательном плоттере для визуализации ячеек
}
delay(1000); //Повторение процесса каждую секунду ИЛИ:
//пока(1); //Запускаем код один раз
У меня проблема со строкой microсекунды = micros(). В комментарии говорится, что это переполнится через 70 минут, и после нескольких дней исследований я вроде как понимаю, что под этим подразумевается. Однако мне нужно, чтобы эта программа работала более 9 часов, чтобы я мог регистрировать доминирующую частоту мозговых волн в течение ночи. Возможно ли это?
@Chris Do, 👍1
Обсуждение2 ответа
Крис До, при таком использовании micros
действительно возникает проблема опрокидывания. В этом случае пробы отбираются на полной скорости без перерыва. Это создаст неверные данные для БПФ.
Решение находится в примере Мигание без задержки. Волшебным способом избежать проблемы опрокидывания является вычитание двух значений unsigned long
.
библиотека arduinoFFT находится в диспетчере библиотек Arduino IDE.
В примере "FFT_03.ino" используется micros
неправильным образом.
Следующий скетч показывает, как избежать проблемы опрокидывания:
// Используйте micros() без проблем с опрокидыванием для фиксированной частоты дискретизации
// для определенного количества образцов.
#define SAMPLES 128
unsigned long previousMicros;
const unsigned long intervalMicros = 1000UL; // 1000 Гц — это 1000 мкс
float vReal[SAMPLES];
float vImag[SAMPLES];
void setup() {
}
void loop() {
previousMicros = micros(); // устанавливаем предыдущийМикрос непосредственно перед вводом оператора for
for(int i=0; i<SAMPLES; i++) {
// Оператор while для ожидания.
// Обратите внимание, что ';' в конце означает «ничего не делать».
while( micros() - previousMicros < intervalMicros);
// «previousMicros» увеличивается на фиксированное число для фиксированной частоты дискретизации.
previousMicros += intervalMicros; // для следующего интервала
vReal[i] = float(analogRead(0));
vImag[i] = 0.0;
}
delay(1000);
}
Возьмите текущее значение micros
и вычтите предыдущее значение micros. Результатом этого вычитания является допустимое число даже во время ролловера. Это единственный способ, который всегда работает.
В строке расчета периода выборки слишком много вещей, которые могут пойти не так. Эти строки:
unsigned int sampling_period_us;
sampling_period_us = round(1000000*(1.0/SAMPLING_FREQUENCY));
В моем примере я установил для intervalMicros
определенное число. Не могли бы вы сделать то же самое? Когда все заработает, можно попробовать посчитать.
Добавлено: ответ Эдгара Боне точно такой же. Это неудивительно, потому что другого пути нет.
Re “The example "FFT\_03.ino" uses micros
in the wrong way”: Я не знал, что OP скопировал плохой пример из самой библиотеки. Хороший улов! Я только что отправил [запрос на извлечение](https://github.com/kosme/arduinoFFT/pull/21) автору arduinoFFT. Надеюсь, он исправит это в upstream., @Edgar Bonet
Пока вы никогда не сравниваете временные метки, перенос не является проблема. Идиома, которую вы использовали:
while (micros() < previous_timestamp + period) ...
сравнивает две временные метки. Это неверно, и это терпит неудачу всякий раз, когда
micros()
переворачивается. Однако вычитание временных меток для получения продолжительности
отлично работает в событиях опрокидывания. И сравнение длительности делает
всегда работайте. Итак, правильная идиома:
while (micros() - previous_timestamp < period) ...
Обратите также внимание, что способ обновления микросекунд
неверен. Ты
увеличивая его на нужный период плюс на время, затраченное
ЦП для выполнения части кода в цикле. Вместо этого вам следует
увеличить его на запрошенный период и не более. Вот мой взгляд на
чего вы пытаетесь достичь:
/* SAMPLING */
microseconds = micros(); // записываем начальное время
for (int i = 0; i < SAMPLES; i++) {
while (micros() - microseconds < sampling_period_us)
; // ждем подходящего момента для взятия образца
microseconds += sampling_period_us; // обновление по периоду
vReal[i] = analogRead(0);
vImag[i] = 0;
}
Эдгар Бонет и Джот, я очень ценю ваши ответы. Я думаю, что я наткнулся на тему ролловера во время исследования и увидел решение, но так и не понял его до сих пор. Просто для ясности, однако, проблема с использованием micros заключается в том, что он будет ролловером (другими словами, примерно через 4,26*10^9 микросекунд он вернется к 0 и снова начнет счет вверх). Решение состоит в том, чтобы использовать интервал, потому что численное значение theinterval будет тем же самым, даже если micros() ролловера. Это правильно? Я попробую этот подход. Большое спасибо за вашу помощь!, @Chris Do
@ChrisDo: Это верно. См. также [переполнение millis() ... плохая вещь?](https://www.gammon.com.au/millis), @Edgar Bonet
Привет, Эдгар, после нескольких дней экспериментов с кодом, который ты предоставил, кажется, что вывод FFT становится очень спорадическим через некоторое время. По сравнению с оригинальным кодом, который я нашел, вывод этого оригинального кода будет примерно таким: 23, 22, 23, 24, 25, 26, 30, 27, 25. С помощью предоставленного вами кода будет выведено что-то вроде 23, 22, 65, 102, 300, 450, 23, 34, 46, 89. Вы случайно не знаете, почему так происходит?, @Chris Do
@ChrisDo: Это не связано с проблемой ролловера. Понятия не имею, почему ваши данные выглядят так., @Edgar Bonet
- Почему мои часы реального времени показывают неверное время с моего ПК?
- Разница между «time_t» и «DateTime»
- Несколько неблокирующих таймеров обратного отсчета?
- 4-битный счетчик вверх и вниз
- Программирование Arduino Uno R3 для срабатывания реле каждые 24 часа
- Объяснить функцию pulseIn с помощью arduino и ультразвукового датчика
- Как повторить код
- Проблема с использованием Arduino Mega Timer2 с прерыванием PinChange
Привет, интересный проект. Сколько памяти нужно?, @Mikael Patel
Извините, но я не уверен, что вы имеете в виду. Если я правильно понимаю, я пытаюсь брать доминирующую частоту каждые десять минут в течение ~9 часов. Это означало бы, что выход COM будет отображать 54 различных данных или запоминать их. (6 данных каждый час * 9 часов)., @Chris Do