תוכן עניינים:

1024 דוגמאות מנתח ספקטרום FFT באמצעות Atmega1284: 9 שלבים
1024 דוגמאות מנתח ספקטרום FFT באמצעות Atmega1284: 9 שלבים

וִידֵאוֹ: 1024 דוגמאות מנתח ספקטרום FFT באמצעות Atmega1284: 9 שלבים

וִידֵאוֹ: 1024 דוגמאות מנתח ספקטרום FFT באמצעות Atmega1284: 9 שלבים
וִידֵאוֹ: פוטושופ 2024 כבר כאן עם כל התכונות החדשות שהיו בבטא 2024, נוֹבֶמבֶּר
Anonim
1024 דוגמאות FFT ספקטרום מנתח באמצעות Atmega1284
1024 דוגמאות FFT ספקטרום מנתח באמצעות Atmega1284
1024 דוגמאות FFT ספקטרום מנתח באמצעות Atmega1284
1024 דוגמאות FFT ספקטרום מנתח באמצעות Atmega1284

הדרכה קלה יחסית (בהתחשב במורכבות של נושא זה) תראה לך כיצד תוכל להכין מנתח ספקטרום פשוט של דוגמאות 1024 באמצעות לוח מסוג Arduino (1284 צר) והפלוטר הטורי. כל סוג של לוח תואם Arduino יעשה, אבל ככל שיש לו יותר זיכרון RAM, רזולוציית התדר הטובה ביותר שתקבלו. הוא ידרוש יותר מ- 8 KB של זיכרון RAM כדי לחשב את ה- FFT עם 1024 דוגמאות.

ניתוח ספקטרום משמש לקביעת מרכיבי התדר העיקריים של אות. צלילים רבים (כמו צלילים המופקים על ידי כלי נגינה) מורכבים מתדר יסודי וכמה הרמוניות בעלות תדר המהווה מספר רב שלם של התדר הבסיסי. מנתח הספקטרום יראה לך את כל המרכיבים הספקטרליים האלה.

ייתכן שתרצה להשתמש בהתקנה זו כמודד תדרים או כדי לבדוק כל סוג של אותות שאתה חושד שמביא רעש במעגל האלקטרוני שלך.

נתמקד כאן בחלק התוכנה. אם תרצה ליצור מעגל קבוע ליישום ספציפי, יהיה עליך להגביר ולסנן את האות. התניה מוקדמת זו תלויה לחלוטין באות שברצונך ללמוד, בהתאם לאמפליטודה, עכבה, תדירות מרבית וכו '… תוכל לבדוק

שלב 1: התקנת הספרייה

אנו נשתמש בספריית ArduinoFFT שנכתבה על ידי אנריקה קונדס. מכיוון שאנו רוצים לחסוך כמה שיותר זיכרון RAM, נשתמש בענף הפיתוח של מאגר זה המאפשר להשתמש בסוג נתוני הצוף (במקום כפול) לאחסון הנתונים שנדגמו ומחושבים. אז עלינו להתקין אותו ידנית. אל דאגה, פשוט הורד את הארכיון ופרק אותו בתיקיית ספריית ה- Arduino שלך (למשל בתצורת ברירת המחדל של Windows 10: C: / Users / _your_user_name_ / Documents / Arduino / libraries)

אתה יכול לבדוק שהספרייה מותקנת כראוי על ידי חיבור אחת הדוגמאות שסופקו, כמו "FFT_01.ino".

שלב 2: קונספט טרנספורמציה של פורייה ו- FFT

אזהרה: אם אינך יכול לסבול סימון מתמטי כלשהו, ייתכן שתרצה לדלג לשלב 3. בכל מקרה, אם אינך מבין הכל, שקול את המסקנה בסוף הקטע.

ספקטרום התדרים מתקבל באמצעות אלגוריתם טרנספורמציה מהירה של פורייה. FFT הוא יישום דיגיטלי המתקרב לקונספט המתמטי של טרנספורמציה פורייה. תחת מושג זה ברגע שתקבל את התפתחות האות בעקבות ציר זמן, תוכל לדעת את ייצוגו בתחום תדרים, המורכב מערכים מורכבים (אמיתיים + דמיוניים). הרעיון הוא הדדי, כך שכאשר אתה מכיר את ייצוג תחום התדרים אתה יכול להפוך אותו חזרה לתחום הזמן ולקבל את האות בחזרה בדיוק כמו לפני השינוי.

