Лягушка, ты меня узнала?
Посмотреть “Часть 1” можно здесь.

В прошлой части я радовался, что Лягушка наконец-то смотрит на человека и поворачивает за ним голову. А потом дочь показала ей предмет и спросила: «Что это такое?» Лягушка ответила, что смотреть не умеет.
Камера сидит у неё прямо в центре банта. Примерно как если бы я надел очки и сказал: «Извини, у меня нет глаз». Лягушка уже находила человека перед собой, но вещь у него в руках для неё не существовала.
В общем, мало приделать камеру к голове. Надо ещё объяснить программе, зачем она там. В этой части будут фото по голосовой команде, знакомство с собственным создателем, память о разговорах, попытка выучить стих и лицо, которое тормозило сильнее мозгов. И всё это нужно подружить с уже имеющимся железом и уместить в нынешнем корпусе: свободного места там почти нет. Поэтому в этой версии стараюсь выжать больше из того, что уже установлено.
Камера есть. А как показать ей предмет?
Программа вынимала из кадра только координаты лица. В обычный вопрос сам снимок, естественно, не попадал. Я отделил просьбы вроде «сфоткай» и «посмотри на задачу» от обычного разговора: тут Лягушке надо именно сделать кадр и посмотреть, что на нём.
Отправлять первый попавшийся кадр было бы проще, но результат легко представить: ребёнок двинул рукой, автофокус ещё ищет положение, а Лягушка по итогу уверенно рассуждает о размазанном пятне. Ещё и сравнит с каким-нибудь блюр-артом. Художественная ценность растёт, пользы - не очень. Я сделал предпросмотр. Она просит немного подержать предмет ровно, при необходимости советует отодвинуть его или добавить света. Я вижу снимок и могу сказать «кадр подходит» либо переснять. Если к картинке нужен текст, его можно добавить и отправить вместе с фото.

Дочь показывает рацию и спрашивает: «Что это?» Лягушка открывает предпросмотр снимка. Камера наконец пригодилась не только для поиска лица.
На первой живой пробе всё это благополучно сломалось. Кнопка «Кадр подходит» не нажималась, голосовую команду Лягушка тоже пропускала. Я сначала даже не понял, слышит ли она меня или только занята собственным объяснением. Разобрал порядок подсказки и ожидания ответа; потом и кнопка, и голос заработали.
Одного кадра тоже не всегда хватает: задание может быть на одной странице, а пример к нему - на другой. Добавил «Ещё фото»: можно собрать до четырёх снимков, пересмотреть их по миниатюрам и отправить с одним вопросом. Пересъёмка заменяет только выбранный кадр. Пока проверил этот путь программно и настоящим запросом с двумя тестовыми картинками; съёмку нескольких страниц в руках ещё предстоит испытать.
Но Лягушка тут же показала другую крайность: на простой вопрос про снимок она вслух перечисляла почти все команды окна. Ребёнок стоит с предметом в руке, а робот читает ему меню. Оставил в итоге короткую подсказку только о следующем действии, а полную справку - на экране.
Чуть позже дочь захотела сфотографировать предмет перед камерой и распечатать снимок. Я обрезал кадр, чтобы на листе остался сам предмет, а не половина стола. Лягушка предложила сказать «печатай». Я произнёс “печатай”, а в ответ - ничего. Нажал кнопку - из принтера вышла фотография по центру листа. Хорошая демонстрация того, как голосовой интерфейс сам предлагает команду и тут же делает вид, что не знаком с ней.
Теперь оставалось пройти весь путь голосом - от обрезанного снимка до бумаги. Поправил обработку команды: на фотографии первое «печатай» открывает предпросмотр листа, второе подтверждает отправку в принтер. В меню добавил выбор цвета и размера картинки на странице. Всё заработало. Снимок наконец добрался до бумаги без помощи пальца.

Обрезаем снимок с рисунком прямо на экране Лягушки. Справа сразу виден результат. Фон и лицо над рисунком размыты для публикации.

