Назад

Милый настольный робот: лицо, которое слушает, на ESP-VoCat с эмулятором в браузере

Большинство настольных гаджетов просто показывают информацию. Мне хотелось такого, который реагирует на людей: робота, который смотрит на вас, когда вы говорите, отвечает вам и делает что-то забавное, когда рядом никого нет. Для этого у Espressif ESP-VoCat v1.2 есть всё необходимое аппаратное обеспечение. В нём объединены SoC ESP32-S3, круглый сенсорный экран 360×360, два микрофона, динамик и аккумулятор в корпусе с кошачьими ушками.

VoCat Eyes — это лицо, которое я написал для него, и первый робот из серии Cute Desktop Robot. Его основные возможности:

Прошивка, эмулятор и инструменты имеют открытый исходный код в репозитории cute-desktop-robot.

Робот VoCat Eyes в 3D-виде эмулятора: синий корпус ESP-VoCat с кошачьими ушками и лицом робота на круглом экране

На изображении выше показан 3D-вид эмулятора: модель корпуса, опубликованная Espressif, с живым экраном эмулируемого робота внутри.

А вот настоящий робот на столе, работающий на той же прошивке:

Робот на реальном аппаратном обеспечении: посмотрите на YouTube.

Попробуйте робота в браузере

Эмулятор ниже запускает C++-код робота, скомпилированный в WebAssembly: тот же движок лица, конечный автомат и экраны, что работают на ESP32-S3. Заменён только аппаратный слой — на симулированные компоненты. Это не требует ни аккаунта, ни установки. Только голосовая беседа требует настоящего робота, поскольку у эмулятора нет сети.

Если окно слишком маленькое, откройте эмулятор в полном окне.

Окно состоит из трёх частей. Слева — робот в 3D, под ним — конечный автомат, а справа — панель с графом эмоций и датчиками. Чтобы попробовать:

  1. Коснитесь экрана робота. Кликните один раз по круглому экрану 3D-робота. Робот проиграет следующую анимацию.
  2. Откройте настройки. Нажмите и удерживайте экран, пока не появятся часы и индикатор батареи, затем кликните один раз по часам. Откроется меню настроек. Кликните по левой половине строки, чтобы уменьшить значение, и по правой — чтобы увеличить. Нажмите и удерживайте, чтобы закрыть меню.
  3. Говорите с одной стороны. На правой панели в разделе SENSORS перетащите ползунок рядом с from на нужный угол, затем нажмите talk [V]. Фиолетовая точка на полу покажет, откуда исходит симулированный голос, и глаза робота повернутся к ней.
  4. Наблюдайте за конечным автоматом. Каждый раз, когда робот меняет активность, подсвечивается соответствующий блок в конечном автомате, а полосы внизу показывают таймеры, которые могут изменить её следующими.
  5. Поверните робота. Перетаскивайте в любом месте за пределами экрана, чтобы вращать 3D-вид, и используйте колесо мыши для масштабирования. Нажмите M, чтобы переключиться на плоский вид экрана.

Клавиатура тоже работает после клика по эмулятору: 1–9 показывают выражения, Y — кивок, U — покачивание головой, а H — речевой пузырь. Касание и настройки работают так же и на настоящем роботе.

На следующем скриншоте показано всё окно эмулятора, как оно выглядит на настольном ПК:

Полный настольный эмулятор: робот в 3D, граф эмоций, датчики с ползунками и конечный автомат с таймерами

Аппаратное обеспечение

ESP-VoCat поставляется в собранном виде — с корпусом, аккумулятором и экраном на месте. Руководство пользователя ESP-VoCat подробно описывает плату. Всю работу выполняет один модуль ESP32-S3. У него 16 МБ флеш-памяти и 16 МБ PSRAM (внешней оперативной памяти). Именно PSRAM делает возможным лицо 360×360: один полный кадр в 16-битном цвете занимает 253 КБ — больше, чем может вместить внутренняя память SoC.

На следующей схеме показаны компоненты, которые использует прошивка, и шина, соединяющая каждый из них с ESP32-S3:

Блок-схема: ESP32-S3 управляет круглым ЖК-дисплеем ST77916 по QSPI, читает данные сенсорного контроллера CST816S и топливомера BQ27220 по I2C, получает сигнал с микрофонов через ES7210 и воспроизводит звук через ES8311 и его усилитель по I2S TDM, а также общается с Gemini Live по Wi-Fi

Два компонента потребовали дополнительной работы:

Как робот рисует своё лицо

Робот перерисовывает своё лицо каждые 33 мс — примерно 30 раз в секунду. Схема ниже прослеживает один из таких кадров от датчиков до экрана:

