Оглавление:

Анализатор спектра БПФ с 1024 отсчетами с использованием Atmega1284: 9 шагов
Анализатор спектра БПФ с 1024 отсчетами с использованием Atmega1284: 9 шагов

Видео: Анализатор спектра БПФ с 1024 отсчетами с использованием Atmega1284: 9 шагов

Видео: Анализатор спектра БПФ с 1024 отсчетами с использованием Atmega1284: 9 шагов
Видео: Основы ЦОС: 18. Преобразование Фурье (ссылки на скачивание скриптов в описании) 2024, Июнь
Anonim
Анализатор спектра БПФ на 1024 отсчета с использованием Atmega1284
Анализатор спектра БПФ на 1024 отсчета с использованием Atmega1284
Анализатор спектра БПФ на 1024 отсчета с использованием Atmega1284
Анализатор спектра БПФ на 1024 отсчета с использованием Atmega1284

Это относительно простое руководство (учитывая сложность этого предмета) покажет вам, как можно сделать очень простой анализатор спектра на 1024 отсчета с использованием платы типа Arduino (1284 Narrow) и последовательного плоттера. Подойдет любая совместимая с Arduino плата, но чем больше у нее оперативной памяти, тем лучше будет разрешение по частоте. Для вычисления БПФ с 1024 отсчетами потребуется более 8 КБ ОЗУ.

Спектральный анализ используется для определения основных частотных составляющих сигнала. Многие звуки (например, звуки музыкального инструмента) состоят из основной частоты и некоторых гармоник, имеющих частоту, кратную основной частоте. Анализатор спектра покажет вам все эти спектральные составляющие.

Возможно, вы захотите использовать эту настройку в качестве частотомера или для проверки любых сигналов, которые, как вы подозреваете, вносят некоторый шум в вашу электронную схему.

Здесь мы сосредоточимся на программной части. Если вы хотите создать постоянную схему для конкретного применения, вам необходимо усилить и отфильтровать сигнал. Это предварительное кондиционирование полностью зависит от сигнала, который вы хотите изучить, в зависимости от его амплитуды, импеданса, максимальной частоты и т. Д. Вы можете проверить

Шаг 1. Установка библиотеки

Мы будем использовать библиотеку ArduinoFFT, написанную Энрике Кондесом. Поскольку мы хотим максимально сэкономить ОЗУ, мы будем использовать ветвь разработки этого репозитория, которая позволяет использовать тип данных с плавающей запятой (вместо двойного) для хранения выборочных и вычисленных данных. Поэтому нам нужно установить его вручную. Не волнуйтесь, просто загрузите архив и распакуйте его в папку библиотеки Arduino (например, в конфигурации Windows 10 по умолчанию: C: / Users / _your_user_name_ / Documents / Arduino / libraries)

Вы можете проверить правильность установки библиотеки, скомпилировав один из предоставленных примеров, например «FFT_01.ino».

Шаг 2: Преобразование Фурье и концепции БПФ

Предупреждение: если вы терпеть не можете видеть какие-либо математические обозначения, вы можете перейти к шагу 3. В любом случае, если вы не понимаете всего, просто рассмотрите заключение в конце раздела.

Частотный спектр получается с помощью алгоритма быстрого преобразования Фурье. БПФ - это цифровая реализация, которая приближает математическую концепцию преобразования Фурье. Согласно этой концепции, как только вы получите эволюцию сигнала по оси времени, вы сможете узнать его представление в частотной области, состоящей из комплексных (действительных + мнимых) значений. Эта концепция взаимна, поэтому, когда вы знаете представление в частотной области, вы можете преобразовать его обратно во временную область и получить обратно сигнал точно так же, как до преобразования.

Но что мы собираемся делать с этим набором вычисленных комплексных значений во временной области? Что ж, большая часть этого будет оставлена инженерам. Мы назовем другой алгоритм, который преобразует эти комплексные значения в данные спектральной плотности: это значение величины (= интенсивности), связанное с каждой полосой частот. Количество частотных диапазонов будет таким же, как и количество выборок.

Вы наверняка знакомы с концепцией эквалайзера, подобной этой «Назад в 1980-е с графическим эквалайзером». Что ж, мы получим такие же результаты, но с 1024 полосами вместо 16 и гораздо большим разрешением по интенсивности. Когда эквалайзер дает общее представление о музыке, точный спектральный анализ позволяет точно вычислить интенсивность каждой из 1024 полос.

Идеальная концепция, но:

  1. Поскольку БПФ представляет собой оцифрованную версию преобразования Фурье, оно аппроксимирует цифровой сигнал и теряет некоторую информацию. Таким образом, строго говоря, результат БПФ, преобразованный обратно с помощью инвертированного алгоритма БПФ, не даст в точности исходный сигнал.
  2. Также теория рассматривает сигнал, который не является конечным, но является постоянным постоянным сигналом. Поскольку мы будем оцифровывать его только на определенный период времени (например, образцы), будет внесено еще несколько ошибок.
  3. Наконец, разрешение аналого-цифрового преобразования повлияет на качество вычисленных значений.

На практике

1) Частота дискретизации (отмечена fs)

Мы будем отбирать сигнал, то есть измерять его амплитуду каждые 1 / fs секунд. fs - частота дискретизации. Например, если мы производим выборку на частоте 8 кГц, АЦП (аналого-цифровой преобразователь), встроенный в микросхему, будет обеспечивать измерение каждые 1/8000 секунды.

2) Количество выборок (отмечено N или выборок в коде)

Поскольку нам нужно получить все значения перед запуском БПФ, нам придется их сохранить, поэтому мы ограничим количество выборок. Алгоритму БПФ требуется количество выборок, равное степени 2. Чем больше у нас выборок, тем лучше, но для этого требуется много памяти, тем более, что нам также понадобится хранить преобразованные данные, которые являются комплексными значениями. Библиотека Arduino FFT экономит место за счет использования

  • Один массив с именем «vReal» для хранения выборочных данных, а затем реальной части преобразованных данных.
  • Один массив с именем "vImag" для хранения мнимой части преобразованных данных.

Необходимый объем оперативной памяти равен 2 (массивы) * 32 (бит) * N (выборки).

Таким образом, в нашем Atmega1284, который имеет хорошие 16 КБ ОЗУ, мы будем хранить максимум N = 16000 * 8/64 = 2000 значений. Поскольку количество значений должно быть степенью 2, мы сохраним максимум 1024 значения.

3) Разрешение по частоте

БПФ вычислит значения для такого количества частотных диапазонов, которое соответствует количеству отсчетов. Эти полосы будут охватывать от 0 Гц до частоты дискретизации (fs). Следовательно, разрешение по частоте составляет:

Разрешение = fs / N

Чем меньше разрешение, тем лучше. Итак, для лучшего разрешения (ниже) мы хотим:

  • больше образцов и / или
  • нижняя фс

Но…

4) Минимальная фс

Поскольку мы хотим видеть много частот, некоторые из которых намного выше, чем "основная частота", мы не можем установить слишком низкую fs. Фактически, существует теорема выборки Найквиста – Шеннона, которая заставляет нас иметь частоту дискретизации, намного превышающую вдвое максимальную частоту, которую мы хотели бы проверить.

Например, если мы хотим проанализировать весь спектр от 0 Гц до 15 кГц, что примерно является максимальной частотой, которую большинство людей может отчетливо слышать, мы должны установить частоту дискретизации на 30 кГц. Фактически, электронщики часто устанавливают ее на 2,5 (или даже 2,52) * максимальной частоты. В этом примере это будет 2,5 * 15 кГц = 37,5 кГц. Обычные частоты дискретизации в профессиональном аудио составляют 44,1 кГц (запись аудио компакт-дисков), 48 кГц и более.

Заключение:

Пункты 1–4 приводят к тому, что мы хотим использовать как можно больше образцов. В нашем случае с устройством ОЗУ 16 КБ мы будем рассматривать 1024 сэмпла. Мы хотим производить выборку с минимально возможной частотой дискретизации, если она достаточно высока для анализа самой высокой частоты, которую мы ожидаем в нашем сигнале (по крайней мере, 2,5 * этой частоты).

Шаг 3: имитация сигнала

Имитация сигнала
Имитация сигнала

Для нашей первой попытки мы немного изменим пример TFT_01.ino, приведенный в библиотеке, чтобы проанализировать сигнал, состоящий из

  • Основная частота, установленная на 440 Гц (музыкальный A)
  • 3-я гармоника на половине мощности основной гармоники («-3 дБ»)
  • 5-я гармоника на 1/4 мощности основной гармоники ("-6 дБ)

Вы можете увидеть на картинке выше результирующий сигнал. Это действительно очень похоже на реальный сигнал, который иногда можно увидеть на осциллографе (я бы назвал его «Бэтменом») в ситуации, когда происходит отсечение синусоидального сигнала.

Шаг 4: Анализ симулированного сигнала - кодирование

0) Включите библиотеку

#include "arduinoFFT.h"

1) Определения

В разделах объявлений у нас есть

const byte adcPin = 0; // A0

const uint16_t samples = 1024; // Это значение ВСЕГДА ДОЛЖНО быть степенью 2 const uint16_t samplingFrequency = 8000; // Повлияет на максимальное значение таймера в timer_setup () SYSCLOCK / 8 / samplingFrequency должно быть целым числом

Поскольку сигнал имеет 5-ю гармонику (частота этой гармоники = 5 * 440 = 2200 Гц), нам необходимо установить частоту дискретизации выше 2,5 * 2200 = 5500 Гц. Здесь я выбрал 8000 Гц.

Мы также объявляем массивы, в которых будем хранить необработанные и вычисленные данные.

float vReal [образцы];

float vImag [образцы];

2) Создание экземпляра

Создаем объект ArduinoFFT. Версия ArduinoFFT для разработчиков использует шаблон, поэтому мы можем использовать либо тип данных float, либо двойной тип данных. Float (32 бита) достаточно для общей точности нашей программы.

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, samples, samplingFrequency);

3) Моделирование сигнала путем заполнения массива vReal вместо заполнения его значениями АЦП.

В начале цикла мы заполняем массив vReal следующим образом:

циклы с плавающей запятой = (((образцы) * signalFrequency) / samplingFrequency); // Количество сигнальных циклов, которые будет читать выборка

for (uint16_t i = 0; i <samples; i ++) {vReal = float ((амплитуда * (sin ((i * (TWO_PI * циклы)) / samples)))); / * Создание данных с положительным и отрицательные значения * / vReal + = float ((амплитуда * (sin ((3 * i * (TWO_PI * циклы)) / образцы))) / 2.0); / * Создание данных с положительными и отрицательными значениями * / vReal + = float ((амплитуда * (sin ((5 * i * (TWO_PI * циклы)) / samples))) / 4.0); / * Построение данных с положительными и отрицательными значениями * / vImag = 0.0; // Мнимая часть должна быть обнулена в случае зацикливания, чтобы избежать неправильных вычислений и переполнений}

Мы добавляем оцифровку основной волны и двух гармоник с меньшей амплитудой. Затем мы инициализируем воображаемый массив нулями. Поскольку этот массив заполняется алгоритмом БПФ, нам нужно очищать его снова перед каждым новым вычислением.

4) вычисления БПФ

Затем мы вычисляем БПФ и спектральную плотность

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward);

FFT.compute (FFTDirection:: Forward); / * Вычислить БПФ * / FFT.complexToMagnitude (); / * Вычислить величины * /

Операция FFT.windowing (…) изменяет необработанные данные, потому что мы запускаем FFT на ограниченном количестве выборок. Первый и последний образцы представляют собой разрыв (с одной стороны нет «ничего»). Это источник ошибки. Операция "управления окнами" имеет тенденцию уменьшать эту ошибку.

FFT.compute (…) с направлением «Вперед» вычисляет преобразование из временной области в частотную.

Затем мы вычисляем значения величины (т. Е. Интенсивности) для каждого из частотных диапазонов. Теперь массив vReal заполнен значениями величин.

5) Последовательный чертеж плоттера

Напечатаем значения на последовательном плоттере, вызвав функцию printVector (…)

PrintVector (vReal, (примеры >> 1), SCL_FREQUENCY);

Это общая функция, которая позволяет печатать данные с осью времени или оси частоты.

Также печатаем частоту полосы, имеющей наибольшее значение амплитуды.

float x = FFT.majorPeak ();

Serial.print ("f0 ="); Серийный. Печать (x, 6); Serial.println ("Гц");

Шаг 5: Анализ смоделированного сигнала - результаты

Анализ смоделированного сигнала - результаты
Анализ смоделированного сигнала - результаты

Мы видим 3 пика, соответствующих основной частоте (f0), 3-й и 5-й гармоникам, с половиной и 1/4 величины f0, как и ожидалось. Мы можем прочитать вверху окна f0 = 440,430114 Гц. По всем причинам, описанным выше, это значение не совсем 440 Гц, но оно очень близко к реальному значению. На самом деле не было необходимости показывать такое количество знаков после запятой.

Шаг 6: Анализ реального сигнала - Подключение АЦП

Анализ реального сигнала - подключение АЦП
Анализ реального сигнала - подключение АЦП

Поскольку мы знаем, как действовать в теории, мы хотели бы проанализировать реальный сигнал.

Электропроводка очень проста. Соедините заземление и сигнальную линию с выводом A0 вашей платы через последовательный резистор номиналом от 1 кОм до 10 кОм.

Этот последовательный резистор защищает аналоговый вход и предотвращает звон. Он должен быть как можно более высоким, чтобы избежать звонка, и как можно более низким, чтобы обеспечить ток, достаточный для быстрой зарядки АЦП. Обратитесь к таблице данных MCU, чтобы узнать ожидаемый импеданс сигнала, подключенного ко входу АЦП.

Для этой демонстрации я использовал функциональный генератор для подачи синусоидального сигнала с частотой 440 Гц и амплитудой около 5 вольт (лучше всего, если амплитуда составляет от 3 до 5 вольт, поэтому АЦП используется почти на полную шкалу) через резистор 1,2 кОм..

Шаг 7: Анализ реального сигнала - кодирование

0) Включите библиотеку

#include "arduinoFFT.h"

1) Объявления и создание экземпляров

В разделе объявлений мы определяем вход АЦП (A0), количество выборок и частоту дискретизации, как в предыдущем примере.

const byte adcPin = 0; // A0

const uint16_t samples = 1024; // Это значение ВСЕГДА ДОЛЖНО быть степенью 2 const uint16_t samplingFrequency = 8000; // Повлияет на максимальное значение таймера в timer_setup () SYSCLOCK / 8 / samplingFrequency должно быть целым числом

Создаем объект ArduinoFFT

ArduinoFFT FFT = ArduinoFFT (vReal, vImag, samples, samplingFrequency);

2) Настройка таймера и АЦП

Мы устанавливаем таймер 1 так, чтобы он работал с частотой дискретизации (8 кГц) и генерировал прерывание при сравнении выходов.

void timer_setup () {

// сбросить таймер 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = бит (CS11) | бит (WGM12); // CTC, предделитель 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000/8) / samplingFrequency) -1; }

И установите АЦП так, чтобы он

  • Использует A0 как вход
  • Срабатывает автоматически при каждом выходе таймера 1, сравнение совпадения B
  • Создает прерывание, когда преобразование завершено

Тактовая частота АЦП устанавливается на 1 МГц путем предварительного масштабирования системной тактовой частоты (16 МГц) на 16. Поскольку каждое преобразование занимает приблизительно 13 тактов на полной шкале, преобразование может быть достигнуто на частоте 1/13 = 0,076 МГц = 76 кГц. Частота дискретизации должна быть значительно ниже 76 кГц, чтобы у АЦП было время для выборки данных. (мы выбрали fs = 8 кГц).

void adc_setup () {

ADCSRA = бит (ADEN) | бит (ADIE) | бит (ADIF); // включаем АЦП, требуется прерывание по завершении ADCSRA | = bit (ADPS2); // Предделитель 16 ADMUX = bit (REFS0) | (adcPin & 7); // установка входа АЦП ADCSRB = bit (ADTS0) | бит (ADTS2); // Таймер / Счетчик1 Сравнить источник триггера Match B ADCSRA | = bit (ADATE); // включаем автоматический запуск}

Мы объявляем обработчик прерывания, который будет вызываться после каждого преобразования АЦП для сохранения преобразованных данных в массиве vReal и очистки прерывания.

// ADC завершает ISR

ISR (ADC_vect) {vReal [resultNumber ++] = ADC; если (resultNumber == образцы) {ADCSRA = 0; // выключаем АЦП}} EMPTY_INTERRUPT (TIMER1_COMPB_vect);

Вы можете получить исчерпывающее объяснение преобразования АЦП на Arduino (analogRead).

3) Настройка

В функции настройки мы очищаем таблицу мнимых данных и вызываем функции настройки таймера и АЦП.

zeroI (); // функция, которая устанавливает в 0 все мнимые данные - объяснено в предыдущем разделе

timer_setup (); adc_setup ();

3) Петля

FFT.dcRemoval (); // Удаляем постоянную составляющую этого сигнала, поскольку АЦП привязан к земле

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward); // Взвешивание данных FFT.compute (FFTDirection:: Forward); // Вычислить БПФ FFT.complexToMagnitude (); // Вычислить величины // распечатать спектр и основную частоту f0 PrintVector (vReal, (samples >> 1), SCL_FREQUENCY); float x = FFT.majorPeak (); Serial.print ("f0 ="); Серийный. Печать (x, 6); Serial.println ("Гц");

Мы удаляем составляющую постоянного тока, потому что АЦП привязан к земле, а сигнал сосредоточен примерно в районе 2,5 вольт.

Затем мы вычисляем данные, как описано в предыдущем примере.

Шаг 8: Анализ реального сигнала - результаты

Анализ реального сигнала - результаты
Анализ реального сигнала - результаты

Действительно, в этом простом сигнале мы видим только одну частоту. Расчетная основная частота равна 440,118194 Гц. Здесь значение снова очень близко к реальной частоте.

Шаг 9: А как насчет обрезанного синусоидального сигнала?

А как насчет обрезанного синусоидального сигнала?
А как насчет обрезанного синусоидального сигнала?

Теперь давайте немного перегрузим АЦП, увеличив амплитуду сигнала выше 5 вольт, чтобы он был ограничен. Не нажимайте слишком много, чтобы не повредить вход АЦП!

Мы видим появление некоторых гармоник. Ограничение сигнала создает высокочастотные компоненты.

Вы ознакомились с основами анализа БПФ на плате Arduino. Теперь вы можете попробовать изменить частоту дискретизации, количество отсчетов и параметр управления окнами. Библиотека также добавляет некоторые параметры для более быстрого вычисления БПФ с меньшей точностью. Вы заметите, что если вы установите слишком низкую частоту дискретизации, вычисленные величины будут казаться полностью ошибочными из-за спектрального сворачивания.

Рекомендуемые: