代码之家  ›  专栏  ›  技术社区  ›  skzlzl

arduino fft与matlab ifft

  •  1
  • skzlzl  · 技术社区  · 8 年前

    我们目前与Arduino合作。

    我正在使用“开放音乐实验室fft库”的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);
    

    这个代码是一个实验。但它并没有恢复。

    表演 fft_run () 在Arduino上,我希望在matlab中进行逆fft。

    有很多问题。

    我想以某种方式问一下该怎么办。

    使现代化

    我已根据 SleuthEye's answer 。但有一个问题。

    -阿杜诺码-

    /*
    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必须小于或等于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
    
          }
         }
        }
    

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

    1 回复  |  直到 8 年前
        1
  •  0
  •   Community Egal    7 年前

    我假设额外的频谱转换是有意的,而不是你发现的问题。例如,你不应该期望在20到40(包括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);
    }
    

    是一个技巧 obtain the inverse transform using Arduino's forward transform 由于您从时间样本开始,我假设您只需要前向变换(因此不需要这部分代码)。

    现在,与 Arduino's FFT example ,有一些差异可能暗示了发生了什么。第一个显著的差异来自示例发送的信号,即频谱幅度的下半部分(128个值),不足以重建原始信号。在您的案例中,您正确地注释了 fft_mag_log 这将允许您发送频谱的复杂值。然而,当您在fft箱上循环时,您只发送每秒钟一次的值(因此丢失了所有的虚部)。

    另一个需要注意的是数据的包装。更具体地说,您发送的是一个数据头(“start”字符串),您必须在Matlab的接收端读取,否则它将与实际数据混合。

    二进制传输

    您正在使用 Serial.println 它以ASCII格式发送你的号码,而你用Matlab的 fread 假设它们是二进制形式的,它会读取它们。为了保持一致,您应该以二进制形式发送数据 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个复数值作为交织的实/虚部分(总共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传输

    或者,您可以使用 系列.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
    }
    

    并在matlab中以ASCII格式读回 fscanf :

    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');
    
    推荐文章
    user19346  ·  Theano中的逆FFT
    9 年前