Конвейер кадра: датчики формируют SensorState для Brain, Brain выбирает выражение для FaceEngine, FaceEngine рисует фигуры в Canvas, Canvas находит изменённые плитки 24 пикселя и VocatLcd отправляет только их по QSPI. Задача Listener передаёт голос, удары и слово активации в датчики, а беседа передаёт set_face и play_act в Brain

Каждый блок на схеме — это один модуль прошивки:

Все выражения и анимации

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

Выражения

Спокойные и задумчивые (16): обычное, любопытное, сосредоточенное, скептическое, скучающее, растерянное и слушающее, среди прочих.

Радостные (15): от лёгкой улыбки до смеха, влюблённости и восхищения.

Грустные (8): обеспокоенное, умоляющее, плачущее, одинокое и виноватое, среди прочих.

Злые (6): от раздражённого до взбешённого, а также отвращение.

Удивлённые и настороженные (7): удивлённое, испуганное, головокружение, нокаут и гипноз, среди прочих.

Сонные (4): сонное, зевающее, спящее и уставшее.

Анимации

Анимации, называемые в коде «актами» (acts), — это короткие сцены, многие с объектом, нарисованным рядом с глазами: дождевая туча, чашка кофе, экран радара. Мозг проигрывает их, когда робот бездействует, а касание экрана запускает следующую.

Язык тела (9): кивок, покачивание головой, закатывание глаз, чихание.

Экранные эффекты (5): глитч, перезагрузка, линия сканера, падающий код.

С объектами (18): сердечки, дождь, фейерверк, пузыри, мяч, танец.

Повседневные вещи (9): книга, наушники, кофе, ракета, снег.

Игры и эффекты круглого экрана (6): Pac-Man, Pong, радуга, радар, аквариум.

Те же сетки в виде анимированных GIF-файлов есть в README репозитория.

Как робот разговаривает

Для беседы используется Gemini Live API, которое принимает поток аудио и отвечает потоком аудио через одно WebSocket-соединение. На момент написания статьи робот использует модель gemini-2.5-flash-native-audio. На схеме ниже показан путь аудио:

Голосовой конвейер: микрофоны ES7210 подают звук в задачу Listener с частотой 16 кГц, задача GeminiLive отправляет 100-мс фрагменты в Gemini Live API и получает аудио с частотой 24 кГц, которое передискретизируется для Player и динамика. Вызовы инструментов направляются в Talk, который меняет лицо. Player отключает Listener на время воспроизведения

Робот отправляет звук с одного микрофона как PCM-аудио с частотой 16 кГц, фрагментами по 100 мс. Gemini отвечает речью с частотой 24 кГц, которую робот преобразует в 16 кГц и воспроизводит.

Беседа ведётся в полудуплексном режиме: робот либо слушает, либо говорит, но никогда не делает это одновременно. Динамик находится в нескольких сантиметрах от микрофонов, поэтому робот отключает поток с микрофона, пока воспроизводит ответ. Иначе Gemini услышал бы собственный голос. Gemini на своей стороне решает, когда человек закончил говорить.

Gemini также получает четыре инструмента — функции, которые он может вызывать во время беседы: set_face, play_act, look и show_text. Когда разговор становится грустным, Gemini может вызвать set_face("sad"). Когда вы просите робота согласиться, он может вызвать play_act("yes"), и робот кивает. Каждая сессия начинается с короткого текстового сообщения, содержащего местное время и дату, чтобы Gemini мог отвечать на вопросы о них.

Три проблемы заняли большую часть времени разработки:

  1. Защищённое соединение. Робот подключается к Gemini по TLS. Функция setInsecure() библиотеки ArduinoWebsockets не действует на ESP32-S3, поэтому TLS-рукопожатие завершалось неудачей. Теперь прошивка включает два корневых сертификата Google (GTS Root R1 и R4, действительны до 2036 года). Она также ждёт синхронизации часов по NTP, поскольку для проверки сертификата нужна текущая дата.
  2. Внутренняя RAM. Активационное слово «Hi ESP» работает на ESP-SR, библиотеке распознавания речи от Espressif, которая занимает около 100 КБ внутренней RAM. Библиотеке TLS нужна внутренняя RAM для рукопожатия, и первые попытки завершались ошибкой SSL - Memory allocation failed. Теперь робот останавливает ESP-SR при начале беседы, что освобождает 107 КБ, и загружает её снова, когда беседа заканчивается. Загрузка ESP-SR занимает около 1,2 секунды.
  3. Отсутствующий поток с микрофона. В течение одного дня робот приветствовал пользователя, а затем игнорировал всё последующее. Его глаза по-прежнему следили за моим голосом, так что микрофоны явно работали. Причина была в том, что прошивка так и не включала аудиопоток к Gemini. Теперь последовательная консоль выводит транскрипцию того, что услышал Gemini, так что та же проблема была бы видна сразу.

