AI-Powered Piano Trainer: Learn Songs With Real-Time Feedback
AI-Powered Piano Trainer: Learn Songs With Real-Time Feedback

Здравствуйте! Меня зовут Ада Ло́пес, я студентка первого курса факультета «Креативные технологии и ИИ» в Университете Хауэст в Кортрейке.

Этот проект — моя первая полностью самостоятельная работа по интеграции искусственного интеллекта с «железом». Фортепианный тренер на базе ИИ — интерактивный комплекс для помощи начинающим в обучении игре на фортепиано. Система предоставляет обратную связь в реальном времени и отслеживает исполнение заранее заданной мелодии.

Под катом — весь путь создания проекта. Мы пройдем от обучения ИИ-модели и создания корпуса — до построения контура обратной связи и решения проблем с потоковой передачей данных в реальном времени. Опыт получился отличным! Он был наполнен отладкой, открытиями и множеством ночных тестов.

В основе лежит простая идея: пользователь играет на клавиатуре мелодию «Twinkle, Twinkle, Little Star». В это время USB‑камера, подключенная к Raspberry Pi и установленная над клавишами, фиксирует каждое нажатие. Видеопоток в реальном времени передается на ноутбук. Там обученная мною модель для распознавания объектов YOLOv8 определяет, какая же клавиша нажата.

Далее распознанная нота отправляется обратно на Raspberry Pi. Устройство сравнивает ее с ожидаемой нотой в последовательности и выводит результат на ЖК-дисплей, подключенный через интерфейс I2C. В конце песни тренажер показывает итоговую оценку точности и даже проигрывает мелодию с помощью зуммера, сигнализируя об окончании сеанса.

Оборудование, материалы и ПО

Ниже представлен полный перечень всего, что потребовалось. Что‑то уже было, что‑то пришлось позаимствовать из учебных наборов, поэтому итоговая стоимость может оказаться и ниже — тут все зависит от ваших запасов.

Категория

Компонент

Примечание / Стоимость

Электроника и оборудование

Raspberry Pi 8GB

€88.95

USB-камера

€23.39

Набор для проектов Freenove

€52.59

Ноутбук

Из личных запасов

MIDI-клавиатура

Из личных запасов

Материалы для корпуса

Листы мультиплексной фанеры толщиной 4 мм

4 шт., €18

Услуги лазерной резки

Выполнено в университете, €5

Светодиодная лента

€7

Программное обеспечение и библиотеки

MakerCase + Inkscape

Для проектирования и кастомизации корпуса

Roboflow

Бесплатный аккаунт для создания и обучения набора данных

YOLOv8 (через библиотеку ultralytics)

Легковесная модель для распознавания объектов

Python-библиотеки:

opencv-python

Для захвата и визуализации видео

socket

Для обмена данными между Pi и ноутбуком

smbus

Для управления ЖК-дисплеем по I2C

csv, os, datetime

Для логирования и работы с файлами

playsound

Для воспроизведения превью мелодии

Шаг 1. Создание пользовательского набора данных

Чтобы наша модель детекции работала в конкретных условиях, не получится полагаться на существующие наборы данных. Большинство общедоступных датасетов либо содержат изображения целых клавиатур, либо обучены для распознавания объектов общего назначения. Нам же нужны были качественные, сфокусированные изображения именно нашей клавиатуры.

Creating a Custom Dataset
Creating a Custom Dataset

Работа начинается с фотографирования нажатий клавиш. Важно охватить все восемь меток: c_pressed, d_pressed, e_pressed, f_pressed, g_pressed, a_pressed, b_pressed и одну для состояния без нажатия. Загружаем снимки в Roboflow и приступаем к их разметке.

Всего вручную с помощью инструмента аннотирования в Roboflow было размечено около 200 изображений. Пришлось позаботиться о том, чтобы включить в выборку руки разных размеров, различные условия освещения и небольшие изменения угла съемки. Такой подход помогает модели лучше обобщать данные и эффективнее работать в реальных условиях. Этот этап подчеркивает фундаментальный принцип прикладного машинного обучения: успех алгоритма напрямую зависит от качества и релевантности исходных данных.

Игровой сервер с криперами и порталом в Незер. Добывайте ресурсы, стройте объекты, исследуйте мир Selectel в Minecraft и получайте призы.

Исследовать →

Шаг 2. Обучение модели

После разметки и загрузки набора данных переходим к обучению модели. Для этого используем веб-интерфейс Roboflow. Сама модель распознавания объектов — YOLOv8. Выбор пал именно на нее из-за оптимального баланса между скоростью и точностью — ключевого фактора для детекции в реальном времени.