Рисунок на экране и его бумажная копия. Ради этого и понадобилась кнопка «Печатать».
Лягушка, ты меня вообще слышишь?
Однажды она начала длинный рассказ. Я дождался, пока голос действительно зазвучит из колонки, и сказал поверх: «Лягушка, сколько будет два плюс три?» Она продолжила как ни в чём не бывало. Повторил громче - замолчала и ответила: «Пять».
Так, стоп. Значит, вопрос она понимает. Почему же первый раз был мимо? Я даже подумал, что на первое «Лягушка» она только включает прослушивание, а со второго уже записывает вопрос.
Сделал отдельный экран с кнопкой «Начать пробу перебивания». Теперь можно было самому нажать кнопку, дождаться звука и произнести одну и ту же фразу во время её речи.
Я стоял примерно в полуметре от микрофона. Динамик находился всего в тридцати сантиметрах от него. Убавил громкость колонки и повторил опыт - Лягушка остановилась с первого раза и ответила на вопрос. Ну конечно. Пока она говорила громко, микрофону приходилось разбирать мой голос на фоне её собственного.
Оставил более тихую громкость для обычного разговора. Иначе получался странный собеседник: пока молчит - слышит тебя нормально, а стоит самой разговориться - приходится ждать конца рассказа или перекрикивать её.
Но дело оказалось не только в колонке. Я смотрел прямо на Лягушку и сразу спрашивал: «Как дела?» Она замечала меня, но первой успевала произнести приветствие. Пока вежливо здоровалась, мой вопрос улетал мимо. Поменял приоритет: теперь она сначала слушает. Если я молчу, уже может поздороваться - в зависимости от настройки в меню.
С английским вскрылось ещё одно ограничение. Когда я спрашивал, как переводится impossible, русский распознаватель пытался записать английское слово русскими буквами. Для таких вопросов добавил отдельный режим Whisper tiny: он распознаёт речь на нескольких языках, а выбрать его можно в настройках. На моих пробах английское слово и предложение он разобрал лучше Vosk. С русским результат зависел от фразы: на одних мне больше нравился Whisper, на других точнее был Vosk. Смешанная речь всё ещё давала ошибки.
По умолчанию я пока оставил русский Vosk. Автоматического переключения между двумя распознавателями нет. Если выбрать Whisper, он разбирает сам вопрос, а имя робота и короткие команды вроде «стоп» по-прежнему ловит Vosk. То есть попробовать английский уже можно, но обещать, что Лягушка одинаково хорошо слышит любые языки, рано. Следующая важная проверка - голос дочери, обычный разговор и задержка на Pi.
Уравнение решила. А экран зачем?
Я попросил решить «два икс плюс три равно одиннадцать». Лягушка объяснила ответ голосом. Посмотрел на дисплей - там ни одного числа. Если нужна помощь с уравнением, одного «икс равен четырём» мало. Хочется разобрать решение: что перенесли, почему поменялся знак и откуда вообще взялась четвёрка.
Я вывел решение на основной экран крупными шагами. Лягушка объясняет первый шаг, а дочь может сказать «дальше», вернуться назад или попросить объяснить именно то место, где запуталась. Экран и файл для Word или PDF я собираю из одних данных: не хватало ещё, чтобы вслух она сказала «четыре», а на бумаге оказалось другое число. Так же можно открыть и подготовить к печати доклад или обычный текст.
Начинать можно и с фотографии непонятного задания. Показываешь страницу Лягушке, добавляешь просьбу объяснить нужный пункт, а ответ можно вывести на экран и распечатать. Например: «Объясни задание 4 по английскому, используя примеры из заданий 2 и 5». Здесь я хочу развивать именно разбор: помочь понять условие, задать наводящий вопрос, показать похожий пример. Сфотографировать всю домашку и получить готовую тетрадь - совсем не та помощь, ради которой я это затевал. При этом модель может ошибиться и в чтении снимка, и в объяснении; экран позволяет хотя бы увидеть, какое условие она разбирает.