אבל מה אנחנו הולכים לעשות עם קבוצת הערכים המורכבים המחושבים בתחום הזמן? ובכן, רובו יותיר למהנדסים. עבורנו נקרא אלגוריתם נוסף שיהפוך את הערכים המורכבים הללו לנתוני צפיפות ספקטרלית: כלומר ערך גודל (= עוצמה) המשויך לכל רצועת תדרים. מספר פס התדרים יהיה זהה למספר הדגימות.

אתה בהחלט מכיר את הרעיון של אקולייזר, כמו זה חזרה לשנות השמונים עם ה- EQ הגרפי. ובכן, נשיג את אותו סוג של תוצאות אך עם 1024 רצועות במקום 16 ורזולוציית עוצמה הרבה יותר. כאשר האקולייזר נותן מבט עולמי על המוסיקה, ניתוח הספקטרלים העדינים מאפשר לחשב במדויק את עוצמת כל אחת מ -1024 הלהקות.

רעיון מושלם, אבל:

  1. מכיוון ש- FFT היא גרסה דיגיטלית של טרנספורמציה פורייה, היא מקרבת את האות הדיגיטלי ומאבדת מידע. אז, באופן קפדני, התוצאה של ה- FFT אם תוחזר לאחור עם אלגוריתם FFT הפוך לא תיתן בדיוק את האות המקורי.
  2. כמו כן התיאוריה מתייחסת לאות שאינו סופי, אך זהו אות קבוע המתמשך. מכיוון שנעשה את זה לדיגיטלי רק לפרק זמן מסוים (כלומר דוגמאות), יציגו עוד כמה שגיאות.
  3. לבסוף הרזולוציה של ההמרה האנלוגית לדיגיטלית תשפיע על איכות הערכים המחושבים.

בפועל

1) תדירות הדגימה (ציינו fs)

נדגום אות, כלומר נמדוד את המשרעת שלו, כל 1/fs שניות. fs היא תדירות הדגימה. לדוגמה, אם נדגום ב- 8 KHz, ה- ADC (ממיר אנלוגי לדיגיטלי) שנמצא על גבי השבב יספק מדידה כל 1/8000 שניות.

2) מספר הדגימות (ציין N או דוגמאות בקוד)

מכיוון שאנו צריכים לקבל את כל הערכים לפני הפעלת ה- FFT נצטרך לאחסן אותם ולכן נגביל את מספר הדגימות. אלגוריתם ה- FFT זקוק למספר דגימות בעל עוצמה של 2. ככל שיש לנו יותר דוגמאות כך טוב יותר אך הוא דורש הרבה זיכרון, על אחת כמה וכמה שנצטרך לאחסן את הנתונים שהופכו, שהם ערכים מורכבים. ספריית ה- Arduino FFT חוסכת מעט מקום על ידי שימוש

  • מערך אחד בשם "vReal" לאחסון הנתונים שנדגמו ולאחר מכן החלק האמיתי של הנתונים שהשתנו
  • מערך אחד בשם "vImag" לאחסון החלק הדמיוני של הנתונים שהשתנו

כמות הנדרשת של זיכרון RAM שווה ל -2 (מערכים) * 32 (סיביות) * N (דוגמאות).

אז ב- Atmega1284 שלנו בעל 16 קילו -בייט של זיכרון RAM נשמור מקסימום N = 16000*8 /64 = 2000 ערכים. מכיוון שמספר הערכים חייב להיות כוח של 2, נשמור לכל היותר 1024 ערכים.

3) רזולוציית התדר

ה- FFT יחשב ערכים למספר תדרים כמו מספר הדגימות. להקות אלה ינועו בין 0 HZ לתדר הדגימה (fs). מכאן שרזולוציית התדרים היא:

פתרון מחדש = fs / N

הרזולוציה טובה יותר כשהיא נמוכה יותר. אז בשביל רזולוציה טובה יותר (נמוכה יותר) אנחנו רוצים:

  • דוגמאות נוספות, ו/או
  • fs נמוך יותר

אבל…

4) מינימום fs