Для использования Gemini нужен ключ API из Google AI Studio, а API оплачивается по факту использования согласно ценам на Gemini API от Google. Ключ хранится в локальном файле secrets.h, который никогда не попадает в репозиторий.

Настройки на экране робота

После настройки роботу не нужен компьютер. Долгое нажатие на экран показывает часы, уровень заряда батареи и состояние Wi-Fi. Нажатие на часы открывает настройки, где можно изменить:

Строка Wi-Fi открывает список сетей в зоне действия и экранную клавиатуру с заглавными буквами, цифрами и символами. ESP32-S3 не может сканировать сети, пока подключается к одной из них, поэтому прошивка приостанавливает попытки подключения во время сканирования и возобновляет их после. Если пароль неверный, экран часов показывает это как причину. ESP32-S3 поддерживает только сети 2,4 ГГц.

Одна кодовая база для робота, ПК и браузера

Код робота не обращается напрямую к драйверам дисплея, сенсора или аудио. Лицо, Brain и экраны проходят через тонкий слой аппаратного обеспечения в src/hw и src/audio. Эмулятор заменяет этот слой, поэтому остальной код собирается без изменений для трёх целей:

Общий C++ в src/ компилируется тремя способами: arduino-cli создаёт прошивку для ESP32-S3, g++ с заглушками заголовков Arduino создаёт настольный эмулятор на raylib, а em++ создаёт тот же эмулятор для браузера

В эмуляторе небольшие заменяющие версии Arduino.h, Wire.h и SPI.h пишут в структуру, которая замещает аппаратное обеспечение. Она хранит память дисплея, регистры контроллера сенсора и показания батареи. Окно эмулятора, написанное с использованием графической библиотеки raylib, отрисовывает эту структуру и позволяет изменять значения датчиков.

Браузерная версия — это та же программа эмулятора, скомпилированная с помощью Emscripten, который превращает C++ в WebAssembly. Это стало возможным благодаря трём изменениям:

  1. raylib для веба. raylib собирается с Emscripten в статическую библиотеку, которая рисует через WebGL.
  2. Один кадр за вызов. Настольная версия работала в бесконечном цикле. Этот цикл стал функцией, которая рисует один кадр. Настольная версия вызывает её в цикле, а браузер вызывает её один раз за обновление экрана.
  3. Фиксированный размер окна. В браузере эмулятор рисует в canvas размером 1380×900, а страница масштабирует canvas по размеру. Изменяемый размер canvas нарушал соответствие между координатами мыши и canvas.

3D-корпус взят у Espressif. ESP-College опубликовал корпус ESP-VoCat (EchoEar) в виде STL-файлов на OSHWHub под лицензией GPL-3.0. Короткий скрипт на Python преобразует STL-файлы в один файл 3D-модели и снижает его детализацию, чтобы браузер загружал его быстро. Эмулятор рисует живой экран на диске в круглом окне корпуса. Когда вы кликаете по 3D-экрану, эмулятор находит точку клика на этом диске и превращает её в касание в соответствующем пикселе.

Соберите своего

Чтобы собрать VoCat Eyes, вам нужна плата ESP-VoCat v1.2 и, на момент написания статьи, версия 3.3 ядра Arduino для ESP32. В README VoCat Eyes перечислены библиотеки и точные команды. Кратко:

  1. Скопируйте secrets.example.h в secrets.h и добавьте свой ключ API Gemini. Там же можно указать сеть Wi-Fi или задать её позже на роботе.
  2. Скомпилируйте скетч со схемой разделов esp_sr_16 и файлом partitions.csv в папке скетча. Такая разметка даёт приложению 6 МБ флеш-памяти.
  3. Загрузите прошивку вместе с srmodels.bin — файлом модели для активационного слова «Hi ESP».
  4. На роботе нажмите и удерживайте экран, коснитесь часов, выберите Wi-Fi, выберите свою сеть и введите её пароль.
  5. Скажите «Hi ESP» или коснитесь маленького микрофона внизу экрана и начинайте говорить.

Если вы разрабатываете прошивку для ESP32-S3 или других плат, Velxio может смоделировать её в браузере, прежде чем вы прошьёте какое-либо оборудование.


Поделиться:
David Montero

Автор

David Montero

Creator of Velxio, the open-source circuit and Arduino simulator.

GitHub velxio.dev

Похожие статьи


Предыдущая статья
Семь хэллоуинских проектов, которые можно собрать и запустить в браузере
Следующая статья
Дашборд использования Claude на M5Stack Basic — лимиты плана и расходы API на вашем столе