Экран программной проверки на простом уравнении. Числа и формулы здесь обычный текст, не нарисованные нейросетью символы. На этом же материале строятся Word и PDF.
В настройках появился мой сетевой Epson L366. Печать я подключил через CUPS - систему, которая в Linux передаёт задания принтеру и следит за их очередью. Отправил тестовую страницу: русское «ТЕСТ», пример 3+2−4=1 и формулу площади круга. Кириллица и знаки напечатались как надо. До этого я видел файлы и статусы заданий, а теперь мог проверить результат на бумаге. Правда, после слов «напечатай» я не хочу отправлять что попало сразу: сначала должен быть готов материал и предпросмотр, а печать требует подтверждения.
А потом я попросил: «Покажи рецепт борща» - и снова получил только устный ответ. Стало ясно, что одной страницы с решениями мало. Программа должна различать «расскажи», «покажи», «сохрани» и «напечатай»: это разные действия. После правки рецепт открывается на экране, а просьба напечатать его ведёт к предпросмотру. Команды «выйди», «закрой» и «верни лицо» возвращают обычное лицо, если микрофон работает и робот не на паузе.
С рецептом ещё можно было обойтись тем, что модель уже знает. А если ребёнок попросит найти что-то новое? Я не хотел верить ответу только потому, что Лягушка произнесла его уверенным голосом. По явной просьбе «найди в интернете» она обращается к подключённому поиску DeepSeek и сохраняет ответ вместе с источниками. Если сказать «найди и покажи», материал открывается на экране. Для печати я собираю короткий текст, а не отправляю в принтер целую веб-страницу с рекламой. Фотографии с найденных сайтов пока не подтягиваются: текст с источниками и собственные снимки с камеры - два отдельных сценария.
Для локальной модели подготовил отдельный поиск через SearXNG, но сервер ещё не выбрал. Погоду Лягушка читает со страницы Яндекса по заданному городу. Пока я проверил код и сетевые запросы; как дочь будет спрашивать у неё погоду и пользоваться поиском, ещё предстоит увидеть.
С именем вылезла смешная подстава. Я уже вынес его в настройки, но на самом роботе сменить не смог: на большом мониторе ПК набирал с обычной клавиатуры, а на маленьком экране поле есть - клавиатуры нет. Как дочери вписать новое имя? А пароль Wi-Fi? Пришлось добавить экранную клавиатуру. Вкладок в настройках стало много, и пришлось разложить их в два ряда. Там же появился поиск сетей Wi-Fi. Стенд пока сидит на Ethernet; полный опыт «убрал провод, ввёл пароль на экране и перезагрузил Pi» мне ещё предстоит пройти.
Зарождение памяти Лягушки. «Создатель» - это тот, кто в настройках?
До этого лицо перед камерой означало для программы примерно следующее: «здесь человек, смотри сюда». Для общения этого мало. Я решил научить Лягушку различать знакомых людей и хранить для каждого свою карточку и недавние разговоры. Чтобы после перезапуска не знакомиться заново, а папины обсуждения не продолжались вдруг с дочерью или её подругой.
Разделил задачу на две части. Сначала - профили: имя, как обращаться к человеку, что о нём стоит помнить и история разговора. Затем - узнавание лица, которое связывает человека перед камерой с нужным профилем. Сами сведения лежат локально в SQLite, небольшой базе данных на Pi. Нейросеть от этого не переобучается: программа передаёт ей подходящую карточку и часть разговора вместе с новым вопросом.
На первой проверке я записал своё лицо под именем «Создатель» и спросил: «Ты знаешь, кто я?» Лягушка ответила примерно так: «Я знаю, что в настройках выбран профиль Создатель. По лицу я тебя не распознала. Профиль выбирается вручную».
Вот спасибо. Ещё и «профил» вместо «профиль» произнесла. В другой раз получился «Создател». Персонаж с памятью задумывался, а заговорило руководство пользователя.
В этой проверке проявилась ошибка в связи между настройками и ответом: выбранная в меню карточка ещё не означает, что перед камерой её владелец. Я разделил эти состояния. Теперь сказать «я тебя узнала» Лягушка может после нескольких подтверждений от камеры. Открыть папин профиль для редактирования и узнать папу в комнате - разные события.