Начинаем с базовой аугментации данных: отражение, коррекция яркости и кадрирования. Такие небольшие хитрости дополнительно улучшают способность модели к обобщению, что особенно важно при работе с относительно небольшим набором данных. Модель обучалась на протяжении 100 эпох с использованием встроенных инструментов Roboflow. В ходе процесса отслеживались такие метрики, как mAP@50, precision (точность) и recall (полнота).

Training the Model
Training the Model

Последняя версия v4 нашей модели достигла показателя mAP@50 в 99,5%, при точности 90,4% и полноте 100%. Далее модель экспортировалась в формате Python с помощью inference_sdk от Roboflow и началась ее подготовка для локального запуска на ноутбуке. Поскольку Raspberry Pi не справляется с обработкой YOLOv8, он будет отвечать только за трансляцию видео и управление обратной связью, в то время как ноутбук возьмет на себя все вычисления для распознавания.

Шаг 3. Построение контура обратной связи в реальном времени

Контур обратной связи — сердце всего проекта. Raspberry Pi захватывает видео с USB‑камеры и отправляет кадры на ноутбук через сокет-соединение. Там кадры принимаются и передаются в обученную модель YOLOv8 для анализа. Результат — метка вроде c_pressed — отправляется обратно на Raspberry Pi через второй сокет.

Building the Real-Time Feedback Loop
Building the Real-Time Feedback Loop

Такое разделение задач — яркий пример прагматичного инженерного подхода. Ограниченные вычислительные ресурсы Raspberry Pi не позволяют запускать на нем ресурсоемкую нейросеть. Поэтому создается распределенная система: маломощное устройство на краю сети (edge device) собирает данные, а более производительный компьютер выполняет сложные вычисления. Такая архитектура является миниатюрной моделью многих современных систем интернета вещей (IoT) и периферийных вычислений.

На Pi другой скрипт получает предсказанную метку и сравнивает ее с текущей ожидаемой нотой в мелодии. Для этого используется простой список из 14 нот, составляющих «Twinkle, Twinkle, Little Star». Каждое верное совпадение засчитывается. В конце песни Pi вычисляет процент точности и отображает итоговый результат на ЖК-дисплее.

Шаг 4. Отображение результатов на ЖК-дисплее

Обратная связь на ЖК‑дисплее была необходима, чтобы сделать взаимодействие с тренажером по-настоящему интерактивным и завершенным. Был взят ЖК‑экран размером 16×2 с интерфейсом I2C, подключенный к Raspberry Pi через выводы GPIO.

Displaying Results on an LCD
Displaying Results on an LCD

Для управления экраном пришлось написать собственный класс на Python — LCD.py. Он отвечает за побитовую передачу данных с помощью библиотеки smbus. На первой строке дисплея в реальном времени отображается каждая распознанная клавиша, а на второй — сообщения вроде «Song Finished!» и итоговая оценка точности. Класс включает низкоуровневые функции для отправки символов, строк и специальных команд, таких как очистка экрана или установка курсора. Написание модуля «с нуля» оказалось и увлекательно, и познавательно. Его разработка позволила глубже понять принципы I2C‑адресации и битовых манипуляций.

from smbus import SMBus
import time


class LCD:

    I2C_ADDR = 0x27 
    LCD_CHR = 1      
    LCD_CMD = 0       
    LCD_LINE_1 = 0x80 
    LCD_LINE_2 = 0xC0 
    LCD_BACKLIGHT = 0x08  
    ENABLE = 0b00000100   
    E_DELAY = 0.0005    

    def __init__(self):
        self.bus = SMBus(1)
        self.lcd_init()

    def lcd_init(self):
        self.send_byte(0x33, self.LCD_CMD) 
        self.send_byte(0x32, self.LCD_CMD) 
        self.send_byte(0x06, self.LCD_CMD) 
        self.send_byte(0x0C, self.LCD_CMD) 
        self.send_byte(0x28, self.LCD_CMD) 
        self.send_byte(0x01, self.LCD_CMD) 
        time.sleep(0.05)

    def send_byte(self, bits, mode):
        bits_high = mode | (bits & 0xF0) | self.LCD_BACKLIGHT
        bits_low = mode | ((bits << 4) & 0xF0) | self.LCD_BACKLIGHT
        self.bus.write_byte(self.I2C_ADDR, bits_high)
        self.send_byte_with_e_toggle(bits_high)
        self.bus.write_byte(self.I2C_ADDR, bits_low)
        self.send_byte_with_e_toggle(bits_low)

    def send_byte_with_e_toggle(self, bits):
        time.sleep(self.E_DELAY)
        self.bus.write_byte(self.I2C_ADDR, (bits | self.ENABLE))
        time.sleep(self.E_DELAY)
        self.bus.write_byte(self.I2C_ADDR, (bits & ~self.ENABLE))
        time.sleep(self.E_DELAY)

    def send_instruction(self, instruction):
        self.send_byte(instruction, self.LCD_CMD)

    def send_character(self, char):
        self.send_byte(ord(char), self.LCD_CHR)

    def send_string(self, message, line=LCD_LINE_1):
        self.send_instruction(line)
        for char in message:
            self.send_character(char)

    def clear(self):
        self.send_instruction(0x01)

    def display_on(self):
        self.send_instruction(0x0C)

    def display_off(self):
        self.send_instruction(0x08)

    def cursor_on(self):
        self.send_instruction(0x0E)

    def cursor_off(self):
        self.send_instruction(0x0C)

