arduino fft 和 matlab ifft

arduino fft and matlab ifft

我们目前与 Arduino 合作。

我正在使用"open music labs FFT library"

的fft库

我的问题有两点。

  1. Arduino 代码问题

  2. Matlab 中的逆 fft(使用 Arduino 的 FFT 结果)

    以下代码使用 Arduino FFT 库进行 FFT(快速傅里叶变换)

    /*
    fft_adc_serial.pde
    guest openmusiclabs.com 7.7.14
    example sketch for testing the fft library.
    it takes in data on ADC0 (Analog0) and processes them
    with the fft. the data is sent out over the serial
    port at 115.2kb.
    */
    
    //#define LOG_OUT 1 // use the log output function
    #define FFT_N 256 // set to 256 point fft
    
    void setup() {
    Serial.begin(115200); // use the serial port
    TIMSK0 = 0; // turn off timer0 for lower jitter
    ADCSRA = 0xe5; // set the adc to free running mode
    ADMUX = 0x40; // use adc0
    DIDR0 = 0x01; // turn off the digital input for adc0
    }
    
    
    void loop() {
    while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
    while(!(ADCSRA & 0x10)); // wait for adc to be ready
    ADCSRA = 0xf5; // restart adc
    byte m = ADCL; // fetch adc data
    byte j = ADCH;
    int k = (j << 8) | m; // form into an int
    k -= 0x0200; // form into a signed int
    k <<= 6; // form into a 16b signed int
    fft_input[i] = k; // put real data into even bins
    fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    
    for (int i = 0 ; i < 512 ; i += 2) {
    fft_input[i] =  (fft_input[i] >> 8);
    fft_input[i+1] = -(fft_input[i+1] >> 8);
    }
    
    
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //fft_mag_log(); // take the output of the fft
    sei();
    
    
    Serial.print("start");
    
    
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
    
    if (! ((i>=20 && i<=40) || (i>=FFT_N-40 && i<=FFT_N-20)))
    {
     fft_input[i] = 0;
     fft_input[i+1] = 0;
    } 
    
    Serial.println(fft_input[i]); // send out the data
      }
     }
    }
    

matlab串口通信代码

   clear all
   clc

   arduino=serial('COM22','BaudRate',115200 );

   fopen(arduino);

   data = fread(arduino, 256);

   ifft(data , 'symmetric');

   fclose(arduino); 
   delete(instrfindall);

使用此代码进行实验。但是没有恢复。

在Arduino上执行fft_run (),我想在matlab中进行逆fft

有很多问题。

想以某种方式请教一下。

更新

我已根据 进行了更改。但有一个问题。

-arduino代码-

/*
fft_adc_serial.pde
guest openmusiclabs.com 7.7.14
example sketch for testing the fft library.
it takes in data on ADC0 (Analog0) and processes them
with the fft. the data is sent out over the serial
port at 115.2kb.
*/

//#define LOG_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft

#include <FFT.h> // include the library

void setup() {
  Serial.begin(115200); // use the serial port
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready

      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response

    for (int i = 0 ; i < 512 ; i += 2) {
      fft_input[i] =  (fft_input[i] >> 8);
      fft_input[i+1] = -(fft_input[i+1] >> 8);
    }

    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //  fft_mag_log(); // take the output of the fft
    sei();
    Serial.println("start");
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
      Serial.write(fft_input[i]);   // send out the real part
      Serial.write(fft_input[i+1]); // send out the imaginary part
    }
  }
}

-matlab端-

clear all
clc

arduino=serial('COM22','BaudRate',115200 );

fopen(arduino);

header = fread(arduino, 5);   % skip "start" header
data   = fread(arduino, 512); % read actual data

% now rearrange the data
rearranged = data(1:2:end) + 1i * data(2:2:end);

recoverd = ifft(rearranged, 'symmetric');


fclose(arduino); 
delete(instrfindall);

我的问题是:它删除了过滤器部分。

Arduino 将数据发送到 MATLAB。来自 Arduino 的 512 数据。 (FFT_N-实数256,虚数256。)

不是确切的恢复。在 matlab 中执行 ifft,而不是原始数据。

数据格式有问题。

这种形式的数据好像是通信有问题(arduino转matlab)

data = fread(arduino, 512); % read actual data.

但我猜。具体原因没找到。

更新

感谢您的回复。

    for (int i = 0 ; i < 512 ; i += 2) {
    fft_input[i] =  (fft_input[i] >> 8);
    fft_input[i+1] = -(fft_input[i+1] >> 8);
    }

已发现不需要此代码。

    for (byte i = 0 ; i < FFT_N ; i+=2) { 
     Serial.write(fft_input[i]);   // send out the real part
     Serial.write(fft_input[i+1]); // send out the imaginary part
    }