Окно знакомства из настоящего интерфейса. Для статьи открыл его с вымышленным профилем и пустым предпросмотром. Сначала подтверждение и отсчёт, потом запись лица по подсказкам; настоящих лиц здесь нет.
Поначалу Лягушка узнавала меня только вблизи, примерно с 35 сантиметров. Шаг назад - имя исчезало. В кадре появлялись двое - становилось ещё хуже. Быстро найти лицо, чтобы повернуть голову, и уверенно назвать человека оказались разными задачами. Для узнавания я стал брать более подробный кадр: MediaPipe отмечает лица, SFace сравнивает их с сохранёнными. Такая проверка требует больше вычислений и выполняется реже, чем обновление взгляда и поворот головы. Просто снизить порог совпадения я не захотел: пусть лучше не назовёт имя, чем уверенно перепутает людей.
Тут же возник вопрос: «Лягушка, ты нас узнала?» Я добавил узнавание до четырёх человек в одном кадре. Лягушка может перечислить тех, кого действительно узнала, и сказать, что рядом кто-то незнакомый. Но сами лица в кадре не говорят, кто именно задал вопрос. Поэтому при группе я не подставляю чью-то личную историю в ответ.
Чтобы камера не замирала, Pi проверяет лица по два за проход, а остальные получают свою очередь. Обработку четырёх лиц и ответы про всю группу проверил программно. Четверых одновременно перед настоящей камерой я ещё не собрал: эту живую проверку пока не считаю пройденной.
Меня Лягушка начала встречать словами вроде «Я тебя узнала, ты Создатель». Забавно первый раз, но при каждом взгляде быстро надоест. Для автоматического приветствия сделал интервал без общения: 30 минут, 60 минут, 12 часов или своё время; по умолчанию - 60 минут. На сказанное мной «привет» она отвечает сразу, этот таймер относится только к её собственной инициативе.

Так выглядят настройки на размере экрана 800×480. Вкладки поместились в два ряда, а интервал приветствия можно изменить без правки кода. Это настоящее окно приложения с вымышленными данными, снятое на отдельном виртуальном экране.
С потерей лица тоже пришлось определить понятное правило. Человек спросил и наклонился к тетради - Лягушка продолжает начатый ответ. Но если следующий вопрос пришёл, когда она уже не знает, кто перед ней, разговор продолжается без личных сведений предыдущего собеседника. Она всё ещё может объяснить уравнение; просто не должна рассказывать гостю то, что обсуждала с папой.
Что остаётся после перезапуска
Для памяти я пока выбрал простой и проверяемый вариант. Имя, обращение, внесённые вручную сведения и недавняя история переживают перезапуск. У каждого профиля они свои. В настройках можно посмотреть разговор, удалить запись, очистить историю или совсем выключить её сохранение.