Шаг 5. Воспроизведение превью мелодии

Чтобы помочь пользователям освежить в памяти мелодию перед началом игры, была добавлена возможность прослушать превью «Twinkle, Twinkle, Little Star». При запуске программа задает вопрос: «Do you want to hear the song first? (y/n)». Если пользователь отвечает утвердительно, с помощью пакета playsoundо для Python воспроизводится короткий mp3-файл. Такой небольшой приятный музыкальный штрих способствует лучшему запоминанию.

Play a Song Preview
Play a Song Preview

Аудиофайл хранится в каталоге assets/ и проигрывается на ноутбуке до начала трансляции видео. Казалось бы, такая небольшая функция, а инструмент стал дружелюбнее и увлекательнее, особенно для начинающих музыкантов.

Шаг 6. Сигнал об окончании с помощью зуммера

Поскольку корпус закрытый и не позволяет видеть внешние светодиоды, для оповещения об окончании сеанса использовался зуммер. Подключаем его к выводу GPIO18 и проигрываем какую‑нибудь незатейливую композицию с помощью ШИМ (PWM). Звуковой сигнал  не просто информирует о завершении песни — весь процесс становится динамичнее. Зуммер срабатывает после обработки всех 14 нот и отображения результатов на дисплее.

Шаг 7. Визуализация предсказаний и инструменты отладки

Окно на стороне ноутбука открывает функция cv2.imshow(). В нем отображается видео с веб-камеры и наложенные результатами распознавания. Также показывается текущая клавиша, которую удалось определить, ограничивающие рамки (bounding boxes) и счетчик кадров в секунду (FPS).

Live Prediction Visuals and Debugging Tools
Live Prediction Visuals and Debugging Tools

Подход показал себя чрезвычайно полезным. Он помог отлаживать низкую частоту кадров и быть уверенным, что модель ведет себя корректно в реальном времени. Кроме того, позволил протестировать производительность алгоритма при слабом освещении, различном положении рук и на разных фонах. Создание таких инструментов визуализации считается признаком зрелого подхода к разработке: программист не просто пишет код, а создает средства для его анализа и проверки.

Шаг 8. Логирование и отслеживание точности

Каждый сеанс игры журналируется в CSV-файл. Строки содержат временну́ю метку, ожидаемую клавишу, распознанную клавишу и отметку о совпадении. Подробная запись чрезвычайно полезна для выявления ошибок модели и отслеживания улучшений от версии к версии.

Logging and Accuracy Tracking
Logging and Accuracy Tracking

Raspberry Pi сохраняет логи в папку logs/ внутри каталога RPi/. Пришлось написать вспомогательные функции для очистки буфера сокета перед каждым сеансом, чтобы избежать «фантомных» предсказаний из предыдущих запусков. Такой систематический сбор данных и внимание к деталям, как очистка буфера, отличают надежный прототип от простого эксперимента.

Шаг 9. Функция повтора и сброс

После завершения последовательности из 14 нот Raspberry Pi вычисляет итоговую точность и выводит ее на ЖК-дисплей. В этот момент зуммер проигрывает свою мелодию, а пользователю предлагается сыграть еще раз. Если ввести «y», сеанс сбрасывается: ЖК-дисплей очищается, буфер сокета опустошается, и все начинается сначала. Так, легко практиковаться несколько раз подряд и отслеживать свой прогресс.

Шаг 10. Структура проекта и GitHub

Кодовая база разделена на папки с понятными названиями:

  • AI/ — содержит скрипт для инференса на ноутбуке и код для превью мелодии;

  • RPi/ — включает класс для LCD, сокет-клиент, логику логирования и скрипт-запускатор run_all_pi.py, который для удобства запускает оба скрипта на стороне Pi параллельно;

  • assets/ — хранит аудиофайл для превью;

  • drafts/ — неиспользуемый экспериментальный код.

