Разработка системы измерения акустики

Тема в разделе "Схемотехника, компоненты, модули", создана пользователем Un_ka, 17 фев 2025.

Метки:
  1. Un_ka

    Un_ka Гуру

    Имеется необходимость измерения спектральных параметров звука с нескольких микрофонов установленных на закрытом канале. В первую очередь интересно замерить задержку во времени прохождения звуковой волны между двумя и более микрофонами с высокой точностью.
    При этом измерение необходимо осуществлять достаточно локально, то есть микрофон должен иметь малые размеры приёмника по сравнению с длиной волны.
    Диапазон частот 100..2000 Гц (длины волн при нормальных условиях около 3-0.15м).

    В качестве микрофонов могут быть использованы следующие микрофоны:
    1. Электронные микрофоны малого диаметра (∅6-10 мм, соотношение диаметра и длины волны в худшем случае 15), пример EM6050;
    2. Микрофоны на технологии MEMS, (∅1 мм, соотношение диаметра и длины волны в худшем случае 150), пример SMD02718C4O4C-38VSM2718AB-N30-B3F;
    3. Цифровые микрофоны на технологии MEMS со в строенным усилителем и АЦП, пример im69d130.
    4. Пьезоэлектрические датчики (Пьезоэлектрические излучатели) (∅26мм, соотношение диаметра и длины волны в худшем случае 5,7), пример
      [​IMG]
    Микрофонам, естественно, нужна обвязка 1 и 2 могут потребоваться отдельные усилители, а 3 достаточно лишь нескольких конденсаторов в обвязке для подключения к микроконтроллеру сбора информации.
    В связи с этим возникает вопрос выбора подходящего микрофона.

    Подключение электретного микрофона к МК
    Согласно https://qna.habr.com/q/669863, для схемы на рисунке 1 требуется резистор 2 кОм для 3,3в и конденсатор неполярный ёмкостью более 1 мкф.
    [​IMG]
    Если методики и рекомендации по расчёту номинала резистора и конденсатора?

    Микроконтроллер.
    Были испытаны возможности двух разных микроконтроллеров без задействования всего потенциала за счёт использования оптимизация обращения к регистрам.
    1. Atmega2560 в составе Arduino mega (16 МГц);
    2. STM32F401RE в составе STM NUCLEO (84 МГц);
    Arduino mega, среда Arduino IDE, соответствующим набором библиотек, частота дискретизации составила 8927 Гц.
    Код (C++):

    #define LEN_SMPL 3000

    int* sample = new int[LEN_SMPL];
    int* sample2 = new int[LEN_SMPL];
    int n = 0, sm = 0;

    unsigned long t1 = 0, t2 = 0;
    void setup() {
      // put your setup code here, to run once:
      Serial.begin(2000000);  ///921600
      analogReference(INTERNAL1V1);
    }

    void loop() {
    t1 = micros();
      for (n = 0; n < LEN_SMPL; n++) {
        sample[n] = analogRead(A0);
        sample2[n] = analogRead(A1);
      }
      t2 = micros();
      Serial.println("Time read " + String(t2 - t1));
      t1 = micros();
      /*
      for (n = 0; n < LEN_SMPL; n++) {
        Serial.println(analogRead(A0));
      }

      t2 = micros();

    Serial.println( String(t2 - t1));

    //Serial.println("Time rw " + String(t2 - t1));
      //delay(10000);

      //   for (n = 0; n < LEN_SMPL; n++) {

      //   sm = analogRead(A0);
      //   Serial.write(sm, sizeof( sm ));
      // }

      // t2 = micros();
      // Serial.println("Time rwrite" + String(t2 - t1));
      // Serial.println("sizeof" + String(sizeof( sm )));
      // delay(10000);
    */

      t1 = micros();
      for (n = 0; n < LEN_SMPL; n++) {
        Serial.print(sample[n]);
        Serial.print(sample2[n]);
      }
      t2 = micros();
      Serial.println("\nTime write " + String(t2 - t1));




      // put your main code here, to run repeatedly:


    }

     
    STM32F401RE, среда Mbed, библиотека Mbed v6.17, частота дискретизации составила 82370 Гц.
    Код (C++):

    /* mbed Microcontroller Library
    * Copyright (c) 2019 ARM Limited
    * SPDX-License-Identifier: Apache-2.0
    */


    #include "mbed.h"
    #include "mbed_mem_trace.h"

    #include <string>
    #include <cstdint>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #defineLEN_SMPL10000
    // Blinking rate in milliseconds
    #defineBLINKING_RATE5000ms
    #definemicrosus_timestamp_t
    Timer timer;

    AnalogIn ain0(A0);
    AnalogIn ain1(A1);
    AnalogIn vrefint(ADC_VREF);
    AnalogIn tempint(ADC_TEMP);
    BufferedSerial pc(USBTX, USBRX,115200);
    uint64_t t1 = 0, t2 = 0;
    uint16_t* sample0 = new uint16_t[LEN_SMPL];
    uint16_t* sample1 = new uint16_t[LEN_SMPL];
    int n = 0, sm = 0;
    Kernel::Clock::time_point start_time,end_time;

    int main()
    {
    mbed_stats_heap_t heap_stats;
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    // DWT->LAR = 0xC5ACCE55;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
    printf("Hello, World!\n");
    printf("Vref(f): %f, Vref : %u, Temperature : %u\r\n",
    vrefint.read(), vrefint.read_u16(), tempint.read_u16());
    // Initialise the digital pin LED1 as an output
    DigitalOut led(LED1);

    while (true) {

    uint32_t start_cycles = DWT->CYCCNT;
    start_time = Kernel::Clock::now();
    for (n = 0; n < LEN_SMPL; n++) {
    sample0[n] = ain0.read_u16();
    sample1[n] = ain1.read_u16();
    }
    end_time = Kernel::Clock::now();
    uint32_t end_cycles = DWT->CYCCNT;
    auto elapsed_time = end_time - start_time;

    ThisThread::sleep_for(100ms);
    printf("Time read: %lld milliseconds\n", elapsed_time.count());
    // Вычисляем время выполнения в тактах
    uint32_t elapsed_cycles = end_cycles - start_cycles;
    printf("Time read: %lu CPU cycles\n", elapsed_cycles);

    // Если нужно перевести такты в микросекунды:
    uint32_t cpu_frequency = SystemCoreClock; // Частота процессора в Гц
    float elapsed_time_us = (float)elapsed_cycles / (cpu_frequency / 1000000.0f);
    printf("Time read: %f microseconds\n", elapsed_time_us);
    printf("cpu_frequency %lu \n", cpu_frequency);
    mbed_stats_heap_get(&heap_stats);
    printf("Heap size: %lu / %lu bytes max %lu \r\n", heap_stats.current_size, heap_stats.reserved_size, heap_stats.max_size);
    t1 = micros();
    led = !led;
    //ThisThread::sleep_for(BLINKING_RATE);

    start_time = Kernel::Clock::now();
    for (n = 0; n < LEN_SMPL; n++) {
    printf("%d %d\n",sample0[n],sample1[n]);
    }
    end_time = Kernel::Clock::now();
    elapsed_time = end_time - start_time;
    printf("\nTime write: %lld microseconds\n", elapsed_time.count());


    }
    }

     

    У данной реализации считывателя сигнала с микрофонов есть два ограничения, расширение которых является перспективным.
    1. Увеличение частоты дискретизации, за счёт оптимизации считывания сигнала;
    2. Уменьшение времени вывода считанных цифровых значений в com port, либо за счёт повышения скорости передачи у серийного порта, либо за счёт отказа от кодирования чисел в ASCII.
    Повышение скорости передачи у серийного порта пробовал, но выше 115200, У STM появляются потери данных.

    Сигналы с разных микрофонов, уже пробовал получать и обрабатывать в Matlab, кому нужно могу выложить код скрипта.
    Спектры в векторном формате emf для лучшего качества сохранил в pdf.
    По итогу есть ряд вопросов:
    1. Какой микрофон лучше использовать для имеющейся задачи?
    2. Если методики и рекомендации по расчёту номинала резистора и конденсатора при подключении электретного микрофона?
    3. Как увеличить частоту дискретизации и стоит ли?
    4. Как увеличить скорость передачи данных на ПК?
    Здесь также будут выкладываться новые результаты по мере их появления.
     
  2. parovoZZ

    parovoZZ Гуру

    Спектр получить?
    Фазу или что?
    В электретных микрофона с питанием это все уже есть
    Использовать быстрый дельта-сигма АЦП.
    BT, USB
     
  3. Un_ka

    Un_ka Гуру

    Удалось добиться увеличения скорости передачи данных за счёт отказа от буферизации при отправке данных в serial port.
    Код (C++):


    #include "mbed.h"
    #include "mbed_mem_trace.h"

    #include <string>
    #include <cstdint>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #define LEN_SMPL 10000
    // Blinking rate in milliseconds
    #define BLINKING_RATE     5000ms
    #define micros us_timestamp_t
    Timer timer;

    AnalogIn   ain0(A0);
    AnalogIn   ain1(A1);
    AnalogIn   ain2(A2);
    AnalogIn   ain3(A3);
    AnalogIn   vrefint(ADC_VREF);
    AnalogIn   tempint(ADC_TEMP);
    //BufferedSerial pc(USBTX, USBRX,115200);
    UnbufferedSerial pc(USBTX, USBRX,460800); // 2000000
    uint64_t t1 = 0, t2 = 0;
    uint16_t* sample0 = new uint16_t[LEN_SMPL];
    uint16_t* sample1 = new uint16_t[LEN_SMPL];
    uint16_t* sample2 = new uint16_t[LEN_SMPL];
    uint16_t* sample3 = new uint16_t[LEN_SMPL];
    int n = 0, sm = 0;
    Kernel::Clock::time_point start_time,end_time;

    int main()
    {
        mbed_stats_heap_t heap_stats;
     
        CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
         // DWT->LAR = 0xC5ACCE55;
          DWT->CYCCNT = 0;
          DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
        printf("Hello, World!\n");
        printf("Vref(f): %f, Vref : %u, Temperature : %u\r\n",
                     vrefint.read(), vrefint.read_u16(), tempint.read_u16());
        // Initialise the digital pin LED1 as an output
        DigitalOut led(LED1);

        while (true) {

    uint32_t start_cycles = DWT->CYCCNT;
      start_time = Kernel::Clock::now();
      for (n = 0; n < LEN_SMPL; n++) {
        sample0[n] = ain0.read_u16();
        sample1[n] = ain1.read_u16();
        sample2[n] = ain2.read_u16();
        sample3[n] = ain3.read_u16();
      }
    end_time = Kernel::Clock::now();
      uint32_t end_cycles = DWT->CYCCNT;
      auto elapsed_time = end_time - start_time;

      ThisThread::sleep_for(100ms);
    printf("Time read: %lld milliseconds\n", elapsed_time.count());

      // Вычисляем время выполнения в тактах
        uint32_t elapsed_cycles = end_cycles - start_cycles;
        printf("Time read: %lu CPU cycles\n", elapsed_cycles);

        // Если нужно перевести такты в микросекунды:
        uint32_t cpu_frequency = SystemCoreClock; // Частота процессора в Гц
        float elapsed_time_us = (float)elapsed_cycles / (cpu_frequency / 1000000.0f);
        printf("Time read: %f microseconds\n", elapsed_time_us);
        printf("cpu_frequency %lu \n", cpu_frequency);
        mbed_stats_heap_get(&heap_stats);
    printf("Heap size: %lu / %lu bytes max  %lu  \r\n", heap_stats.current_size, heap_stats.reserved_size, heap_stats.max_size);
      t1 = micros();
    led = !led;
            //ThisThread::sleep_for(BLINKING_RATE);
         
    start_time = Kernel::Clock::now();
      for (n = 0; n < LEN_SMPL; n++) {
        printf("%d %d %d %d\n",sample0[n],sample1[n],sample2[n],sample3[n]);
      }
    end_time = Kernel::Clock::now();

    elapsed_time = end_time - start_time;
    printf("\nTime write: %lld microseconds\n", elapsed_time.count());
        }
    }
     
    Также было обнаружено, что при имеющейся конфигурации ПК Matlab может получать данные без потерь до скорости 460800 бод/с (менее 0,5 Мбит/с).

    Увеличил количество считываемых каналов до 4.

    Сравнение сигналов с пьезодатчиков показало наличие разности фаз между ними.
    Пьезодатчики были заключены в корпуса, как показано на рисунке 1.
    upload_2025-2-27_22-12-5.png
    Корпус такой конструкции по задумке должен был уменьшить размеры приёмника давления по сравнению с длиной волны.
    Для испытаний использовался удалённый на расстояние более 3 длин волн динамик, на котором воспроизводился синусоидальный сигнал постоянной частоты.
    Обработка сигнала в Matlab показала, что на частоте воспроизводимый динамиком 432 Гц, наблюдалась разность фаз между двумя одинаковыми пьезодатчиками подключёнными во второй и третий канал соответственно. Теоретически разность фаз на возбуждаемый частоте между датчиками должна была быть равна фазовой задержке между измерениями, однако такого не происходит.
    О фазовой задержке между измерениями
    Пусть дискретизация АЦП fadc, имеется n каналов, тогда фазовая задержка между i-ым и j-ым каналами
    dfi (f)=(1/fadc)*(i-j)*2*pi*f
    При частоте f adc = 82370 Гц, n=4 канала, I=3, j=2, f= 432 Гц:
    0,033 радиан.
    Имеющиеся разность 0,263 радиан.
    upload_2025-2-27_22-44-14.png
    Что может быть не так?
     

    Вложения:

  4. Un_ka

    Un_ka Гуру

    Провёл испытания с трубой длиной 1 м, датчики были присоединены к трубе на одном и том же расстоянии от концов трубы в 0,5м, как показано на рисунке.

    Динамик располагался на одном из входов трубы.
    [​IMG]
    Замеры и обработка показали всё тоже наличие разности фаз между 2 и 3 каналами к которым были подключены датчики:
    [​IMG]
    Посторонних шумов меньше в трубе, да и звук от динамика слабее гаснет соотношение сигнал/шум уже радует.
     
  5. Ariadna-on-Line

    Ariadna-on-Line Гуру

    То есть высчитанные разности фаз между ближайшими по очереди каналами ровно в 8 раз больше теоретических. Это вдвое больше чем число каналов. Тогда не морочьте голову, задайте только ДВА канала и мерьте заново.
     
    Последнее редактирование: 2 мар 2025