Карточка из работающего меню; окно увеличено по высоте, чтобы показать поля вместе. Имя и сведения специально выдуманы для статьи. Поле «Что помнить» заполняется вручную, а история разговора сохраняется отдельно.
Бесконечного дневника я не делал: на профиль остаются до 20 последних сообщений, а в запрос попадает ещё меньший фрагмент. Иначе старый доклад займёт весь доступный контекст. Новые факты из разговоров программа сама в карточку не выписывает. Если Лягушка учла что-то из недавнего диалога, это ещё не значит, что она вспомнит об этом через полгода.
Пока мне нужно наблюдать, какие реплики сохраняются у каждого человека и что из них попадает в ответ. Поэтому сделал просмотр и удаление истории, а сведения в карточке можно поправить самому. Сейчас Лягушка пользуется этой карточкой и недавним разговором - именно это я называю её памятью. Поиска по смыслу среди всех давних бесед ещё нет.
Автоматическое сжатие старых разговоров в короткую сводку я тоже пока не добавлял. Дополнительный запрос к модели займёт время, но дело не только в скорости. При пересказе можно потерять важное «не», смешать предположение с фактом или превратить разовое «сегодня мне грустно» в постоянную характеристику человека. А если потом пересказывать уже пересказ, ошибка будет путешествовать вместе с памятью.
Сначала нужно определить, какие сведения сохранять, откуда они взялись, как их обновлять и исправлять. Ограниченная история сейчас понятнее, чем автоматически составленное досье, которое я ещё не научился нормально проверять.
Характер тоже можно настроить
Память отвечает на вопрос «с кем я говорю и что мы обсуждали», а манера ответа задаётся отдельно. В меню есть два режима: «Обычный помощник» и «Детский (безопасное общение)». Возраст можно указать вручную и включить адаптацию объяснений. Угадывать его по лицу или голосу я не стал.

Режим общения и адаптация под возраст во вкладке «Ответы». Окно увеличено для читаемости; раскрыт выбор двух имеющихся режимов.
Там же можно изменить промпт - инструкцию о характере и правилах ответа. Например, попросить сначала уточнять, какое место в задаче непонятно, и давать подсказку вместо немедленной выдачи решения. Это способ направить разговор, а не гарантия, что модель никогда не ошибётся.

Редактор промпта в увеличенном окне приложения, с исходной инструкцией приложения, без личного текста. Можно изменить её или вернуть исходную.
Здесь есть важная граница: промпт и режим общения пока общие для робота, а сведения и история - отдельные для каждого человека. Собственную инструкцию к каждому профилю я ещё не привязывал. Так что обещать «с папой она автоматически одна, с дочерью совсем другая» пока было бы преждевременно.
Брови есть. Читать мысли пока не умеет
С узнаваемым лицом захотелось сделать ещё одну вещь: чтобы Лягушка немного повторяла выражение собеседника. Улыбнулся - она может улыбнуться и спросить про настроение. Нахмурился - тоже меняет брови. Звучит как мелочь, а персонаж сразу кажется внимательнее.
На мне нахмуривание срабатывало, на дочери - нет. Универсальный детектор папиных бровей, поздравляю. После отдельной пробы с ребёнком поправил порог. Потом выяснилось, что ждать несколько секунд до следующей реакции тоже странно: дочь уже расслабила лицо, а робот ещё переживает по расписанию. Сделал проще: нахмуренность держится, пока её видно, расслабление снимает выражение, следующее нахмуривание снова работает. Это мы уже проверили вживую.
При этом я не хочу писать, что робот «понимает эмоции ребёнка». Он замечает конкретные признаки лица. Нахмуренные брови вполне могут означать не грусть, а попытку решить пример. Поэтому здесь уместнее тихо повторить мимику или осторожно спросить, чем уверенно объявить человеку его настроение.
Аня решает, с кем знакомить Лягушку
Я представил дочь с подругой перед камерой. Подруга встала ближе - робот может переключиться на неё, и вся презентация пойдёт не по плану. Для дочери важно, что это её робот, а не конкурс «кто первый попал в объектив». Я добавил отметку основного собеседника; если детей в семье несколько, таких отметок может быть несколько. Увидев незнакомого рядом с основным человеком, Лягушка может предложить познакомиться. Если дома гости и знакомиться сейчас не хочется, такие предложения выключаются галочкой.
Кто-то заходит в гости на пару часов, а кого-то хочется запомнить надолго. Поэтому при знакомстве можно выбрать срок: шесть часов, двенадцать, день, своё время или навсегда. Имя и срок надо подтвердить. Добавил и ручную запись через меню - не всем хочется устраивать церемонию знакомства перед камерой.
Там же добавил возможность выбрать, с кем Лягушке разрешено разговаривать: со всеми, только с основными собеседниками или с уже знакомыми. Мне нравится, что владелец сам определяет этот круг, а не просто смотрит на список распознанных лиц.
Свой профиль я проверил. А вот Аня с подругой этот сценарий ещё не испытывала. Пока не услышу, как Лягушка приглашает их познакомиться в обычном разговоре, не пойму, получилось ли то, ради чего я всё это добавлял.
Ещё я попробовал узнавание по голосу на себе. Сначала запись упрямо отвечала «мало речи», хотя я говорил. Пришлось поправить измерение речевого участка, записать образцы заново и пройти сравнение своего и другого голоса. В этой пробе система их различила. Автоматическое узнавание по голосу оставил опциональным; голос дочери ещё нужно записать и проверить отдельно. Да и не надо делать из голоса волшебный пароль: это подсказка, с кем разговаривает домашний робот, а не защита от записи с телефона.
Спокойной ночи. Без лекции о том, как полезен сон
Когда робот начал узнавать людей, пришлось научить его противоположному - оставлять людей в покое. Особенно ночью. Мне не хотелось говорить «Лягушка, спи» и в ответ слушать длинное пожелание добрых снов, пока ребёнок уже засыпает. Команда сна должна просто остановить речь и привод, затем погасить экран или оставить на нём тусклое спящее лицо. Без последнего слова за Лягушкой.
Так и сделал. Можно выбрать время сна и подъёма, а можно отправить её спать голосом. Одно случайное «Лягушка» ночью не будит робота. Нужна явная просьба «просыпайся», «проснись», «доброе утро» или удержание экрана около трёх секунд.
С моим голосом «просыпайся» сработало. «Проснись» сначала не пробилось несколько раз, потом получилось сразу. А дочь однажды сказала «Лягушка, доброе утро», и та продолжила спать. Нет, я не программировал правило «будить разрешено только папе». Значит, эту команду ещё надо отладить именно на её голосе.

