Определение направления звука — увлекательная концепция в робототехнике и автоматизации. Она позволяет устройству «слышать» и определять направление звука. Эта концепция имеет множество применений, включая навигацию роботов, интеллектуальные системы безопасности, игры и развлечения. Ранее мы показали вам, как измерить звук в дБ с помощью Arduino, а в этом уроке давайте узнаем, как определить направление звука с помощью Arduino и нескольких микрофонов.
Ниже вы можете увидеть короткую демонстрационную GIF-картинку проекта, который мы обсудим сегодня. Давайте узнаем некоторые основы определения направления звука с помощью Arduino и нескольких микрофонов. Без лишних слов, давайте погрузимся в технику, используемую здесь.
Необходимые компоненты
Ниже приведен список необходимых компонентов. Этот проект был протестирован с Arduino UNO R4 WiFi, Arduino UNO R3 и ESP32, поэтому вы можете использовать любой из них. Однако могут быть незначительные корректировки кода для каждого микроконтроллера.
- Плата Arduino Uno (купить на AliExpress).
- Модуль усилителя микрофона MAX4466 - 4 шт.
- Модуль OLED дисплея SSD1306 128×64 с диагональю 0.96 дюйма и интерфейсом I2C (купить на AliExpress).
- Соединительные провода.
- Макетная плата (опционально).
- 3D-печать (опционально).
Как работает локализация звука?
В двух словах, локализация звука включает в себя обнаружение звука с четырех микрофонов одновременно, идентификацию сигнала с самой высокой амплитудой и определение направления источника звука на основе микрофона, принимающего самый сильный сигнал. Хотя этот метод прост, он имеет ограничения, которые могут снизить эффективность системы.
Вот тут-то и вступает в игру продвинутый метод — анализ с помощью быстрого преобразования Фурье (БПФ). БПФ смещает сигнал из временной области в частотную область, что упрощает дифференциацию звуков, например, человеческих голосов от фонового шума, путем анализа диапазонов частот. Такой подход снижает некоторые ограничения более простого метода и позволяет проводить обнаружение в фиксированном диапазоне частот.
Используя четыре микрофона, расположенных вокруг устройства, система прослушивает целевую частоту (например, 3000 Гц) и сравнивает величину звука от каждого микрофона. Arduino обрабатывает сигналы в реальном времени , находя направление на основе самого сильного сигнала. Затем эта информация визуализируется на OLED-экране с анимированными «глазами», которые отслеживают источник звука. Эта физическая установка необходима для точного определения направления, позволяя обнаруживать на полных 360°.
Для этой цели вам не обязательно прибегать к 3D-печати; для этой цели вполне подойдет обычный картон или пенопласт.
Теперь, когда вы поняли концепцию, давайте ее воплотим!
3D-модель
Я разработал 3D-корпус для этого проекта с помощью TinkerCad. 3D-модель можно скачать по следующей ссылке с GitHub.
Примечание: 3D-печать не является обязательной для сборки этого устройства для определения направления звука. Можно использовать альтернативы, такие как пена, картон или небольшие коробки.
Схема проекта
Схема будет зависеть от вашего выбора микроконтроллера. Для Arduino UNO схема будет следующей.
При использовании другого микроконтроллера сосредоточьтесь на выборе подходящих контактов АЦП для микрофонных входов. Для Arduino я использовал A0, A1, A2 и A3 для микрофонных входов, а A4 и A5 — как контакты SDA и SCL для OLED-дисплея.
Для простоты я запитал все микрофоны и OLED-дисплей от выхода Arduino 3,3 В. Модуль MAX4466 поддерживает входное напряжение от 2,4 до 5,5 В, а OLED работает при 3,3 В (некоторые дисплеи могут поддерживать до 5 В, но я рекомендую 3,3 В для безопасности).
Разобравшись со схемой, приступим к сборке.
Сборка оборудования
Давайте начнем сборку оборудования. Начиная с головной части, разместите все компоненты, как показано на рисунке.
Я закрепил модуль микрофона и OLED-дисплей горячим клеем и укоротил штырьки для беспроблемной пайки. Вы можете выбрать наиболее подходящий для вас метод.
Я обмотал микрофоны небольшим количеством ленты, чтобы обеспечить плотное прилегание.
Я начал с пайки OLED-дисплея, создав параллельное соединение шин питания и отдельные соединения для аналогового выхода и контактов SDA/SCL.
Далее я продолжил подключение оставшихся проводов.
После завершения пайки я проложил провода через центральное отверстие в нижней 3D-печати, которая функционирует как полая труба. Затем я припаял штыревые контакты для легкого подключения к Arduino UNO.
Завершив сборку оборудования, перейдем к кодированию.
Написание кода программы
Теперь давайте разберем логику кода и функциональность этого проекта локализации звука.
Программа определяет направление звука с помощью четырех микрофонов, выполняя БПФ для анализа звуковых частот, особенно около 3000 Гц. В зависимости от направления звука она отображает «глаза» на OLED-экране для отслеживания источника. Если звук не обнаружен, она отображает «сонные глаза» как состояние бездействия.
Теперь давайте разберем код, начав с используемых библиотек.
Используемые библиотеки:
- arduinoFFT — доступно в менеджере библиотек
- Wire - библиотека по умолчанию
- Adafruit_GFX — доступно в менеджере библиотек
- Adafruit_SSD1306 — доступно в диспетчере библиотек
Эти библиотеки совместимы как с Arduino UNO, так и с ESP32.
Далее инициализируются константы, переменные и библиотеки. Я создал отдельные объекты FFT для каждого микрофона, чтобы управлять ими независимо. Определены важные параметры управления, такие как целевая частота, пороговое значение амплитуды и частота дискретизации. Вы можете настроить их для тонкой настройки системы.
А далее вы можете увидеть некоторые пользовательские функции в нашей программе.
Объяснение работы кода
Функция настройки (setup()):
- Инициализирует последовательную связь и OLED-дисплей.
- Очищает дисплей, чтобы убедиться, что он готов к отображению графики.
Основной цикл (loop()):
- Цикл loop() выполняется непрерывно, вызывая функцию determineAngle() для проверки источника звука.
- Затем он вызывает drawEyes() для обновления экрана OLED-дисплея с нормальными или спящими глазами, в зависимости от полученных звуковых данных.
Определение направления звука (determineAngle()):
- Берет 64 выборки с каждого микрофона и применяет БПФ для преобразования звуковых данных в частотные данные.
- Она проверяет величину целевой частоты (3000 Гц) от каждого микрофона и определяет, какой микрофон имеет самый сильный звуковой сигнал.
- На основе самого сильного сигнала она вычисляет угол прихода звука (0°, 90°, 180° или 270°).
Отображение глаз (drawEyes()):
- Функция drawEyes() рисует различные изображения глаз на OLED-экране на основе рассчитанного угла.
- Если звук остается неизменным в течение длительного периода (на основании angleCounter()) , глаза будут выглядеть сонными.
Теперь надеюсь, вы понимаете рабочую логику нашего кода. Полный код вы можете найти в конце этой статьи.
Далее давайте подробно рассмотрим работу проекта.
Работа над проектом локализации звука
После загрузки программы конечный результат можно увидеть в GIF картинке ниже. Мы протестировали ее с различными целевыми частотами, и она отреагировала точно.
Согласно принятым нами правилам, если звук исходит с одного и того же направления в течение длительного времени, на OLED-дисплее отображается символ сна.
Этот проект можно расширить, чтобы обнаруживать диапазон частот, а не одну определенную частоту. Однако он все еще восприимчив к шуму. Хотя программные решения могут помочь, аппаратные решения, такие как аналоговые шумовые фильтры на входах микрофона, дадут наилучшие результаты.
Применения проекта определения направления звука
- Робототехническая навигация: роботы, оснащенные системой звуковой локализации, могут определять направление голосовых команд или близлежащих звуков, что помогает им более естественно взаимодействовать с людьми или быстрее реагировать на оповещения.
- Интеллектуальные системы безопасности: благодаря интеграции функции определения направления звука интеллектуальные системы безопасности могут определять подозрительные звуки , например, бьющееся стекло или громкие шумы, и немедленно направлять камеры или сигналы тревоги в этом направлении.
- Интерактивные художественные инсталляции: художники и дизайнеры могут использовать функцию определения направления звука для создания захватывающих экспозиций , которые реагируют на звуки аудитории, позволяя инсталляциям оживать в зависимости от взаимодействия с посетителями.
- Системы с голосовым управлением: в устройствах для умного дома функция определения направления звука помогает микрофонам сосредоточиться на источнике голосовых команд, повышая точность и скорость реагирования виртуальных помощников, таких как Alexa или Google Home.
- Мониторинг окружающей среды: эта технология может помочь в мониторинге дикой природы, определяя направление звуков животных или других звуков окружающей среды , способствуя исследованиям и охране природы.
- Акустические измерения и профилирование звука: инженеры и исследователи могут использовать эту систему для изучения распространения звука в различных средах или для проведения точных измерений направления звука в экспериментах.
Исходный код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
/* Audio Direction Detection with OLED Display using FFT and Multiple Microphones Description: This program captures sound from four microphones and analyzes the frequency spectrum using Fast Fourier Transform (FFT). The target frequency is set to 3000 Hz by default. Based on the detected sound direction (calculated from the strongest magnitude of the target frequency), it displays corresponding "eyes" on an OLED screen to indicate the source of the sound. If no significant sound is detected, the system enters a "sleep mode" by showing sleepy eyes. Features: - Multi-microphone setup to capture sound from multiple directions. - Fast Fourier Transform (FFT) analysis to extract frequency data. - Visual representation of sound direction via "eyes" on an OLED display. - Sleep mode: Displays "sleepy eyes" when no significant sound is detected. - Angle calculation based on the loudest sound source in 90-degree intervals. Specifications: - Sampling Frequency: 10,000 Hz - FFT Samples: 64 samples per microphone - OLED Resolution: 128x64 pixels - Target Frequency: 3000 Hz (default, can be modified) - Angle Output: 0°, 90°, 180°, 270° (corresponding to sound direction) Hardware Requirements: - 4 analog microphones (connected to A0, A1, A2, A3) - SSD1306 OLED display (128x64) - Arduino-compatible board Libraries Required: - arduinoFFT (for FFT computation) - Adafruit GFX (for OLED graphics) - Adafruit SSD1306 (for controlling the OLED display) Author: RK_ME, Circuit Digest */ #include "arduinoFFT.h" #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> // OLED display settings #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Analog inputs for microphones, when using Arduino UNO. in case of other Compactable microcontroller, you can use it analog input pins. Tested in ESP32 #define MIC1 A0 #define MIC2 A1 #define MIC3 A2 #define MIC4 A3 // Constants #define SAMPLES 64 // Number of samples for FFT, must be a power of 2 - Adjust as per your Requirement, but limited to the specific hardware #define SAMPLING_FREQ 10000 // Hz, limited by ADC conversion time - Adjust as per your Requirement, but limited to the specific hardware int SAMPLING_PERIOD_US = round(1000000 * (1.0 / SAMPLING_FREQ)); // Sampling period in microseconds // FFT object and data arrays double Real1[SAMPLES], Imag1[SAMPLES]; double Real2[SAMPLES], Imag2[SAMPLES]; double Real3[SAMPLES], Imag3[SAMPLES]; double Real4[SAMPLES], Imag4[SAMPLES]; ArduinoFFT<double> FFT1 = ArduinoFFT<double>(Real1, Imag1, SAMPLES, SAMPLING_FREQ, true); ArduinoFFT<double> FFT2 = ArduinoFFT<double>(Real2, Imag2, SAMPLES, SAMPLING_FREQ, true); ArduinoFFT<double> FFT3 = ArduinoFFT<double>(Real3, Imag3, SAMPLES, SAMPLING_FREQ, true); ArduinoFFT<double> FFT4 = ArduinoFFT<double>(Real4, Imag4, SAMPLES, SAMPLING_FREQ, true); // Variables float targetFreq = 3000.0; // Default target frequency (Hz) - Adjust as per your Requirement float selectedFreqMag[4]; // Magnitudes for target frequency float angle = 0; // Initial angle float magnitudeThreshold = 73.0; // Threshold to determine if the frequency is detected - Adjust as per your Requirement // Variables to track the angle and a counter for detecting stable angles float lastAngle = -1; // Stores the previous angle (-1 indicates uninitialized) int angleCounter = 0; // Counter for how many times the same angle is detected int angleThreshold = 50; // Threshold for how many times the same angle must be detected before showing sleepy eyes - Adjust as per your Requirement // Function to map frequency to FFT bin int frequencyToBin(float freq, float samplingFreq, int numSamples) { return round((freq * numSamples) / samplingFreq); } // Function to find the index of the maximum value in an array int findMaxIndex(float arr[], int arrSize) { int index = 0; for (int i = 1; i < arrSize; i++) { if (arr[i] > arr[index]) index = i; } return index; // Return the index of the maximum value } // Function to calculate the angle based on the microphone with the highest magnitude float calculateAngle(float arr[], int arrSize) { return findMaxIndex(arr, arrSize) * 90; // Map index to angle, assuming 90-degree intervals } // Function to draw sleepy eyes on the OLED display void drawSleepyEyes() { display.drawRect(64, 24, 16, 3, 1); // Right eye display.drawRect(42, 24, 15, 3, 1); // Left eye display.drawRect(52, 40, 19, 2, 1); // Mouth } // Function to draw eyes based on the detected angle void drawEyes() { // Check if the current angle is the same as the previous angle if (angle == lastAngle) { angleCounter++; // Increment the counter if the angle is the same } else { angleCounter = 0; // Reset counter if the angle changes } // Update the last angle lastAngle = angle; // If the angle remains the same for a certain threshold, display sleepy eyes if (angleCounter >= angleThreshold) { drawSleepyEyes(); } else { // Draw normal eyes based on the angle // Adjust eye positions based on the angle if (angle == 90) { // Downward display.drawCircle(74, 52, 7, 1); display.fillCircle(74, 54, 3, 1); display.drawCircle(48, 52, 7, 1); display.fillCircle(48, 54, 3, 1); display.drawCircle(60, 62, 31, 1); } else if (angle == 0) { // Rightward display.drawCircle(119, 35, 7, 1); display.fillCircle(121, 35, 3, 1); display.drawCircle(98, 37, 7, 1); display.fillCircle(100, 37, 3, 1); display.drawCircle(111, 52, 31, 1); display.drawCircle(96, 58, 3, 1); } else if (angle == 180) { // Leftward display.drawCircle(28, 33, 7, 1); display.fillCircle(26, 33, 3, 1); display.drawCircle(9, 26, 7, 1); display.fillCircle(7, 26, 3, 1); display.drawCircle(14, 42, 31, 1); display.drawCircle(20, 54, 3, 1); } else if (angle == 270) { // Upward display.drawCircle(72, 9, 8, 1); display.drawCircle(48, 11, 9, 1); display.fillCircle(72, 6, 3, 1); display.fillCircle(48, 7, 3, 1); display.drawCircle(61, 24, 31, 1); display.drawCircle(65, 34, 4, 1); } } } // Function to take samples from microphones and perform FFT void determineAngle() { long microseconds = micros(); // Start timing for sampling // Take samples for each microphone for (int i = 0; i < SAMPLES; i++) { Real1[i] = analogRead(MIC1); // Sample from MIC1 Real2[i] = analogRead(MIC2); // Sample from MIC2 Real3[i] = analogRead(MIC3); // Sample from MIC3 Real4[i] = analogRead(MIC4); // Sample from MIC4 Imag1[i] = Imag2[i] = Imag3[i] = Imag4[i] = 0; // Initialize imaginary components to 0 // Wait for the sampling period to finish while ((micros() - microseconds) < SAMPLING_PERIOD_US); microseconds += SAMPLING_PERIOD_US; } // Perform FFT on each microphone's data FFT1.windowing(FFTWindow::Hamming, FFTDirection::Forward); FFT1.compute(FFTDirection::Forward); FFT1.complexToMagnitude(); FFT2.windowing(FFTWindow::Hamming, FFTDirection::Forward); FFT2.compute(FFTDirection::Forward); FFT2.complexToMagnitude(); FFT3.windowing(FFTWindow::Hamming, FFTDirection::Forward); FFT3.compute(FFTDirection::Forward); FFT3.complexToMagnitude(); FFT4.windowing(FFTWindow::Hamming, FFTDirection::Forward); FFT4.compute(FFTDirection::Forward); FFT4.complexToMagnitude(); // Get magnitudes for the target frequency for each microphone int targetBin = frequencyToBin(targetFreq, SAMPLING_FREQ, SAMPLES); selectedFreqMag[0] = Real1[targetBin]; selectedFreqMag[1] = Real2[targetBin]; selectedFreqMag[2] = Real3[targetBin]; selectedFreqMag[3] = Real4[targetBin]; // Determine if the maximum magnitude exceeds the threshold float maxMagnitude = selectedFreqMag[0]; for (int i = 1; i < 4; i++) { if (selectedFreqMag[i] > maxMagnitude) { maxMagnitude = selectedFreqMag[i]; } } // If no significant frequency is detected, display sleepy eyes if (maxMagnitude < magnitudeThreshold) { drawSleepyEyes(); } else { // Calculate the angle based on the highest magnitude angle = calculateAngle(selectedFreqMag, 4); } Serial.println("Angle: " + String(angle)); } // Setup function void setup() { Serial.begin(115200); // Start serial communication if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for (;;); } display.clearDisplay(); // Clear the display display.display(); // Initialize the display } // Main loop void loop() { determineAngle(); display.clearDisplay(); drawEyes(); display.display(); delay(1); } |