Project Structure and GitHub
Project Structure and GitHub

Проект также включает файл README.md с общими инструкциями и feedforward.md с размышлениями о процессе работы. Хорошая организация кода и наличие документации — не менее важная часть, чем сама функционирующая система. Забота о справочных материалах не только показывает уважение к другим разработчикам, но и превращает личный проект в ресурс, которым может воспользоваться сообщество.

Репозиторий: GitHub.

Шаг 11. Проектирование деревянного корпуса

Прежде чем что-либо собирать, хорошо бы убедиться, что камера будет расположена на правильной высоте и под нужным углом. Измерения показали, что оптимальная высота для съемки клавиатуры составляет от 40 до 45 см от основания. С учетом расчетов 3D‑модель корпуса с шиповыми соединениями создали с помощью MakerCase. Затем импортировали дизайн в Inkscape для дальнейшей доработки.

Designing the Wooden Enclosure
Designing the Wooden Enclosure

Добавили точные вырезы для USB‑камеры (4.5x2 см) и ЖК‑дисплея (8x3.6 см), а также внесли несколько творческих дополнений. Нижняя передняя панель была удлинена, чтобы на ней можно было разместить ноты, а верхняя — чтобы было место для приклеивания светодиодной ленты. Эти дополнения сделали конструкцию более функциональной и визуально интересной.

Шаг 12. Сборка деревянного корпуса

Когда дизайн был закончен, мы экспортировали его и вырезали все необходимое на лазерном станке в университете. Материал — листы 4‑миллиметровой мультиплексной фанеры. После того как все детали изготовлены, единственное, что остается, — собрать их с помощью столярного клея. Несколько нарисованных от руки элементов добавили корпусу индивидуальность. Шиповые соединения облегчили сборку, а конечная конструкция получилась прочной и устойчивой.

Building the Wooden Enclosure
Building the Wooden Enclosure

Сбоку есть отверстие для вывода кабелей, а расширенные панели мы использовали точно по назначению: одна — для размещения нот во время игры, другая — для крепления светодиодной ленты, освещающей эти ноты. Современные технологии действительно объединяют проектирование, код и работу с материалами в единый творческий процесс.

Заключительные мысли и выводы

Этот проект помог мне вырасти и как разработчику, и как мейкеру. Я научилась собирать и размечать наборы данных, обучать модель, отлаживать системы реального времени, использовать побитовые операции для управления ЖК-дисплеем и создавать физический прототип.

Final Thoughts
Final Thoughts

Система не безупречна — иногда быстрые или слишком мягкие нажатия остаются незамеченными, но она надежно работает при игре в стабильном темпе. Самое главное — это увлекательный и мотивирующий способ практиковаться в игре на фортепиано и исследовать пересечение музыки, искусственного интеллекта ПО и «железо». Честный взгляд на недостатки не умаляет достигнутого, а наоборот, подчеркивает главную ценность — полученный опыт и знания.

Я невероятно горжусь этим первым самостоятельным проектом и с нетерпением жду возможности учиться дальше.

Комментарии (5)


  1. Tyusha
    19.07.2025 13:08

    Как известно, научиться играть на фортепиано очень просто — надо всего лишь нажимать нужные клавиши в нужное время.


  1. Prohard
    19.07.2025 13:08

    Так кто автор-то? Ада Лопес или Зорин? И страну, где этот университет, тоже надо писать. А так на манию величия смахивает.


    1. oneastok Автор
      19.07.2025 13:08

      Данные об авторе есть (вверху, второе предложение после приветствия):

      • автор оригинального текста: Ада Ло́пес;

      • место учебы автора: Факультет «Креативные технологии и ИИ» в Университете Хауэст в Кортрейке.

      Ссылка на оригинальную публикацию (под названием статьи): https://www.instructables.com/AI-Powered-Piano-Trainer-Learn-Songs-With-Real-Tim/


  1. Johan_Palych
    19.07.2025 13:08

    Из перевода непонятно, что за железо использовалось. Открыл первоисточник.
    Raspberry Pi 5 - 8GB
    Logitech C270 - 720p HD Webcam - 3MP - Grijs
    Freenove Projects Kit for Raspberry Pi 5 / 400 / 4B / 3B+ / 3B
    You can check out my GitHub here - уже убрали


    1. oneastok Автор
      19.07.2025 13:08

      Про GitHub автора действительно пропала строчка. Искренне благодарю за внимательность! Вернул пропажу в конец 10‑го шага.