Программный кадр спящего лица. В настройках можно вместо него полностью гасить экран. Сам Pi при таком сне продолжает работать: это не выключение питания.
Почему она так долго думает?
Задумчивое лицо понадобилось, чтобы показать ожидание ответа. После вопроса Лягушка порой молчала так долго, что ребёнок успевал позвать её ещё раз. Я-то знал, что она не зависла: в это время Vosk заканчивал фразу, модель готовила ответ, Piper - речь. А ребёнок видел только молчание.
Разобрал задержку по этапам. Сначала Vosk определял конец фразы, потом моя программа выдерживала ещё одну паузу. Часть тишины к этому моменту уже прошла. Я стал учитывать её, а не складывать ожидания одно поверх другого. Полностью убрать паузу нельзя: ребёнок может остановиться посреди объяснения, подумать и продолжить. Для такого рассказа оставил отдельный режим, в котором он сам говорит, когда закончил.
Большой выигрыш оказался в голосе. Piper раньше заново загружал голосовую модель к каждой реплике. Я оставил её прогретой и начал озвучивать ответ частями. На одинаковой короткой фразе подготовка первого кусочка на Pi сократилась примерно с 3,8 до 0,7 секунды. Это время подготовки звука, не вся дорога от вопроса до ответа. Но теперь Лягушка начинала говорить раньше - без этой неловкой тишины перед первым словом.
Позже подключил потоковую выдачу текста DeepSeek: программа собирает из неё короткие фразы и передаёт Piper ещё до готовности всего ответа. Соединение с API между вопросами тоже переиспользуется. Пока звука ещё нет, Лягушка показывает задумчивое лицо; обычная мимика и движение рта возвращаются с началом речи. Иначе получалось обидно: модель текст уже прислала, голос ещё готовится, а лицо улыбается так, будто вопрос вообще забыли.

