Два микрофона. Положение диктора в пространстве.

Тема в разделе "Arduino & Shields", создана пользователем ilgamer, 7 мар 2012.

  1. Корней

    Корней Гик

    Попробуем выяснить, откуда взялись эти 112 мкс на одно АЦПреобразование. ATmega328 в ардуине работает на 16 МГц, а АЦП способен выполнять с частотой всего 9кГц. Выглядит как надувательство.
    Посмотрим даташит ATmega328, раздел ADC:
    Теперь ясно, что 112 мкс и 9кГц ардуины - не предел для МК, но зачем в ардуине установлен далеко не самый производительный режим ADC? А самое главное, можно ли нам поменять эти установки, и как?
    Смотрим даташит:
    Умножим 9кГц на 13 - 117кГц. 16МГц/117кГц - 136. Ближайшее значение делителя - 128. Т.е. разработчики ардуино установили частоту ADC для обеспечения максимально возможной точности преобразования. Технически же ADC может обеспечить в несколько раз большую частоту семплирования, при условии, что точность - последнее, что нас интересует.
    Так как данные для БПФ у нас 8-битные, то воспользуемся кодом из исходных текстов инициализации ардуино для изменения частоты ADC МК.
    Добавляем в наш скетч процедуру увеличения частоты ADC в 8 раз (то есть изменяем делитель со 128 на 16):
    Код (Text):

    #include <avr/sfr_defs.h>
    #include <fix_fft.h>

    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

    #define POW2 7
    const unsigned int DLEN=1<<POW2;
    char im[DLEN];
    char data[DLEN];

    void setup(){
      Serial.begin(115200);
     
      cbi(ADCSRA,ADEN);
      sbi(ADCSRA,ADPS2);
    сbi(ADCSRA,ADPS1);
    сbi(ADCSRA,ADPS0);
      sbi(ADCSRA,ADEN);
    }
     
    Остальной код без изменений.
    Получаем:
     
    Megakoteyka и Dmitriy Kunin нравится это.
  2. Корней

    Корней Гик

    16 мкс на семпл - это частота выборки в 60кГц, что позволит нам работать с сигналом до 30кГц, что для работы с голосом избыточно и это в 10 раз меньше, чем задержка между сигналами. Поэтому мы изменим цикл выборки, так что бы частота дискретизации осталась 10кГц, что обеспечит возможность обрабатывать сигнал до 5кГц. При 128 семплах, минимальная частота и шаг диаграммы - 78Гц.
    Общая производительность также увеличится. Слово длительностью полсекунды, может быть обработано раз 15.

    Код (Text):
      for (i=0; i < DLEN; i++){
       delayMicroseconds(58);
        val = analogRead(0);
         val = analogRead(0);
        data[i] = (val>>2) -128;
        im[i] = 0;

        data[i] = (val>>2) -128;
        im[i] = 0;
    };
    Продолжение следует.
     
    Dmitriy Kunin нравится это.
  3. Dmitriy Kunin

    Dmitriy Kunin Гик

    Корней, огромный респект и благодарность, за исследование темы!
    Сам собирался разбирать эту проблемму, правда я не ограничен Ардуино, и эти исследования могли пройти мимо этого сайта.
    Я считаю, что не в материале дело, а в форме волновода.
    Если использовать две трубки направленные в разные стороны, длинной сантиметров по несколько (надо посчитать частоту), то половина дела уйдёт в эту механическую часть.
    Вычисления останутся те же, а вот исходный сигнал станет более явно разделён во времени, что улучшит стабильность результатов.
     
  4. Корней

    Корней Гик

    На здоровье. Но мы еще не закончили.;)
    Я умышлено использую такую простую аудиоконфигурацию. Как исчерпаем ее возможности, перейдем к "ушам". А материал между микрофонами нас интересует для того, что бы получить затухание высоких частот, аналогичное эффекту "головы между ушами".
     
  5. ilgamer

    ilgamer Нерд

    Возможно стоит использовать более плотный разделитель вроде книги? Ничего на тему таблиц проводимости, поглощения звуковых волн, я не нашел.
     
  6. Корней

    Корней Гик

    http://www.engineeringtoolbox.com/accoustic-sound-absorption-d_68.html
    Про эффективное поглошение звука подробно рассказано по вашей первой ссылке.
    Если ориентироваться на "природу", то наверное имеет смысл найти материал с плотностью, равной плотности мозга живого человека.
     
  7. ilgamer

    ilgamer Нерд

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

    Корней Гик

    Продолжаем. С быстродействием мы определились. Как говорил товарищ Бендер - на такие шансы ловить можно. У нас даже есть небольшой запас по производительности fft - таблица значений синуса находится во flash, пожертвовав 192 байта ОЗУ мы уменьшим время каждой операции доступа к значениям этой таблицы на целый такт CPU. Но оснований заниматься такой оптимизацией у нас пока нет. Лучше убедимся в том, что взятая нами библиотека fft работает. Что бы 100% контролировать исходные данные, заполним массивы выборок синтезированным сигналом. Например, воспользуемся той же таблицей значений синуса из fft.c.
    Код (Text):

    #include <fix_fft.h>


    #define POW2 7
    const unsigned int DLEN=1<<POW2;
    char im[DLEN];
    char data[DLEN];


    #define N_WAVE    256
    const prog_int8_t FullSinewave[N_WAVE] PROGMEM = {
      0, 3, 6, 9, 12, 15, 18, 21,
      24, 28, 31, 34, 37, 40, 43, 46,
      48, 51, 54, 57, 60, 63, 65, 68,
      71, 73, 76, 78, 81, 83, 85, 88,
      90, 92, 94, 96, 98, 100, 102, 104,
      106, 108, 109, 111, 112, 114, 115, 117,
      118, 119, 120, 121, 122, 123, 124, 124,
      125, 126, 126, 127, 127, 127, 127, 127,

      127, 127, 127, 127, 127, 127, 126, 126,
      125, 124, 124, 123, 122, 121, 120, 119,
      118, 117, 115, 114, 112, 111, 109, 108,
      106, 104, 102, 100, 98, 96, 94, 92,
      90, 88, 85, 83, 81, 78, 76, 73,
      71, 68, 65, 63, 60, 57, 54, 51,
      48, 46, 43, 40, 37, 34, 31, 28,
      24, 21, 18, 15, 12, 9, 6, 3,

      0, -3, -6, -9, -12, -15, -18, -21,
      -24, -28, -31, -34, -37, -40, -43, -46,
      -48, -51, -54, -57, -60, -63, -65, -68,
      -71, -73, -76, -78, -81, -83, -85, -88,
      -90, -92, -94, -96, -98, -100, -102, -104,
      -106, -108, -109, -111, -112, -114, -115, -117,
      -118, -119, -120, -121, -122, -123, -124, -124,
      -125, -126, -126, -127, -127, -127, -127, -127,

      -127, -127, -127, -127, -127, -127, -126, -126,
      -125, -124, -124, -123, -122, -121, -120, -119,
      -118, -117, -115, -114, -112, -111, -109, -108,
      -106, -104, -102, -100, -98, -96, -94, -92,
      -90, -88, -85, -83, -81, -78, -76, -73,
      -71, -68, -65, -63, -60, -57, -54, -51,
      -48, -46, -43, -40, -37, -34, -31, -28,
      -24, -21, -18, -15, -12, -9, -6, -3
    };
     
    signed char getSignal(int sample){
      return pgm_read_byte_near(FullSinewave + ((sample<<1)&0xff));
    }

    void drawSignal(char* data_p){
      for(int i=0;i<DLEN;i++){
        Serial.print(data_p[i],DEC);
        Serial.print(' ');
      };
      Serial.println();
    }
    void drawFFT(char* data_p,char* im_p){
      for (int i=0; i<(DLEN>>1);i++){
        int amp= sqrt(data_p[i] * data_p[i] + im_p[i] * im_p[i]);
        Serial.print(amp,DEC);
        Serial.print(' ');
      };
      Serial.println();
    }
    void setup(){
      Serial.begin(115200);
    }

    void loop(){
      int i;
      for (i=0; i < DLEN; i++){
        data[i] =getSignal(i);
        im[i] = 0;
      };
      Serial.print("Signal: ");
      drawSignal(data);
      fix_fft(data,im,POW2,0);
      Serial.print("FFT result: ");
      drawFFT(data,im);
    }
     
     
    Так как мы сделали так, что период сигнала равен длительности выборки, то после БПФ мы должны увидеть значения амплитуды только в первом элементе массива. Частота нашего сигнала - единица. Не 1 Гц, а именно единица, так как сейчас длительностные характеристики нашего сигнала измеряются относительно ширины выборки.
    Получаем:
    Значимая величина амплитуды в 1 первом элементе массива.

    Увеличим частоту вдвое:
    Код (Text):
    signed char getSignal(int sample){
      return pgm_read_byte_near(FullSinewave + ((sample<<2)&0xff));
    }
    Вчетверо:

    Код (Text):
    signed char getSignal(int sample){
      return pgm_read_byte_near(FullSinewave + ((sample<<3)&0xff));
    }
    В 32 раза:
    Код (Text):
    signed char getSignal(int sample){
      return pgm_read_byte_near(FullSinewave + ((sample<<6)&0xff));
    }
    Работает.
     
  9. Корней

    Корней Гик

    Зачем же ее высчитывать. Она известна.
    Совет Дмитрия мы послушали. Но как мы будем менять направленность микрофонов, если мы не понимаем, какие материалы и как следует применять для этого? И каких необходимых показаний можно добиться?
     
  10. Корней

    Корней Гик

    Продолжаем. Усложним задачу. Синтезируем сигнал из составляющих разной частоты.
    Код (Text):
    #include <fix_fft.h>

    #define POW2 7
    const unsigned int DLEN=1<<POW2;
    char im[DLEN];
    char data[DLEN];

    #define N_WAVE    256
    const prog_int8_t FullSinewave[N_WAVE] PROGMEM = {
      0, 3, 6, 9, 12, 15, 18, 21,
      24, 28, 31, 34, 37, 40, 43, 46,
      48, 51, 54, 57, 60, 63, 65, 68,
      71, 73, 76, 78, 81, 83, 85, 88,
      90, 92, 94, 96, 98, 100, 102, 104,
      106, 108, 109, 111, 112, 114, 115, 117,
      118, 119, 120, 121, 122, 123, 124, 124,
      125, 126, 126, 127, 127, 127, 127, 127,

      127, 127, 127, 127, 127, 127, 126, 126,
      125, 124, 124, 123, 122, 121, 120, 119,
      118, 117, 115, 114, 112, 111, 109, 108,
      106, 104, 102, 100, 98, 96, 94, 92,
      90, 88, 85, 83, 81, 78, 76, 73,
      71, 68, 65, 63, 60, 57, 54, 51,
      48, 46, 43, 40, 37, 34, 31, 28,
      24, 21, 18, 15, 12, 9, 6, 3,

      0, -3, -6, -9, -12, -15, -18, -21,
      -24, -28, -31, -34, -37, -40, -43, -46,
      -48, -51, -54, -57, -60, -63, -65, -68,
      -71, -73, -76, -78, -81, -83, -85, -88,
      -90, -92, -94, -96, -98, -100, -102, -104,
      -106, -108, -109, -111, -112, -114, -115, -117,
      -118, -119, -120, -121, -122, -123, -124, -124,
      -125, -126, -126, -127, -127, -127, -127, -127,

      -127, -127, -127, -127, -127, -127, -126, -126,
      -125, -124, -124, -123, -122, -121, -120, -119,
      -118, -117, -115, -114, -112, -111, -109, -108,
      -106, -104, -102, -100, -98, -96, -94, -92,
      -90, -88, -85, -83, -81, -78, -76, -73,
      -71, -68, -65, -63, -60, -57, -54, -51,
      -48, -46, -43, -40, -37, -34, -31, -28,
      -24, -21, -18, -15, -12, -9, -6, -3
    };

    signed int test[DLEN];
    signed int maxT=127;

    signed char getSin(char sample, char dividerPow){
      return pgm_read_byte_near(FullSinewave + ((sample<<(1+dividerPow))&0xff));
    }

    signed char getSignal(int sample){
      return test[sample]*127.0/maxT;
    }

    signed int generateSignal(signed int* data_p){
      signed int maxT=-32767;
      for(int i=0;i<DLEN;i++){
        data_p[i]=getSin(i,1)+getSin(i,2)+getSin(i,4);
        if(data_p[i]>maxT) maxT=data_p[i];
      }
      return maxT;
    }

    void drawSignal(char* data_p){
      for(int i=0;i<DLEN;i++){
        Serial.print(data_p[i],DEC);
        Serial.print(' ');
      };
      Serial.println();
    }
    void drawFFT(char* data_p,char* im_p){
      for (int i=0; i<(DLEN>>1);i++){
        int amp= sqrt(data_p[i] * data_p[i] + im_p[i] * im_p[i]);
        Serial.print(amp,DEC);
        Serial.print(' ');
      };
      Serial.println();
    }
    void setup(){
      Serial.begin(115200);
      maxT=generateSignal(test);
    }

    void loop(){
      int i;
      for (i=0; i < DLEN; i++){
        data[i] =getSignal(i);
        im[i] = 0;
      };
      Serial.print("Signal: ");
      drawSignal(data);
      fix_fft(data,im,POW2,0);
      Serial.print("FFT result: ");
      drawFFT(data,im);
    }
     
     
    Работает.
     
  11. Корней

    Корней Гик

    Да, амплитудно-частотная диаграмма похожа на правду.
    Посмотрим фазу.
    Для получения значения фазы из результатов БПФ необходимо вычислить арктангенс (хотя, если нам достаточно отношения "больше-меньше" между разными значениями фаз, то арктангенс можно вообще не вычислять). Библиотека avr, используемая в ардуино, имеет такую фунцию, но после того, как atan2 в Arduino-22 вернула 2 с лишним радиана, мы заменили ее на простую линейно-кусочную апроксимацию, точности которой достаточно для нашей задачи.
    Получилась функция, возвращающая фазу в градусах.
    Код (Text):
    float phase(char cim,char cre){
      if(!cre) if(cim<0) return -90.0;
      else return 90.0;
      float d=float(cim)/float(cre);
      float ad=abs(d);
      float f;
      if(ad>=0&&ad<=1) f=ad*45.0;
      else if(ad>1&&ad<4) f=ad*((75.96-45)/(4-1))+36;
      else if(ad>=4&&ad<=128) f=ad*((90-75.96)/(128-4))+75.5;
      if(d<0) return -f;
      return f;
    }
    Добавляем ее в последний скетч, модифицируем функцию вывода результата:
    Код (Text):
    void drawFFT(char* data_p,char* im_p){
      for (int i=0; i<(DLEN>>1);i++){
        int amp= sqrt(data_p[i] * data_p[i] + im_p[i] * im_p[i]);
        float f=phase(im_p[i],data_p[i]);
        Serial.print(amp,DEC);
        Serial.print('(');
        Serial.print(f);
        Serial.print(')');
        Serial.print(' ');
      };
      Serial.println();
    }
    Получаем:
    Похоже, что есть проблема. Фаза одной из составляющих смещена почти на 180 градусов, что не соответствует исходному сигналу. Либо ошибка в библиотеке fft, либо ошибка в нашем скетче. Нам проще проверить сначала свой код. Первое, что проверим - мы масштабировали синтезированный сигнал, для того, что бы его значения укладывались в диапазон [-128;127] исходных данных для fft. Вполне возможно, что масштабирование изменило форму сигнала. Изменим операцию
    Код (Text):
     data_p[i]=getSin(i,1)+getSin(i,2)+getSin(i,4);
    ценой потери точности формы сигнала так, что бы масштабирование не работало:
    Код (Text):
     data_p[i]=getSin(i,1)/3+getSin(i,2)/3+getSin(i,4)/3;
    Получаем:
    Фазы всех трех составляющих совпадают. Разница между отсчетами сигнала, полученного разными способами не превышает 1. Но, вероятно, этого вполне достаточно, что бы в условиях используемой цельночисленной математики повлиять на знак величины, если та близка к нулю.
    Для проверки выведем значения действительной и мнимой частей результата БПФ для каждого из вариантов синтезирования сигнала:
    Теперь мы знаем причину и можем с ней бороться. Например, считать модуль(1) за 0, или вообще вычислять фазу из модуля im/re. Но пока не будем ничего менять и посмотрим, может быть для сравнения фаз сигналов эта проблема не существенна, или же мы сможем выбрать более подходящий способ борьбы.
     
  12. Корней

    Корней Гик

    Синтезируем второй сигнал со сдвигом на величину, соответствующую, например, 180 градусам фазы для сигнала 16 частоты.
    Код (Text):
     int sh=4;
      for (i=0; i < DLEN; i++){
        data0[i] =getSignal(i);
        im0[i] = 0;
        if(i<(DLEN-sh)) data1[i]=getSignal(i+sh);
        else data1[i]=0;
        im1[i] = 0;
      };
    Значения 1, 4 и 16 элементов диаграмм:
    Фаза 1 частоты сдвинута, примерно, на 23 градуса, фаза 2 частоты - примерно на 45, фаза 16 частоты - примерно на 180. Работает.
    Перед тем, как обработать реальный голосовой сигнал, используем реальный же, но простой сигнал.
    Подадим на аналоговые входы 0 и 1 ардуино сигнал со сважностью 2 с его же pwm выходов 11 и 10.
    Код (Text):
    #include <avr/sfr_defs.h>
     #include <fix_fft.h>

    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

     char im0[DLEN];
    char data0[DLEN];
    char im1[DLEN];
    char data1[DLEN];
    void drawSignal(char* data_p){
      for(int i=0;i<DLEN;i++){
        Serial.print(data_p[i],DEC);
        Serial.print(' ');
      };
      Serial.println();
    }
    void drawFFT(char* data_p,char* im_p,char i){
      int amp= sqrt(data_p[i] * data_p[i] + im_p[i] * im_p[i]);
      float f=phase(im_p[i],data_p[i]);
      Serial.print(amp,DEC);
      Serial.print('{');
      Serial.print(data_p[i],DEC);
      Serial.print(';');
      Serial.print(im_p[i],DEC);
      Serial.print('}');
      Serial.print('(');
      Serial.print(f);
      Serial.print(')');
      Serial.print(' ');
    }
    void drawFFT(char* data_p,char* im_p){
      for (int i=0; i<(DLEN>>1);i++) drawFFT(data_p,im_p,i);
      Serial.println();
    }
    float phase(char cim,char cre){
      if(!cre) if(cim<0) return -90.0;
      else return 90.0;
      float d=float(cim)/float(cre);
      float ad=abs(d);
      float f;
      if(ad>=0&&ad<=1) f=ad*45.0;
      else if(ad>1&&ad<4) f=ad*((75.96-45)/(4-1))+36;
      else if(ad>=4&&ad<=128) f=ad*((90-75.96)/(128-4))+75.5;
      if(d<0) return -f;
      return f;
    }
    void setup(){
      Serial.begin(115200);
      cbi(ADCSRA,ADEN);
      sbi(ADCSRA,ADPS2);
      cbi(ADCSRA,ADPS1);
      cbi(ADCSRA,ADPS0);
      sbi(ADCSRA,ADEN);
      analogWrite(11,127);
      analogWrite(10,127);
    }
    void loop(){
      int i;
      int sh=4;
      unsigned long s;
      Serial.println("sa");
      Serial.println(s=micros());
      for (i=0; i < DLEN; i++){
        delayMicroseconds(56);
        data0[i] =(analogRead(0)>>2) -128;
        data1[i]=(analogRead(1)>>2) -128;
        im0[i] = 0;
        im1[i] = 0;
      };
      Serial.println((micros()-s)/DLEN);
      Serial.print("Signal 0: ");
      drawSignal(data0);
      Serial.print("Signal 1: ");
      drawSignal(data1);
      fix_fft(data0,im0,POW2,0);
      fix_fft(data1,im1,POW2,0);
      Serial.print("FFT result 0: ");
      drawFFT(data0,im0);
      Serial.print("FFT result 1: ");
      drawFFT(data1,im1);
      Serial.print("FFT short result 0: ");
      drawFFT(data0,im0,5);
      drawFFT(data0,im0,6);
      drawFFT(data0,im0,7);
      Serial.println();
      Serial.print("FFT short result 1: ");
      drawFFT(data1,im1,5);
      drawFFT(data1,im1,6);
      drawFFT(data1,im1,7);
      Serial.println();
    }
     
    Получаем:
    Попробуем определить частоту pwm ардуино: максимальная амплитуда наблюдается в 6 элементе диаграммы, каждый элемент имеет ширину 73Гц, как мы уже рассчитывали, итого мы наблюдаем максимум амплитуды в районе частоты 438Гц. Проверим: http://arduino.cc/en/Reference/AnalogWrite.
    "У нас все точно".
    Попробуем разобраться, почему другие элементы диаграммы значимо не равны 0.
    1. Специалисты по спектральному анализу утвержают, что гармонический сигнал имеет прямоугольный спектр, а прямоугольный сигнал имеет гармонический спектр.
    2. Мы оцифровываем сигнал в произвольные моменты времени, поэтому по краям массива семплов могут (и остаются) "обрывки" сигнала разной длительности, например:
    которые алгоритм БПФ "принимает" за сигналы каких-то других частот.
    С 1 причиной нам ничего делать не нужно, так как мы не планируем работать с прямоугольными сигналами. Для борьбы со второй причиной "лишних" частот в спектре существуют специальные математические процедуры. Для нас эти процедуры означают дополнительный объем вычислений, или, как минимум, дополнительные затраты ОЗУ (если мы хотим сохранить имеющуюся точность диаграммы), поэтому сначала попробуем обойтись без них.

    С амплитой разобрались. Посмотрим на фазу. А с фазой, на первый взгляд, все плохо.
    Причина та же: мы оцифровываем сигнал в произвольные моменты времени и БПФ каждый раз "видит" сигнал смещенным на разную величину. Но нас интересует разность фаз двух сигналов и, возможно, эта величина не будет зависеть от абсолютных величин фаз каждого из сигналов.
     
  13. Корней

    Корней Гик

    Сдвигаем второй сигнал на величину, соответствующую примерно 45 градусному сдвигу его фазы.
    Код (Text):

    int sh=3;
    for (i=0; i < DLEN; i++){
        delayMicroseconds(56);
        data0[i] =(analogRead(0)>>2) -128;
        if(i<sh) data1[i]=0;
        if(i<(DLEN-sh)) data1[i+sh]=(analogRead(1)>>2) -128;
        im0[i] = 0;
        im1[i] = 0;
      };
    Сдвигаем первый сигнал на величину, соответствующую примерно 45 градусному сдвигу его фазы.

    Код (Text):

    int sh=3;
    for (i=0; i < DLEN; i++){
        delayMicroseconds(56);
        data1[i] =(analogRead(0)>>2) -128;
        if(i<sh) data0[i]=0;
        if(i<(DLEN-sh)) data0[i+sh]=(analogRead(1)>>2) -128;
        im0[i] = 0;
        im1[i] = 0;
      };
    Очевидно, что если считать, что сдвиг фаз сигналов не может превышать четверть периода, т.е. 90 градусов, то мы можем однозначно определить направление разницы фаз и ее величину по имеющимся данным.
    Продолжение следует.
     
    Andrey.D75 нравится это.
  14. barok

    barok Нуб

    Если мы косинус будем раскладывать в ряд фурье по синусам, то чем больше членов ряда, тем точнее будет восроизведена форма. Однако всегда при угле=0 амплитуда =0. Это я к тому, что не получить угол сдвига фаз=пи/2 из ряда фурье ну никак.
     
  15. Корней

    Корней Гик

    Вполне возможно. Но к чему это утверждение в этой теме?
     
  16. barok

    barok Нуб

    Видимо надо сдвиг искать не через БПФ.
     
  17. Корней

    Корней Гик

    В этой теме искали сдвиг через БПФ?
     
  18. barok

    barok Нуб

    "Выводы из проделанного: фазовый сдвиг сигнала - надежный критерий для определения направления на источник звука".
    "В следующей части "полезания в кузов" мы повторим измерения с использованием естественного источника звука."
    Вроде это основной инструмент темы....

    Это я к тому, что может выровнять сигналы от микрофонов по амплитуде, и двигая по углу, добиться их совпадения. Так яснее как-то это выглядит, и понятнее, какая точность "наведения". Причем если в сигнале есть и низкая и высокая частота, то точнее будет.
     
  19. teddyfox

    teddyfox Нерд

    Увидел ваши посты и решил поделиться наболевшим. Лучше поздно, чем ничего.

    Делаю акустический определитель направления. Хочу, чтобы мой робот-путешественник на мой хлопок возвращался ко мне. 8 микрофонов (8 штук электретных DFRobot Analog Sound Sensor DFR0034) расположил с шагом 45 градусов. Замерив аналоговый сигнал с их выходов после хлопка (предварительно обработав и усилив) и сравнив показания, можно определить направление на источник звука с точностью до 22,5 градусов (а если откалибровать идеально микрофоны и применив математику похитрее - то и с бОльшей). За несколько итераций робот должен выйти на меня.

    На картинке показал как обрабатывается сигнал с микрофона для подготовки его к отправке на аналоговый вход Arduin'ы (Мега). Сигнал от хлопка с расстояния в пол-метра дает всплеск в 100 мВ. На расстоянии в 5-6 метров будет десяток мВ (а то и меньше). Усиливаем до диапазона 0-5В (применяю ОУ rail-to-rail с однополярным питанирем 5В) (см. схему). Дальше - на пиковый детектор чтоб сделать макс. значение сигнала попродолжительнее. Дальше - на компаратор чтобы отсечь шумы и случайные звуки. Выход с компаратора завожу на прерывание для запуска аналоговых измерений (через монтажное ИЛИ со всех восьми компараторов).

    О времени считывания analogRead(): на Меге время аналогового чтения (вместо декларируемых 110 мкс) можно резко сократить, настроив прескейлер АЦП на 16 МГц (без ущерба точности). Вот так:

    bitClear(ADCSRA,ADPS0); bitClear(ADCSRA,ADPS1); bitSet(ADCSRA,ADPS2);

    У меня получается за 1,5 мс (макс. уровень сигналов держится пиковым детектором 5 мс) десять раз прочесть 8 каналов - из десяти значений каждого канала я отбрасываю два наибольших и два наименьших, а остальные шесть усредняю.

    Все спаяно, все работает. То чем сейчас занимаюсь - максимально обужаю диаграмму направленности микрофонов и калибрую их показания. Электретные микрофоны - ненаправленные. Поэтому сначала засунул каждый в полихлорвиниловую трубочку, залил сзади мягким силиконовым герметиком, обмотал ворсистой тканью, засунул все это хозяйство в 8-угольную коробочку (фото) со звукоизолированными стенками. Всё равно пока диаграммы еще широки (не получается 100% определения с точностью 22,5 град). Еще манек позвукоизолирую и если не получится решить задачу восьмью микрофонами, то уж для схемы 4-х микрофонов узости диаграмм достаточно.

    Если этот текст будет вам полезен, то буду рад.
    :) foto.JPG
     

    Вложения:

    • cxema.jpg
      cxema.jpg
      Размер файла:
      82,3 КБ
      Просмотров:
      1.082
  20. nailxx

    nailxx Официальный Нерд Администратор

    Обалдеть! Про прескейлер ADC не подозревал. Очень ценно, спасибо.