我的困难是, 当您执行此代码时,OUTPUT 的部分是 256 REAL 和 256 IMAGINARY。

但是,

   header = fread(arduino, 5);    % skip "start" header
   data   = fread(arduino, 1024); % read actual data sent in binary form

   % now rearrange the data
   rearranged = (data(1:4:end) + 256*data(2:4:end)) + 1i *(data(3:4:end) +     256*data(4:4:end));

   recovered = ifft(rearranged, 'symmetric');

"SIZE * PRECISION must be less than or equal to InputBufferSize.."

缓冲区大小问题...

所以再试一次。 我不得不按照你说的修改代码。

    /*
    fft_adc_serial.pde
    guest openmusiclabs.com 7.7.14
    example sketch for testing the fft library.
    it takes in data on ADC0 (Analog0) and processes them
    with the fft. the data is sent out over the serial
    port at 115.2kb.
    */

    //#define LOG_OUT 1 // use the log output function
    #define FFT_N 256 // set to 256 point fft

    #include <FFT.h> // include the library

    void setup() {
    Serial.begin(115200); // use the serial port
    TIMSK0 = 0; // turn off timer0 for lower jitter
    ADCSRA = 0xe5; // set the adc to free running mode
    ADMUX = 0x40; // use adc0
    DIDR0 = 0x01; // turn off the digital input for adc0
    }

    void loop() {
    while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
    while(!(ADCSRA & 0x10)); // wait for adc to be ready
    ADCSRA = 0xf5; // restart adc
    byte m = ADCL; // fetch adc data
    byte j = ADCH;
    int k = (j << 8) | m; // form into an int
    k -= 0x0200; // form into a signed int
    k <<= 6; // form into a 16b signed int
    fft_input[i] = k; // put real data into even bins
    fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency respons
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //    fft_mag_log(); // take the output of the fft
    sei();
    Serial.println("start"); // header send 
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
    Serial.write(fft_input[i]);   // send out the real part
    Serial.write(fft_input[i+1]); // send out the imaginary part

      }
     }
    }

你的回答让我很忙。使活跃。好答案。

我假设额外的频谱转换是有意的,而不是您认为有问题的地方。例如,您不应该期望取回 bins 20-40 中的频谱值,因为您明确地将它们归零。另外,代码

for (int i = 0 ; i < 512 ; i += 2) {
  fft_input[i] =  (fft_input[i] >> 8);
  fft_input[i+1] = -(fft_input[i+1] >> 8);
}

的一个技巧。由于您从时间样本开始,我假设您只需要正向变换(因此不需要那部分代码)。

现在,与 Arduino's FFT example 相比,有一些差异可能暗示了正在发生的事情。第一个显着差异是示例发送的是频谱幅度的下半部分(128 个值),不足以重建原始信号。在您的情况下,您正确地注释掉了 fft_mag_log ,它应该允许您发送频谱的复数值。但是,当您遍历 fft bin 时,您只会发送每个第二个值(因此会丢失所有虚部)。

另一件需要注意的事情是数据的打包。更具体地说,您正在发送一个数据头("start" 字符串),您必须在 Matlab 的接收端读取它,否则它只会混入您的实际数据中。

二进制传输

您正在使用 Serial.println which sends your number in ASCII form, whereas you read them back with Matlab's fread which reads them assuming they are in binary form. For consitency you should send your data in binary form with Serial.write:

for (byte i = 0 ; i < FFT_N ; i+=2) { 
  Serial.write(fft_input[i]);   // send out the real part
  Serial.write(fft_input[i+1]); // send out the imaginary part
}

然后,由于您将 256 个复值作为交错的 real/imaginary 部分发送(总共 512 个值),您将需要读取这 512 个值(通常每个值 2 个字节,以小端顺序排列)并在 Matlab 端重新排列数据:

header = fread(arduino, 5);    % skip "start" header
data   = fread(arduino, 1024); % read actual data sent in binary form

% now rearrange the data
rearranged = (data(1:4:end) + 256*data(2:4:end)) + 1i *(data(3:4:end) + 256*data(4:4:end));

recovered = ifft(rearranged, 'symmetric');

ASCII 传输

或者您可以使用 Serial.println 发送数据(即以纯 ASCII 格式):

for (byte i = 0 ; i < FFT_N ; i+=2) { 
  Serial.println(fft_input[i]);   // send out the real part
  Serial.println(fft_input[i+1]); // send out the imaginary part
}

然后用 fscanf:

在 matlab 中以 ASCII 格式读回
fscanf(arduino, "start"); % skip "start" header
data = fscanf(arduino, "%d");    % read actual data sent in plain ASCII form

% now rearrange the data
rearranged = data(1:2:end) + 1i * data(2:2:end);

recovered = ifft(rearranged, 'symmetric');