Знак вопроса слегка покачивается. Это GIF из программной пробы анимации, не съёмка дисплея. Хотелось без надписи «загрузка 37%» показать, что Лягушка занята ответом.
Для слушания я попробовал отдельное любопытное выражение. Тут же перестарался: микрофон почти всё время включён, и новое лицо вытеснило обычное, которое мне нравилось больше. Вернул его, а любопытство оставил на время распознанной речи. Шум поворачивающегося привода сам по себе тоже не должен включать эту мимику.
Потоковая озвучка, конечно, тоже нашла способ меня поддеть. Как раз на ответе про нескольких людей Лягушка могла начать: «Да, узнала…» - и оборваться. Или сказать «Передо мной Созд…» и замолчать. Я воспроизвёл переход от одного лица к группе: прежнее правило приватности отменяло речь, когда число людей в кадре менялось. Короткий ответ с именами отделил от личной истории. В тесте ответ теперь не обрывается. Но я ещё раз поставлю перед камерой двоих: не хочется снова услышать «Созд…».
Мозги уже ответили, лицо ещё догоняет
А свежая история вообще началась с графики. Лягушка нормально слышала, отвечала достаточно быстро, но лицо двигалось с такими тормозами, что весь эффект живого персонажа пропадал. Я первым делом подумал на нагрев. Pi был горячий, камера, распознавание и речь работают одновременно - мало ли.
Проверил: температура около 64-65 °C, вентилятор крутится примерно на 4900 об/мин, CPU держит 2,4 ГГц, текущих признаков ограничения частоты нет. В тот момент перегревом эту задержку объяснить не получилось. Зато сама отрисовка занимала почти всё время обработчика интерфейса, а Xwayland съедал около 77% одного ядра. Для двух глаз, рта и бровей как-то многовато.
Лицо у меня рисуется фигурами на Tkinter Canvas. Старый порядок выглядел безобидно: удалить прежние фигуры, нарисовать новые. Но на этой связке Tk/Xwayland повторное создание фигур после полной очистки занимало много времени, из-за чего анимация шла рывками. Поменял порядок: старый кадр сначала скрывается, новый создаётся, и только потом удаляется старый. Так цветовые ресурсы остаются занятыми, пока их не подхватят новые фигуры.
В коде это оказалось небольшим изменением:
def draw(self, now, blink=1.0): self.itemconfigure('face', tags=('_retired_face',), state='hidden') try: self._draw_frame(now, blink) finally: self.delete('_retired_face')
Старым фигурам пришлось сразу убрать рабочие теги лица, рта и бровей. Иначе они продолжали бы участвовать в расчёте размеров и перемещений, даже будучи невидимыми. finally нужен, чтобы при ошибке не оставался ещё один скрытый слой. Рисунок при этом не упрощал и частоту анимации не снижал.
Вот что получилось на работающем Pi:
Что измерял |
До |
После |
|---|---|---|
Время рисования кадра, p95 |
171 мс |
9-10 мс |
Интервал цикла интерфейса, p95 |
205 мс |
43 мс |
Загрузка Xwayland, доля одного ядра |
77% |
4% |
Здесь p95 - время, в которое укладываются 95% последних 180 отсчётов. Это не средний FPS; загрузку CPU снимал отдельными восьмисекундными пробами. В обоих замерах работали камера, микрофон и привод, на экране было нейтральное лицо. Разговоры слово в слово не воспроизводил, поэтому дополнительно прогнал старую и новую отрисовку на одном дисплее в порядке старая → новая → новая → старая, без перезапуска графического сервера. Ускорение повторилось.
Заодно сравнил геометрию и цвета в 450 вариантах выражений, взглядов и моргания: совпали со старой отрисовкой. Сон и задумчивость тоже проверил. А после установки просто посмотрел на робота: всё снова плавно, без прежних тормозов. Вот это тот случай, когда таблица радует, но живые глаза на экране радуют ещё больше. Долгую работу в собранном корпусе всё равно проверю отдельно.
Стих учить, а не кнопки запоминать
Однажды я подумал о совсем другой просьбе: «Помоги выучить стих». Ребёнок читает по две строки, закрывает концовку тетрадкой, запинается, просит первое слово. Проверка в духе «правильно / неправильно» тут мало поможет. Захотелось, чтобы Лягушка вела занятие, как терпеливый партнёр, а не как экзаменатор.
Сначала получился экран с текстом и кнопками. Я сохранил пробный стих, нажал «по две строки» и сам запутался, что дальше: читать, нажимать, ждать? Ну отлично, теперь ребёнку вместо стихотворения надо выучить интерфейс. Переделал порядок. Лягушка читает маленький кусочек, ждёт повторения, постепенно скрывает окончания строк, подсказывает слово по просьбе и возвращается к трудному месту. Если распознанные слова не совпали с текстом, это ещё не доказательство ошибки ребёнка: микрофон тоже мог не расслышать. Лучше дать повторить, чем спорить с правильно прочитанной строчкой.
Стих можно ввести, найти по названию, продиктовать или сфотографировать страницу камерой Лягушки. После «найди стих» название тоже можно сказать голосом. Полученный текст сначала открывается для правки. Автофокус, поиск и распознавание иногда ошибаются; учить ребёнка неверной строчке было бы совсем обидно. На фразу «мне задали выучить стих» Лягушка предлагает помощь и начинает только после согласия.

Сначала строки видны целиком. Лягушка прочитала свою часть и ждёт повторения; каждый переход отдельно нажимать не нужно.
Этот сценарий уже прошёл программную пробу на выдуманном тексте. Но мне важнее будущий вечер, когда дочь попробует учить настоящий стих и скажет, удобно ли ей. Если она опять будет искать кнопку «дальше», значит, переделывать ещё раз.

А здесь часть слов уже спрятана. Оба кадра из программной проверки на выдуманном стихе, не из занятия с дочерью.
Дальше - собрать всё вместе
MG996R уже уверенно поворачивает почти собранную голову. Теперь занимаюсь соединением с телом: нужно убрать люфт и нормально провести шлейфы. Для новой шеи предусмотрел два подшипника 6901 и доступ к винту привода через затылок. В новой 3D-модели проверю геометрию до ±60°. Реальный ход ещё не измерял: его ограничат и детали, и свободная петля проводов.
Печать, сборка и проверка этого узла ещё впереди. Картинку готовой шеи пока не прикладываю по уважительной причине: готовой распечатанной шеи ещё нет.
С помощью по урокам тоже хочу двигаться дальше: разбирать непонятные задания, объяснять на примерах, помогать учить. От помощи родителей никто не отказывается. Но если папы или мамы сейчас нет рядом, можно обратиться к этому маленькому другу за объяснением, а потом обсудить результат вместе. Как сделать такую помощь полезной и удобной, ещё будем проверять с дочерью.
Чем дальше делаю Лягушку, тем меньше верю фразе «ну тут просто добавим одну кнопку». За кнопкой «Печатать» внезапно оказываются принтер, предпросмотр, микрофон и робот, который должен вовремя перестать объяснять, как этой кнопкой пользоваться. А ребёнок всего лишь хотел распечатать фотографию.
Теперь уже есть что пробовать вместе: показать предмет, поговорить, посмотреть разбор задачи, выучить пару строк.
Впереди - проверки узнавания в группе при разном свете и расстоянии, пробы команд голосом дочери и настоящий урок. Пока Лягушка живёт на столе среди проводов, но разговаривать с ней уже гораздо интереснее, чем отлаживать очередное окно настроек.
Продолжение будет - в третьей части соберём Лягушку в корпус, проверим её в деле и вернёмся к новым функциям. В общем, осталось «только собрать». Кажется, я уже знаю, чем это обычно заканчивается.