מכיוון שאנו רוצים לראות הרבה תדרים, חלקם גבוהים בהרבה מ"תדר היסוד ", איננו יכולים להגדיר את ה- FS נמוך מדי. למעשה יש את משפט הדגימה Nyquist – Shannon שמאלץ אותנו להיות בעלי תדר דגימה הרבה מעל פי שניים מהתדירות המקסימלית שהיינו רוצים לבדוק.

לדוגמה, אם נרצה לנתח את כל הספקטרום מ- 0 הרץ כדי לומר 15 קילוהרץ, שזה בערך התדר המרבי שרוב בני האדם יכולים לשמוע באופן מובהק, עלינו להגדיר את תדר הדגימה על 30 קילוהרץ. למעשה האלקטרוניקים קובעים אותו לעתים קרובות על 2.5 (או אפילו 2.52) * התדירות המקסימלית. בדוגמה זו זה יהיה 2.5 * 15 KHz = 37.5 KHz. תדרי הדגימה הרגילים באודיו המקצועי הם 44.1 KHz (הקלטת תקליטורי שמע), 48 KHz ועוד.

סיכום:

נקודות 1 עד 4 מובילות ל: אנו רוצים להשתמש בכמה דוגמאות ככל האפשר. במקרה שלנו עם מכשיר זיכרון RAM של 16 KB נשקול 1024 דוגמאות. אנו רוצים לדגום בתדירות הדגימה הנמוכה ביותר האפשרית, כל עוד הוא גבוה מספיק כדי לנתח את התדר הגבוה ביותר שאנו מצפים לאות שלנו (2.5 * תדר זה, לפחות).

שלב 3: הדמיית אות

סימולציה של אות
סימולציה של אות

בניסיון הראשון שלנו, נשנה מעט את הדוגמה TFT_01.ino שניתנה בספרייה, כדי לנתח אות המורכב

  • התדר הבסיסי, מוגדר ל- 440 הרץ (מוזיקלי A)
  • הרמוניה שלישית בחצי מעוצמת היסוד ("-3 dB")
  • הרמונית חמישית ב 1/4 מהעוצמה של היסוד ("-6 dB)

אתה יכול לראות בתמונה מעל האות המתקבל. זה אכן נראה מאוד כמו אות אמיתי שאפשר לפעמים לראות באוסילוסקופ (הייתי קורא לזה "באטמן") במצב שיש חיתוך של אות סינוסי.

שלב 4: ניתוח אות מדומה - קידוד

0) כלול את הספרייה

#כלול "arduinoFFT.h"

1) הגדרות

בסעיפי ההצהרות יש לנו

const byte adcPin = 0; // A0

const uint16_t דוגמאות = 1024; // ערך זה חייב להיות תמיד כוח של 2 const uint16_t samplingFrequency = 8000; // ישפיע על הערך המקסימלי של הטיימר ב- timer_setup () SYSCLOCK/8/sampling התדירות צריכה להיות מספר שלם

מכיוון שלאות יש הרמוניה חמישית (תדירות הרמונית זו = 5 * 440 = 2200 הרץ) עלינו להגדיר את תדר הדגימה מעל 2.5 * 2200 = 5500 הרץ. כאן בחרתי 8000 הרץ.

אנו מצהירים גם על המערכים שבהם נשמור את הנתונים הגולמיים והמחושבים

float vReal [דוגמאות];

float vImag [דוגמאות];

2) אינסטנטציה

אנו יוצרים אובייקט ArduinoFFT. גרסת ה- dev של ArduinoFFT משתמשת בתבנית כדי שנוכל להשתמש בצוף או בסוג הנתונים הכפול. Float (32 סיביות) מספיק ביחס לדיוק הכללי של התוכנית שלנו.

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

3) הדמיה של האות על ידי אכלוס מערך vReal, במקום לאכלס אותו בערכי ADC.

בתחילת הלולאה אנו מאכלסים את מערך vReal עם:

מחזורי צף = (((samples) * signalFrequency) / samplingFrequency); // מספר מחזורי האות שהדגימה תקרא

for (uint16_t i = 0; i <samples; i ++) {vReal = float ((משרעת * (sin ((i * (TWO_PI * מחזורים)) / דוגמאות)))) / / בנה נתונים עם חיובי ו- ערכים שליליים */ vReal += float ((משרעת * (sin ((3 * i * (TWO_PI * מחזורים))/ דוגמאות)))/ 2.0);/ * בנה נתונים עם ערכים חיוביים ושליליים */ vReal += float ((משרעת * (sin ((5 * i * (TWO_PI * מחזורים)) / דוגמאות))) / 4.0); / * בנה נתונים עם ערכים חיוביים ושליליים * / vImag = 0.0; // יש לאפס חלק דמיוני במקרה של לולאה כדי למנוע חישובים והצפות שגויים}

אנו מוסיפים דיגיטליזציה של גל היסוד ושתי ההרמוניות עם פחות משרעת. מאשר אנו מאתחלים את המערך הדמיוני בעזרת אפסים. מכיוון שמערך זה מאוכלס על ידי אלגוריתם FFT עלינו לנקות אותו שוב לפני כל חישוב חדש.

4) מחשוב FFT

לאחר מכן אנו מחשבים את ה- FFT ואת הצפיפות הספקטראלית

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

FFT.compute (FFTDirection:: קדימה); / * Compute FFT */ 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), ההרמוניות השלישית והחמישית, עם חצי ו -1/4 מגודל f0, כצפוי. אנו יכולים לקרוא בחלק העליון של החלון f0 = 440.430114 הרץ. ערך זה אינו בדיוק 440 הרץ, בגלל כל הסיבות שהוסברו למעלה, אך הוא קרוב מאוד לערך האמיתי. לא ממש היה צורך להציג כל כך הרבה עשרונים חסרי משמעות.

שלב 6: ניתוח אות אמיתי - חיווט ה- ADC

ניתוח של אות אמיתי - חיווט ה- ADC
ניתוח של אות אמיתי - חיווט ה- ADC

מכיוון שאנו יודעים כיצד להמשיך בתיאוריה, נרצה לנתח אות אמיתי.

החיווט פשוט מאוד. חבר את הקרקע יחד ואת קו האות לסיכה A0 של הלוח שלך באמצעות נגד סדרה בערך 1 קאוהם עד 10 קאוהם.

נגד סדרה זו יגן על הקלט האנלוגי וימנע מצלצולים. הוא חייב להיות גבוה ככל האפשר כדי להימנע מצלצולים, וכמה שיותר נמוך כדי לספק מספיק זרם כדי לטעון את ה- ADC במהירות. עיין בגיליון הנתונים של MCU כדי לדעת את העכבה הצפויה של האות המחובר בכניסת ה- ADC.

עבור הדגמה זו השתמשתי בגנרטור פונקציות כדי להזין אות סינוסי של תדר 440 הרץ ומשרעת סביב 5 וולט (עדיף אם המשרעת היא בין 3 ל -5 וולט כך שה- ADC משמש כמעט בקנה מידה מלא), באמצעות נגד 1.2 קאוהם.

שלב 7: ניתוח אות אמיתי - קידוד

0) כלול את הספרייה

#כלול "arduinoFFT.h"

1) הצהרות והסתה

בחלק ההצהרה אנו מגדירים את קלט ה- ADC (A0), מספר הדגימות ותדירות הדגימה, כמו בדוגמה הקודמת.

const byte adcPin = 0; // A0

const uint16_t דוגמאות = 1024; // ערך זה חייב להיות תמיד כוח של 2 const uint16_t samplingFrequency = 8000; // ישפיע על הערך המקסימלי של הטיימר ב- timer_setup () SYSCLOCK/8/sampling התדירות צריכה להיות מספר שלם

אנו יוצרים את האובייקט ArduinoFFT

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

2) טיימר והתקנת ADC

הגדרנו את טיימר 1 כך שהוא יסתובב בתדר הדגימה (8 קילוהרץ) ויעלה הפרעה בהשוואה לפלט.

void timer_setup () {

// איפוס טיימר 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = bit (CS11) | ביט (WGM12); // CTC, מכשיר טרום של 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000 /8) / samplingFrequency) -1; }

והגדר את ה- ADC כך

  • משתמש ב- A0 כקלט
  • מפעילה אוטומטית בכל פלט טיימר 1 השווה התאמה ב '
  • יוצר הפרעה עם השלמת ההמרה

שעון ה- ADC מוגדר על 1 מגה -הרץ, על -ידי שינוי גודל מוקדם של שעון המערכת (16 מגה -הרץ) ב -16. מכיוון שכל המרה לוקחת כ -13 שעונים בקנה מידה מלא, ניתן להשיג המרות בתדר של 1/13 = 0.076 מגהרץ = 76 קילוהרץ. תדירות הדגימה צריכה להיות נמוכה משמעותית מ- 76 KHz כדי לאפשר ל- ADC זמן לדגום את הנתונים. (בחרנו fs = 8 KHz).

void adc_setup () {

ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // הפעל את ADC, רוצה הפרעה בסיום ADCSRA | = bit (ADPS2); // מכשיר טעינה מראש של 16 ADMUX = bit (REFS0) | (adcPin & 7); // הגדרת קלט ADC ADCSRB = bit (ADTS0) | bit (ADTS2); // טיימר/מונה 1 השווה את מקור ההדק של התאמה B ADCSRA | = bit (ADATE); // הפעל את ההפעלה האוטומטית}

אנו מצהירים על מטפל הפרעות שייקרא לאחר כל המרת ADC לאחסן את הנתונים המומרים במערך vReal, ולסלק את ההפרעה

// ADR השלם ISR

ISR (ADC_vect) {vReal [resultNumber ++] = ADC; אם (resultNumber == דוגמאות) {ADCSRA = 0; // לכבות את ADC}} EMPTY_INTERRUPT (TIMER1_COMPB_vect);

תוכל לקבל הסבר ממצה על המרת ADC ב- Arduino (analogRead).

3) התקנה

בפונקציית ההתקנה אנו מנקים את טבלת הנתונים הדמיונית וקוראים לפונקציות הגדרת הטיימר וה- ADC

zeroI (); // פונקציה שהגדירה ל- 0 את כל הנתונים הדמיוניים - מוסברת בחלק הקודם

timer_setup (); adc_setup ();

3) לולאה

FFT.dcRemoval (); // הסר את רכיב ה- DC של האות הזה מכיוון שה- ADC מופנה לקרקע

FFT.windowing (FFTWindow:: Hamming, FFTDirection:: Forward); // שקול נתונים FFT.compute (FFTDirection:: קדימה); // מחשב FFT FFT.complexToMagnitude (); // חישוב גודל // הדפסת הספקטרום והתדר הבסיסי f0 PrintVector (vReal, (דוגמאות >> 1), SCL_FREQUENCY); float x = FFT.majorPeak (); Serial.print ("f0 ="); הדפסה סידורית (x, 6); Serial.println ("הרץ");

אנו מסירים את רכיב ה- DC מכיוון שה- ADC מופנה לקרקע והאות מתרכז סביב 2.5 וולט בערך.

לאחר מכן אנו מחשבים את הנתונים כפי שהוסבר בדוגמה הקודמת.

שלב 8: ניתוח אות אמיתי - תוצאות

ניתוח של אות אמיתי - תוצאות
ניתוח של אות אמיתי - תוצאות

אכן אנו רואים רק תדר אחד באות הפשוט הזה. התדר הבסיסי המחושב הוא 440.118194 הרץ. גם כאן הערך הוא קירוב קרוב מאוד של התדר האמיתי.

שלב 9: מה לגבי אות סינוסואידי קצוץ?

מה לגבי אות סינוסואידי מקוצץ?
מה לגבי אות סינוסואידי מקוצץ?

עכשיו מאפשר להגביר מעט את ה- ADC על ידי הגדלת משרעת האות מעל 5 וולט, כך שהוא נחתך. אל תלחץ יותר מדי כדי לא להרוס את קלט ה- ADC!

אנו יכולים לראות כמה הרמוניות מופיעות. חיתוך האות יוצר רכיבים בתדירות גבוהה.

ראית את יסודות ניתוח FFT על לוח Arduino. כעת תוכל לנסות לשנות את תדירות הדגימה, מספר הדגימות ופרמטר החלון. הספרייה מוסיפה גם פרמטר כלשהו לחישוב ה- FFT מהר יותר עם פחות דיוק. תבחין שאם תגדיר את תדירות הדגימה נמוכה מדי, הגדלים המחושבים יראו שגויים לחלוטין בגלל קיפול ספקטרלי.

מוּמלָץ: