Прийшов час осідлати справжнього Буцефала🏇🏻Приборкай норовливого коня разом з Newxel🏇🏻Умови на сайті
×Закрыть

Дом с голосовым управлением: мой опыт реализации

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

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

Пролог

Тема умных домов, несомненно, на хайпе. Тем не менее для нашей страны это, к сожалению, пока еще темный лес. И дело тут вовсе не в инженерии как таковой. Дело в конечном потребителе и спросе на все эти умные «приблуды».

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

Те, кто уже делал у себя капитальный ремонт, три раза перекрестятся, если вы предложите им повторить этот дивный и запоминающийся опыт, потому что они прекрасно знают, что капитальный ремонт — это боль, масса потраченных сил, времени и финансов. Если говорить о тех, кто еще этого не делал, то тут могут сработать опять-таки либо финансовые, либо психологические факторы.

Лично я начал интересоваться IoT-тематикой уже после того, как был сделан капитальный ремонт. Соответственно, я не имел возможности заранее спроектировать и спланировать интеграцию всех этих умных штуковин в свою квартиру. Когда же появились знания, желание и возможности, основным критерием подобной интеграции для меня послужила минимизация расходов и деструктивной активности по отношению к своей обители. С технологической точки зрения я фокусировался на нестандартных решениях, направленных прежде всего на UX. Так и зародилась тема голосового управления собственным домом.

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

Немного о себе. Я работаю в компании Waverley Software на позиции Technical QA Manager. Несмотря на свой title, я занимаюсь не только вопросами обеспечения качества, вовлечен еще и в разработку, DevOps-практики, а также в улучшение процессов в компании. Я член программного комитета и постоянный спикер крупнейших украинских конференций по обеспечению качества: Selenium Camp и QA Fest. Консультирую, менторю, провожу тренинги. Мои контакты можно найти в профиле. Так что по любым вопросам — you’re welcome.

Проблематика

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

Ситуация номер один: представьте, что вы захотели каких-то вкусняшек. Вы направляетесь на кухню, включаете свет, набираете печенек/конфет/[ваш вариант], наливаете чай и направляетесь к выходу по своим делам.

Дойдя до двери, вы понимаете, что надо выключить свет. Нас ведь еще с детства приучили к экономии электроэнергии, не так ли? Но обе руки-то заняты! Казалось бы, решение очень простое: поставить все на стол (освободив руки), выключить свет, снова взять чашку с вкусняшками и отправиться по своим делам. Раз плюнуть! Но...

Человек-то — существо ленивое. И в таких ситуациях, как правило, наш мозг начинает проводить некие оптимизации с целью минимизации общего количества действий, которые нужно осуществить для решения подобного рода задач. И что в итоге оказывается наиболее простым вариантом выключения света, на наш взгляд? Конечно же, использование любых свободных частей тела: локтя, колена и т. п. — у кого на что хватит гибкости и растяжки.

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

Ситуация номер два: вы очень ждали какое-то событие и хотели посмотреть его по телевизору. К примеру, финал Лиги чемпионов по футболу или «Україна має талант». Вы набираете попкорн, пивасик и усаживаетесь поудобнее в кресло за минуту до начала.

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

Ситуация номер три: вы пришли уставшие с работы, еле добрели до спальни, закинули телефон куда-нибудь подальше (чтобы утром хоть как-то проснуться по будильнику), завалились в кровать, и тут к вам приходит осознание того, что вы забыли выключить свет в коридоре.

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

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

Еще с детства нас приучили, что для управления телевизором нужно воспользоваться пультом дистанционного управления. Конечно, не у всех было такое счастливое детство. Лично я застал период, когда каналы надо было накручивать на самом телевизоре. Но тем не менее мы будем рассматривать более современный период.

Что еще? Для манипуляций со светом мы привыкли к обычным кнопочным выключателям. Более модный и молодежный инструмент управления умными устройствами — мобильный телефон и т. д.

Теперь давайте подумаем: а что объединяет все эти интерфейсы в контексте нашего с ними взаимодействия?

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

Так вот, главный вопрос теперь заключается в том, можно ли как-то упростить себе жизнь при попадании в подобные ситуации.

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

Таким образом, дальнейшее повествование я буду основывать на концепции улучшения UX за счет расширения списка доступных конечным пользователям (коими мы с вами и являемся) интерфейсов, чтобы дать им свободу выбора в зависимости от ситуации, в которой они окажутся.

Низкоуровневое управление устройствами

Внутренняя коммуникация

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

Последующее изложение будет основано на упомянутом в самом начале критерии — минимизации потенциальных расходов. В центральной части приведенного выше изображения можно лицезреть бюджетную плату NodeMCU V3 на базе чипа ESP8266, представляющего собой Wi-Fi-модуль. В буквальном смысле для нас это означает, что эта плата при желании может стать полноценным «жителем» нашей локальной подсети. Зачем это может нам понадобиться, мы узнаем чуть позже. А пока обратим внимание на то, что изображено вокруг. Это различные сенсоры и приемопередатчики, которые при подключении к нашей плате позволят ей отправлять и принимать какую-либо информацию об окружающем нас мире.

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

Наверняка, заходя в магазин светильников, вы обращали внимание на то, что некоторые люстры продаются в комплекте с подобного рода пультами:

С помощью такого пульта мы можем включить/выключить свет. По сути, он содержит в себе радиопередатчик, работающий на частоте 433 МГц. Соответственно, в люстре у нас присутствует радиоприемник, умеющий интерпретировать сигналы, отправляющиеся с пульта при нажатии на кнопки A и B.

С точки зрения удобства использования такой пульт не представляет особой ценности. Но чисто технически, обладая знаниями о том, какую именно информацию он отправляет на люстру, мы можем без особого труда проэмулировать его работу посредством радиопередатчика, подключенного к NodeMCU.

То же касается и других устройств (например, светодиодных лент), использующих радиочастоты для внешней коммуникации.

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

Такая эмуляция позволит нам программно управлять теми устройствами, к которым мы так привыкли, даже если в них нет никаких smart-функций.

Более подробно об этом я расскажу чуть позже. Сейчас нам важно понять возможности нашего микроконтроллера в контексте взаимодействия с окружающим миром.

Внешняя коммуникация

Когда и зачем нам может понадобиться внешняя коммуникация с микроконтроллерами?

Современные методы управления умными устройствами зачастую предполагают их доступность из любой точки мира. Если следовать подобной тенденции, то нам нужно обеспечить доступ к нашим микроконтроллерам извне. Но тут у нас вырисовывается некая развилка, предполагающая выбор: ходить ли на устройства напрямую или через какой-то коммуникационный узел? Здесь же следует задать и другой вопрос: каким способом будет осуществляться взаимодействие?

У себя я организовывал следующую схему. Внешние запросы прилетают посредством REST на локально поднятый веб-сервер по защищенному соединению. Для этого я покупал доменное имя и SSL-сертификат. При этом сервер, проанализировав запрос, рассылает сообщения микроконтроллерам в локальной подсети по MQTT-протоколу. Этот вариант показался мне самым простым и относительно надежным с учетом того, что все это делалось на энтузиазме в свободное время. При коммерческой разработке безопасности, конечно же, следует уделить намного больше внимания. Но рассматриваемый случай совсем не относится к тем, когда следует параноить.

Железо сервера

Чтобы не держать ресурсоемкий PC 24/7 для решения таких простых задач, в качестве железа был выбран Raspberry Pi 3 Model B+ (к слову, 4-й версии тогда еще не существовало). Я брал его в комплекте с корпусом, дополнительными радиаторами и хорошим кулером. На кулере рекомендую не экономить, так как китайские варианты выдают просто космический уровень шума. Чувствуешь себя как на аэродроме. После нескольких часов сидения рядом с таким устройством начинает болеть голова. Я долго искал варианты, оптимальные с точки зрения уровня шума. В итоге о своем выборе ни капельки не пожалел. Этот setup стоит у меня в спальне и совершенно не мешает ни работе, ни сну.

Софт, необходимый для разработки

При любых раскладах для реализации конечной цели нам понадобится умение программировать. Наш конкретный случай предполагает базовые знания C/C++. Но в целом сложность кода не настолько высокая, чтобы испугать разработчика с опытом в каком-либо другом популярном ныне языке программирования.

Если говорить о среде разработки, лично я попробовал следующие варианты: Arduino, Atom, VS Code и CLion. Первые два я отнес бы к категории либо «для начинающих», либо «набросать по-быстрому». Если же вы разработчик с опытом, ценящий скорость и качество разработки, то рекомендую обратить внимание на последние два варианта. VS Code — бесплатный, CLion — платный.

В пару ко всем этим IDE (кроме Arduino) нам потребуется еще и плагин PlatformIO, существенно ускоряющий процесс разработки. И вот тут появляется очень большая разница между VSCode и CLion. В первом варианте плагин мощный и удобный, во втором — неожиданно убогий и баговый. Если сравнивать исключительно две эти IDE, то для меня CLion выглядит более функциональным (возможно, из-за того, что я уже привык к другим продуктам JetBrains). Поэтому тут все очень индивидуально, it’s up to you, на чем акцентировать внимание — на качестве плагина или на IDE.

По библиотекам все сложно. Их много, и все они платформозависимые. Мне лично было очень непривычно и жутко неудобно переиспользовать те скетчи (runnable code snippets), которыми кишит интернет. Потому я написал парочку оберток, которые значительно ускоряют прототипирование: arduino-network и arduino-sensors-wrappers. Они могут пригодиться вам для quick start.

Практический пример

Чуть ранее я затронул тему управления обычными устройствами. Теперь пришло время разобрать эту часть более подробно на примере простой LED-лампочки — рядового «жителя» любой люстры.

Мы можем управлять ею только с помощью выключателя. Но нас ведь интересует именно программное управление, не так ли? Какие у нас есть для этого опции?

Первый вариант, который сейчас активно появляется на нашем рынке, — это Sonoff. Выглядит неплохо, но я даже не стал смотреть в его сторону ввиду того, что ему нужен прямой подвод к фазе. А у меня монтажные коробки расположены таким образом, что ради подключения этого выключателя пришлось бы рвать обои и долбить стены. Сразу вспоминаем второй критерий (деструктивная активность по отношению к своей квартире).

Из недорогих альтернативных вариантов начал смотреть на китайские опции вроде такого выключателя. У них часто бывают скидки, что дает возможность покупки чуть ли не вдвое дешевле Sonoff. Из плюсов: работает от батарейки CR2032, легко клеится на стену без каких-либо разрушений, содержит встроенный радиопередатчик 433 МГц, возможна опция покупки вместе с радиореле (об этом чуть дальше). Если же вам не важна эстетика, то можно взять и более дешевый кнопочный вариант с аналогичным функционалом.

Итак, допустим, мы взяли себе такой радиовыключатель.

Как теперь связать его с нашей LED-лампочкой? Вспоминаем ранее рассмотренный радиопульт, который продается в комплекте с некоторыми люстрами. Я уже упоминал о том, что он взаимодействует с неким приемником. Соответственно, если в нашем выключателе есть радиопередатчик, нам не хватает только приемника. А что из себя представляет приемник в случае обычной лампочки? Это радиореле, которое может быть спрятано где-то на подходе (к примеру, в полом плафоне). По ссылке выше есть опция покупки выключателя вместе с радиореле. Но, несмотря на его весьма неплохое production-ready-состояние, я бы обратил внимание на какие-то альтернативные варианты. Вот, к примеру, такой — за $2-4 из Китая.

Огромным преимуществом этого варианта является то, что у него, в отличие от предшественника, есть опция гибкого обучения в нескольких режимах. Подобного рода реле приходят к нам с чистой памятью, и мы сами должны обучить их распознавать сигналы выключателя. Существует два наиболее популярных режима обучения: self-lock и interlock. Первый предполагает, что одна и та же кнопка выключателя будет работать и на замыкание, и на размыкание реле (то есть свет будет и включаться, и выключаться по нажатию одной и той же кнопки). Второй же режим предполагает включение по одной кнопке, а выключение — по другой. Для достижения нашей конечной цели более гибким вариантом обучения является режим interlock — и с точки зрения случайных срабатываний (поясню чуть позже), и с точки зрения отслеживания изменения состояния. Реле, идущее в комплекте с выключателем, позволяет обучаться лишь в режиме self-lock. В целом, если вас это устраивает, то можно покупать и набором.

Итак, у нас есть три звена: лампочка, реле и выключатель. Процесс обучения подробно расписан и показан на страницах товаров, так что я не буду акцентировать на этом внимание. Как нам теперь узнать коды, отправляемые выключателем на реле, для получения возможности управления с помощью NodeMCU?

Для этого нам понадобится все та же NodeMCU в комплекте с радиоприемником для прослушивания радиоэфира.

Я бы рекомендовал обратить внимание еще и на специальный адаптер. Он идеально подойдет для разработки по ряду причин. Во-первых, к нему можно подключить нормальный блок питания до 24 В. Во-вторых, у него есть дополнительно 4 пары пинов (5V-GRD). Это очень важно ввиду того, что большинство необходимых нам приемопередатчиков требуют для работы 5 В, чего не может напрямую обеспечить NodeMCU.

Подключившись к нашей плате через serial port, мы можем наблюдать, что происходит в логах при нажатии на кнопки выключателя.

Код для этого нам понадобится элементарный (с использованием ранее упомянутых библиотек):

#include "RFReceiver.hpp"

RFReceiver* receiver;

void setup() {
    Serial.begin(BAUD_RATE);
    receiver = new RFReceiver(RF_RECEIVER_PIN);
}

void loop() {
    receiver->listen();
}

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

Disclaimer. Изображенная схема подразумевает подключение реле в сеть 220 В. Если вы никогда не работали с высоким напряжением, обязательно пригласите человека, который расскажет вам основные правила безопасности и поможет сделать все под присмотром.

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

home/bedroom/device/927a15b2-d6fd-4f3a-a1c3-fdc7ccf25d45

Помимо этого, нам следует согласовать формат сообщений. Чтобы немного упростить себе жизнь при дальнейшей интеграции с другими компонентами нашей системы, можно сразу заложить поддержку JSON.

Чтобы улучшить читабельность кода, можно добавить дополнительный уровень абстракции для управления подконтрольной лампой:

#include "BedroomLamp.hpp"

const unsigned int BedroomLamp::RC_SWITCH_PROTOCOL = 1;
const unsigned int BedroomLamp::RC_SWITCH_LENGTH = 24;
// Intercepted codes
const unsigned long BedroomLamp::TURN_ON_CODE = 180356;
const unsigned long BedroomLamp::TURN_OFF_CODE = 180353;

BedroomLamp::BedroomLamp(uint8_t transmitterPin) : Lamp(transmitterPin) {
    Lamp::switchProtocol(RC_SWITCH_PROTOCOL);
}

void BedroomLamp::changeState(bool shouldTurnOn) {
    if (shouldTurnOn) {
        Lamp::turnOn(TURN_ON_CODE, RC_SWITCH_LENGTH);
    } else {
        Lamp::turnOff(TURN_OFF_CODE, RC_SWITCH_LENGTH);
    }
}

При этом основной управляющий код может выглядеть следующим образом:

#include "EntryPoint.hpp"
#include "BedroomLamp.hpp"
#include "MqttClient.hpp"
#include "OtaServer.hpp"
#include "WifiClient.hpp"

MqttConfig *mqttConfig;
MqttClient *mqttClient;
OtaServer *otaServer;
WifiClient *wifiClient;
BedroomLamp *bedroomLamp;

void setup() {
    Serial.begin(BAUD_RATE);
    initNetwork();
    initSensors();
}

void initNetwork() {
    wifiClient = new WifiClient(DEVICE_IP, MASK, GATEWAY, WIFI_SSID, WIFI_PASSWORD);
    otaServer = new OtaServer();
    mqttConfig = new MqttConfig(MQTT_BROKER_IP, MQTT_BROKER_PORT, MQTT_USERNAME, MQTT_PASSWORD, mqttCallback);
    mqttClient = new MqttClient(mqttConfig, wifiClient);
}

void initSensors() {
    bedroomLamp = new BedroomLamp(RF_TRANSMITTER_PIN);
}

void mqttCallback(const char *topic, const byte *payload, unsigned int length) {
    // mqtt payload processing
    // bool value = …
    bedroomLamp->changeState(value);
}

void loop() {
    otaServer->listen();

    if (!mqttClient->isConnected()) {
        mqttClient->connect([] { mqttClient->subscribe(Topic::DEVICE.c_str()); });
    }

    mqttClient->keepAlive();
}

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

Уже в текущем варианте мы можем совершенно спокойно управлять нашими устройствами из любой точки мира при условии проброса порта MQTT-брокера в мир. Но нам это не понадобится, потому что мы будем придерживаться первоначальной схемы.

Голосовое управление

Два лагеря

Итак, мы уже приблизительно поняли, как происходит общение с устройствами на самом низком уровне. Теперь попытаемся понять, каким образом звуковая волна (в виде голосовой команды) может быть трансформирована в радиосигнал, отправляемый на реле для манипуляции светом.

Как вы уже, наверное, успели догадаться, нам никак не обойтись без системы распознавания речи. И тут можно выделить два больших лагеря: готовые устройства и собственные «велосипеды». Вам придется заранее определиться с выбором, дабы не утонуть в пучине сложностей, с которыми у вас потенциально может не хватить сил бороться.

VS

Если говорить о готовых решениях, тут в лидерах продукты от Amazon и Google. В случае же изобретения своего собственного решения, помимо железной составляющей, которой я коснусь чуть позже, есть еще и довольно крупная софтверная часть. Я лично игрался с CMU Sphinx и Kaldi (распознавание речи), а также с Marry TTS (синтез речи). Хотя, насколько я знаю, на рынке уже есть и неплохие альтернативы. Тем не менее акцентировать внимание я буду именно на Kaldi — инструменте, с которым знаком лично.

Свой «велосипед»

Как я уже отмечал ранее, для реализации нашей цели — голосового управления — нам необходима система распознавания речи, причем довольно хорошая. Когда я занимался изучением этого вопроса, основными критериями для меня были вменяемая точность распознавания, open-source, возможность локального развертывания и поддержка русского языка. Все эти критерии отлично покрывались Kaldi.

Итак, допустим, мы решили строить свой «велосипед». Что нам понадобится по софту?

  • Kaldi Docker image: я настоятельно рекомендую пользоваться Docker и не пробовать разворачивать все на вашей основной системе, если только вы не хотите прострелить себе обе ноги. Этот образ содержит уже готовую русскую модель и WebSocket server (Python).
  • Socket client: ввиду наличия сокет-сервера берем любой клиент (независимо от языка).
  • Wake word detection engine: если мы планируем делать все по образу и подобию готовых решений, нам понадобится обучить модель реагировать на какое-нибудь активационное слово или фразу. Это необходимо и с точки зрения защиты приватности, и чтобы не дергать лишний раз наш ASR. Приведенный по ссылке сервис позволяет вам сделать это совершенно бесплатно, пригласив любого желающего поучаствовать в обучении. К слову, я уже начинал обучать модель реагировать на фразу «Эй, Мэри». Так что, желающие помочь, welcome.

Далее последует пример NestJS-сервиса, работающего с HotwordDetector и Kaldi. Но то же самое можно сделать и на любом другом языке. Идея заключается в отправке голосового потока — Kaldi, только после срабатывания активационного слова. Здесь же предусмотрена и симуляция работы тайм-аута у реальных колонок при молчании пользователя.

Ввиду того, что транскрипт может прийти неточный (с довольно высокой вероятностью), для корректной интерпретации команд нам понадобится еще какой-нибудь хороший алгоритм сравнения строк. Я использовал библиотеку string-similarity, основанную на коэффициенте Дайса.

При этом обработчик полученного транскрипта от Kaldi может тут же отправлять команду на наш микроконтроллер с помощью MQTT. Эту часть я пока опускаю, чтобы не смещать фокус в сторону коммуникаций. Пока акцентируем внимание на ASR-части.

@Injectable()
export class ASRService {
  @Inject()
  private readonly kaldiSocketService: KaldiSocketService
  @Inject(WINSTON)
  private readonly logger: Logger
  private readonly detector: HotwordDetector

  constructor() {
    this.detector = new HotwordDetector(DETECTOR_OPTIONS, [HOT_WORD_MODEL_OPTIONS], RECORDER_DATA, console)
      .on('error', err => this.logger.error(err))
      .on('hotword', (index, hotword, buffer) => {
        this.logger.info('[WAKE WORD DETECTED] %s', hotword)
        this.detector.stop()
        this.kaldiSocketService.startRecognition(this.start)
      })
  }

  public start = () => {
    this.detector.start()
  }
}
@Injectable()
export class KaldiSocketService {
  @Inject(WINSTON)
  private readonly logger: Logger
  private readonly recorder: AudioRecorder = new AudioRecorder(RECORDER_OPTIONS, console)
  private socketClient: WebSocket
  private pauseTimeout: NodeJS.Timeout | null = null

  public connect(): void {
    if (this.socketClient) {
      this.socketClient.removeAllListeners()
    }

    this.socketClient = new WebSocket(KALDI_SERVER_URL)
      .on('open', () => {
        this.logger.info('Connected to Kaldi server')
      })
      .on('close', () => {
        this.logger.info('Disconnected from Kaldi server')
      })
      .on('message', (data: string) => this.handleMessage(data))
  }

  public startRecognition(restartFn: () => void) {
    if (!this.isSocketAlive()) {
      this.connect()
    }

    if (this.recorder) {
      this.recorder.stop()
    }

    this.recorder
      .start()
      .stream()
      .on('error', err => this.logger.error(err))
      .on('data', chunk => this.sendChunk(chunk))
      .on('end', () => restartFn())
    this.startPauseTimer(this.stopRecognition)
  }

  public stopRecognition = (): void => {
    this.recorder.stop()
  }

  public sendChunk = (chunk: any): void => {
    this.socketClient.send(chunk)
  }

  public disconnect = () => {
    this.sendChunk('{"eof" : 1}')
    this.stopPauseTimer()
  }

  private async handleMessage(data: string): Promise<void> {
    const kaldiPayload: KaldiPayload = JSON.parse(data)

    if (kaldiPayload.result && kaldiPayload.result.length > 0) {
      this.logger.info('[FINAL TRANSCRIBE] %s', kaldiPayload.text)
      const match: BestMatch = compare(kaldiPayload.text, AVAILABLE_COMMANDS)
      const command: string = match.bestMatch.target
      this.logger.info('[MATCHING COMMAND] %s -> %s', kaldiPayload.text, command, match.bestMatch.rating)

      if (match.bestMatch.rating > SIMILARITY_LEVEL && POWER_COMMANDS.includes(command)) {
        // publish MQTT message
        this.startPauseTimer(this.stopRecognition)
      } else if (command === STOP_WORD) {
        this.disconnect()
      }
    } else if (kaldiPayload.partial) {
      this.logger.info('[PARTIAL TRANSCRIBE] %s', kaldiPayload.partial)
      this.stopPauseTimer()
    }
  }

  private startPauseTimer = (callback: () => void): void => {
    this.pauseTimeout = setTimeout(callback, SILENCE_TIMEOUT)
  }

  private stopPauseTimer = (): void => {
    if (this.pauseTimeout) {
      clearTimeout(this.pauseTimeout)
      this.pauseTimeout = null
    }
  }

  private isSocketAlive(): boolean {
    return this.socketClient.readyState === this.socketClient.OPEN
  }
}

В чем подвох?

Казалось бы, вот же она — почти идеальная система голосового управления собственным домом! Да еще и на русском языке! Дайте две!

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

  • ASR-домен. Скорее всего, «поиграться» вам хватит и того, что уже рассмотрено ранее. Но со временем вы начнете натыкаться на множество проблем (точность распознавания, ложные срабатывания, фильтрация шумов и т. п.), которые потребуют от вас определенных знаний в области распознавания речи. И тут придется довольно далеко выйти из своей зоны комфорта.
  • Упрочнение модели wake word. Для демоцелей вам хватит и нескольких семплов. Но, чтобы выйти на production-ready-уровень, придется собирать с миру по нитке для улучшения модели.
  • Микрофон с высокой долей вероятности станет убийцей всей вашей затеи со своим «велосипедом». Дело в том, что в серийном производстве у нас нет универсальных решений, покрывающих все нюансы использования в контексте умного дома. Голосовое управление, как идея, будет иметь право на жизнь тогда и только тогда, когда мы сможем предоставить конечному пользователю возможность быть услышанным из любой точки помещения. Где бы вы ни находились в пределах комнаты, ваш продукт должен слышать и понимать вас вне зависимости от того, присутствуют вокруг посторонние шумы или нет.

В свое время я споткнулся именно на проблеме микрофона. Дело в том, что свои тесты я проводил на лэптопе и беспроводной гарнитуре в идеальных условиях. Но когда дело дошло до реального использования, я понял, что ни один из бюджетных микрофонов не приспособлен для решения таких задач. А на дорогостоящее железо я даже не стал смотреть, потому что это разрушило бы наш первый критерий — минимизацию расходов. В итоге затею с голосовым управлением на собственном «велосипеде» пришлось приостановить.

Первая надежда

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

Железо готовых решений

То, что практически сразу бросилось в глаза уже при первом тестировании, — это способность умных колонок слышать и понимать собеседника на относительно большом расстоянии (взять, к примеру, комнату длиной 6-8 метров), несмотря на посторонние шумы. Мне тут же стало жутко интересно, как работает их железо. Ведь, по сути, тот же Amazon Echo Dot 3 можно было купить по $22 на Black Friday. А это намного дешевле всех модных микрофонов, которые совсем не решают описанных выше проблем.

Из того, что удалось нарыть, самым вменяемым оказалось видео с Intel AI DevCon. Если кратко, то там приводилась следующая схема:

Тут мы видим целый массив из четырех микрофонов (характерно для Echo Dot 3, для других моделей может отличаться), динамик и чип с DSP-алгоритмами на борту.

Из алгоритмов можно выделить следующие:

  • wake word detection;
  • audio feedback disposal;
  • acoustic echo cancellation;
  • beamforming.
Более подробно о них можно узнать из видео, на которое я сослался выше.

По Google ничего вменяемого, в плане железа, найти не получилось. Но, судя по наблюдениям, работают их умные колонки приблизительно на том же уровне, что и амазоновские. Соответственно, можно сделать допущение о том, что в плане железа и возможностей они стараются не отставать от прямых конкурентов.

В результате мощная железная составляющая по относительно невысокой цене автоматически выводит готовые решения в лидеры по сравнению с собственным «велосипедом».

Не все так гладко

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

Приблизительно таким же образом моя супруга общается дома с Alexa. Но в защиту устройств от Amazon необходимо отметить, что в этом видео можно совершенно спокойно подставлять вместо Alexa любую существующую альтернативу, будь то Google, Siri и прочие, — результат будет неизменным.

Боевые испытания

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

Такой вариант проходит бета-тест у меня дома уже около года. Результаты наблюдений я приведу чуть позже. Для начала нужно избавиться от неизвестных в правой части, а именно понять, как Alexa интерпретирует пользовательские команды и что с ними происходит дальше.

Когда пользователь выражает намерение взаимодействовать с каким-либо устройством, происходит следующее:

  • голосовой поток после предобработки на железе улетает в Amazon Cloud, где в конечном счете попадает на так называемый Alexa Skill;
  • на скиле формируется специализированный запрос (директива), содержащий информацию о пользовательском намерении;
  • директива улетает на Lambda function (back-end нашего скила), где мы обязаны ее обработать;
  • на этом полномочия Amazon заканчиваются.

Alexa Skill

Alexa Skill — это, по сути, программное обеспечение для колонки. При этом хостится все на стороне Amazon. Существует несколько типов скилов, которые вы можете разрабатывать. Но нам необходим лишь smart home skill. Quick start guide можно найти в официальном GitHub-репозитории. Если вы решили следовать именно ему, то можете пропустить эту часть. Но вынужден предупредить, что, несмотря на заявленные в документации 15 минут, можете смело выделять неделю своего времени на то, чтобы разобраться во всем с нуля.

Для старта вам понадобится зарегистрировать основной Amazon-аккаунт и аккаунт разработчика. Скилы создаются именно из-под Dev-аккаунта. Но основной нам понадобится для создания Lambda function (об этом чуть позже). Сразу стоит отметить то, что при регистрации вам обязательно придется привязать платежную карту. При этом с вас снимут $1, а затем вернут обратно, чтобы проверить вашу платежеспособность. Но не волнуйтесь, создание скилов не будет стоить вам ни копейки.

Итак, после регистрации идем в dev console и жмем Create Skill.


На следующей странице даем имя нашему скилу и не забываем выбрать тип:


После создания скила вы увидите следующую страницу:


И тут нас подстерегает первая неожиданность от Amazon. В качестве back-end для нашего скила мы можем использовать только AWS Lambda (в отличие от других типов скилов). К чему это приведет, я расскажу чуть позже. Lambda у нас пока нет, поэтому делать тут нечего. Следуем ссылке в самом низу — Setup Account Linking.

Страница довольно важная, так как здесь вам придется сконфигурировать доступ к своему oauth-провайдеру. Шаг обязательный, без него вы не сможете идти дальше. Здесь вам придется сделать выбор: использовать либо built-in-поддержку Amazon Oauth, либо какой-то существующий сервис вроде Auth0, либо свой «велосипед». Я покажу пример конфигурации для первых двух вариантов.

Amazon Oauth

Идем на страницу Login with Amazon и создаем новый security profile.


Заполняем обязательные поля (privacy notice URL для тестовых целей можно поставить фейковый — http://example.com/privacy.html) и сохраняем. На этом этапе у нас уже сгенерированы client id и secret, которые понадобятся нам на скиле.


Но нам нужно выполнить еще один шаг. Открываем Web Settings нашего профиля на редактирование и обновляем поле Allowed Return URLs данными из поля Allowed Redirect URLs нашего скила.


На самом же скиле нужно заполнить client id / secret сгенерированными значениями из профиля:

Остальные поля заполняем так, как показано на скрине выше. Сохраняем, Oauth настроен!

Auth0

Создаем Auth0-аккаунт (лучше воспользоваться вариантом с соцсетями). Затем создаем новый Machine to Machine Application.


Далее нам нужно сделать то же, что мы уже делали в случае с Amazon security profile: заполнить Allowed Callback URLs значениями из скила:


Ну а на самом скиле уже надо будет заполнить client id / secret, сгенерированный на Auth0. Остальные поля придется заполнять в соответствии с требованиями Auth0 и именем вашего домена.

AWS Lambda

Как я уже упоминал ранее, в качестве back-end для обработки запросов с Alexa Skill нам понадобится Lambda function. Для ее создания вам придется воспользоваться уже основным AWS-аккаунтом.


Здесь, задав имя, все остальное можно оставить по умолчанию, если только вам не нужен другой runtime. Мы будем использовать пример на базе JS/TypeScript. Вы же можете выбрать более близкий вам язык, но тогда придется адаптировать код.


Далее на главной странице нам нужно добавить Smart Home trigger.


При этом вам сразу придется указать ID вашего скила, который можно скопировать на главной странице:


Для того чтобы теперь связать функцию с нашим скилом, необходимо скопировать ARN:

Затем ARN надо вставить в поле Default Endpoint на главной, то есть в итоге у нас образуется жесткая двусторонняя связь между скилом и Lambda.

Итак, у нас все готово для разработки back-end.

Alexa Smart Home Skill API

Как я уже отмечал ранее, на нашем back-end (Lambda) понадобится реализовать обработчики запросов, прилетающих из скила. И тут начинается ад, потому что придется долго и нудно «курить» официальную документацию. Основная проблема тут в том, что готового SDK под smart home skills пока еще не существует, поэтому многие вещи придется описывать с нуля. На просторах GitHub можно найти разные «велосипеды» пользователей, которые столкнулись с такой же проблемой. Но после изучения исходников становится понятно, что код строился в основном под свои специфические кейсы.

Теперь о проблеме AWS Lambda. Была бы возможность, я бы вообще от нее отказался, потому что это чуть ли не основной bottleneck в контексте smart home skills. Приведу простой пример. Приходите вы такие домой с работы (никто не обращался к вашим устройствам в течение дня), просите Alexa включить свет. И что, думаете, он вот так сразу и загорится? А вот и нет! Cold start нашей «лямбды» просто уничтожает весь UX. Задержка просто нереальная для таких задач. Да, все последующие запросы будут проходить довольно шустро, но первый очень разочаровывает. Политика Amazon тут предельно ясна: подсадить пользователя на как можно большее количество своих сервисов. Но не давать разработчикам права выбора (как в случае с другими скилами) — это просто свинство.

На самом деле, в процессе написания этой статьи Amazon объявил о новой фиче Provisioned Concurrency, которая якобы должна решить проблему холодного старта. Но пока не совсем очевидно, будет ли существовать какая-нибудь лимитированная free-версия. В противном случае это будет выглядеть весьма странно: заманивать разработчиков бесплатностью написания скилов, но в то же время добавлять ложку дегтя, которая выглядит как «хочешь улучшить UX — плати».

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

Для quick start с лампочкой вам понадобится реализовать несколько ключевых обработчиков:

  • Authorization — срабатывает при активации пользовательского аккаунта (account linking).
  • Discovery — вызывается для опроса устройств, находящихся в локальной подсети с колонкой (тут важно быстро вернуть список доступных, пусть даже виртуальных).
  • ReportState — срабатывает в случае необходимости запросить состояние конкретного устройства (актуально для мобильного приложения).
  • PowerController — обработчик команд включения/выключения (срабатывает по ключевым словам — on/off).

Схематически процесс преобразования голоса в API-запрос выглядит следующим образом (на примере Discovery):

Когда мы просим Alexa найти устройства, в облаке формируется директива, содержащая разнообразную информацию. Но для нас наиболее актуальными являются Interface и Header Name. Эти два поля дают нам возможность однозначно идентифицировать обработчик, который необходимо вызвать в ответ на прилетевший запрос. В этом конкретном примере мы обязаны вернуть детальный список устройств (Endpoints). При этом в числе endpoint-характеристик присутствуют два важных поля: Endpoint ID (уникальный идентификатор устройства) и Friendly Name (имя, на которое устройство будет откликаться). Чтобы подробнее понять важность этих полей, давайте рассмотрим еще одну схему использования уже управляющей команды включения света:

Когда мы произносим «Alexa, light on», в облаке происходит поиск соответствия озвученного friendly name — light одному из ID ранее найденных устройств (endpoint id). Таким образом, в директиве у нас прилетает уже идентификатор устройства, а не его имя. При этом интерфейс определяется на основании самой команды on, которая, согласно документации, привязана к PowerController. Header же опирается на само значение: в случае с on — это TurnOn, а для off было бы TurnOff. Вот такой вот нехитрый принцип.

Следует также отметить, что реализация так называемых контроллеров напрямую зависит от тех свойств, что мы вернули на фазе Discovery для каждого из устройств. Например, светодиодную ленту мы можем как включать/выключать, так и менять ее яркость. А это означает, что в процессе поиска такой ленты мы должны обозначить поддержку PowerController и BrightnessController. Таким образом, когда пользователь захочет включить ленту, сработает первый, изменить яркость — второй.

Ну а что мы будем делать с этой информацией дальше, это наше личное дело. Как я уже отмечал ранее, на этом полномочия Amazon заканчиваются.

Помимо официальной документации, желательно обратить внимание на примеры payload для разных типов запросов. Они очень сильно упростят процесс реализации обработчиков. Ну и рекомендую еще ознакомиться с примером моего «велосипеда», который можно взять за шаблон для smart home back-end. Это первая версия, которая довольно долгое время проходила боевые испытания.

Но с недавнего времени я тестирую и альтернативный подход — использование Lambda в качестве прокси на свой собственный back-end. Это было сделано по ряду причин. Часть из них раскроется позже. Но основная — это максимальная независимость от облачных сервисов. Если вдруг Amazon через время опомнится и даст возможность ходить напрямую на свой back-end, это сможет свести к нулю затраты на потенциальную миграцию.

Пример реализации Authorization handler на собственном back-end NestJS:

@Injectable()
export class AuthorizationHandler implements RequestHandler {
  @Inject()
  private readonly requestProvider: RequestProvider

  public handle(): SmartHomeResponse {
    return {
      event: {
        header: {
          namespace: Interface.AUTHORIZATION,
          name: HeaderName.ACCEPT_GRANT_RESPONSE,
          payloadVersion: this.requestProvider.request.directive.header.payloadVersion,
          messageId: uuid4()
        },
        payload: {}
      }
    }
  }

  public canHandle(): boolean {
    return this.requestProvider.canHandle(this.name)
  }

  public get name(): RequestIdentifier {
    return Interface.AUTHORIZATION
  }
}

Как видно из кода, нет ничего сверхъестественного. Важно лишь следовать формату, описанному в официальной документации.

А что же у Google?

Забегая вперед, скажу, что в настоящее время я обкатываю похожую схему smart home и c колонкой Google Home.

На первый взгляд, все очень похоже на вариант с Amazon. Вместо Alexa Skill используется аналогичный Google-сервис под названием Actions on Google. Но тут есть несколько ключевых отличий:

  • у Google нет встроенной поддержки oauth, поэтому придется либо подключать свое решение, либо воспользоваться готовым провайдером вроде Auth0;
  • Google позволяет ходить напрямую на свой собственный back-end без каких-либо serverless-зависимостей.

Последний пункт в моем личном рейтинге возносит Google на вершину хит-парада, потому что таким образом мы можем свести к минимуму количество облачных зависимостей. Более того, сейчас в developer preview находится killer feature, которая может потенциально вывести Google и в мировой топ, — возможность развертывания собственного кода непосредственно на колонке, что в сочетании с их SDK даст возможность общаться с нашими микроконтроллерами напрямую. Заявленная задержка в 300 мс выглядит многообещающе.

Наиболее внимательные читатели наверняка заметили еще один дополнительный узел — БД. Первую версию своего решения я строил с минимальным числом зависимостей. Соответственно, состояния хранились в памяти самих микроконтроллеров. Но если вы обратите внимание на любой из популярных how-to по созданию smart home skills от Amazon или Google, вы заметите рекомендации относительно использования DynamoDB/Firestore для хранения состояний/информации о ваших устройствах. Я настоятельно не рекомендую верить в эти «разводы», если вы хотите обойтись бесплатным использованием ваших скилов. Чисто технически с DynamoDB еще можно «выехать» на бесплатном тарифе. Но с Firestore я бы точно не связывался. Более того, с учетом того, что все это делается лично для себя, а не с коммерческой целью, сильно завязываться на «зоопарк» облачных сервисов я бы не стал.

В результате я локально поднял Cassandra у себя в Docker. Но с точки зрения common sense в условиях домашних нагрузок можно смело брать и любую другую БД.

Smart Home Action

Здесь процесс аналогичен амазоновскому: регистрация, создание проекта, базовая настройка самого action + oauth. Следует также отметить, что вам выдадут на виртуальный счет $300 на год. Можете делать с ними что хотите. Но рекомендую не подключать лишние сервисы, чтобы в один прекрасный день не получить письмо счастья с кругленькой суммой.

Итак, идем на Actions on Google и создаем новый проект:


При выборе типа проекта нужно обязательно выбрать Smart Home (изменить свой выбор потом не получится):


В процессе quick setup придется заполнить разную информацию. Display Name может быть произвольным. Эта опция нужна лишь для voice / chat bots. Smart Home actions (по аналогии с Alexa Skills) явно не вызываются:


На странице Actions появляется коронная фича Google:

Здесь нужно в поле Fulfillment указать URL к вашему back-end. Для тестовых целей можете воспользоваться ngrok-сервисом для проброса локального порта наружу. Но в идеале рекомендую купить себе домен и SSL-сертификат. Local home configuration понадобится для той самой фичи, которая сейчас находится в preview, — развертывания своего кода на колонке. Но пока я еще не тестировал ее. Потому оставляем поле пустым.

Account linking. Тут все немного сложнее, чем у Amazon. Как я уже упоминал ранее, встроенной поддержки oauth у Google нет. Соответственно, рассматривать мы будем пример с Auth0. Процесс создания приложения выглядит идентичным тому, что мы делали для Amazon. Но есть следующие нюансы:


Allowed Callback URLs должны вести, с одной стороны, на /userinfo, с другой — на ваш Google-проект, который был создан чуть раньше. Ну и Allowed Web Origins должен соответствовать гугловскому oauth redirect URL. Но это еще не все. У Google есть открытый баг, который магическим образом влияет на интеграцию с Auth0. Расписывать его детально я не буду, но суть в том, что refresh token не обновится без задания audience на стороне Google. А Google, в свою очередь, не позволяет никоим образом его задать. Соответственно, единственным рабочим, но костыльным решением стало глобальное добавление audience на уровне самого Auth0: в General Settings вашего API нужно присвоить полю Identifier следующее значение: https://your.domain.eu.auth0.com/api/v2/ (your.domain, естественно, нужно заменить на вашу конфигурацию).

Теперь мы можем обновить action. Помимо client id / secret, остальные поля нужно заполнить следующим образом:


Будьте внимательны. В официальных guides отсутствует offline_access scope, который необходим для корректного обновления refresh token. Сохраняем, теперь наш action готов к использованию.

Отмечу еще одну деталь: в отличие от обычных actions для создания voice / chat bots, вы не сможете протестировать smart home action в эмуляторе. Вам в любом случае понадобится back-end.

Google Smart Home Skill API

По сравнению с Amazon Google API на первый взгляд выглядит намного проще. Хоть идея и аналогичная, с точки зрения реализации обработчиков есть кое-какие глобальные отличия. Следующий маппинг позволит уловить их суть:


У Google API более обобщенный. Например, здесь вы не встретите ярко выраженный Discovery. По сути, Sync handler включает в себя и Authorization, и Discovery, то есть срабатывает он при линковке аккаунта пользователя, но тут же мы обязаны вернуть список доступных устройств в ответе.

Альтернативой ReportState является Query, который также срабатывает при опросе состояния устройства через мобильное приложение.

Пожалуй, наиболее существенное отличие между API Google и Amazon — это набор управляющих обработчиков. Если у Amazon все они разбиты относительно свойств самих устройств, то у Google есть всего один Execute — handler, который срабатывает на любую управляющую команду. Ну а в такой ситуации наша задача немного усложняется, поэтому придется уделить чуть больше внимания вопросу дизайна наших обработчиков.

Буквально несколько примеров по подобию Amazon:


Как я уже отмечал выше, ярко выраженной фазы поиска тут нет, потому о новых устройствах можно узнать либо во время привязки аккаунта через мобильное приложение, либо посредством ручной отправки Sync-запроса. Сам обработчик при этом идентифицируется лишь на основании поля Intent.

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


Сам Execute request при этом стал сложнее за счет ранее упомянутого обобщения.

Custom back-end

Пришло время затронуть тему кода. Ввиду незавершенности нового подхода к организации локального back-end я буду приводить пока лишь code snippets, чтобы ввести вас в контекст. Финальная версия вскоре будет доступна на GitHub.

Я приведу пример с Google, но похожую технику можно применить и по отношению к Amazon. Начнем с контроллера, куда мы приходим с Actions on Google:

@Controller()
@UseGuards(GoogleAuth0Guard)
export class DeviceController {
  @Inject()
  private readonly actionService: ActionService

  @Post('/smarthome')
  public async customActionHandler(): Promise<SmartHomeV1Response> {
    return this.actionService.handleRequest()
  }
}

Здесь всегда будет единая точка входа для любого реквеста. С учетом использования Auth0 мы можем подключить свой Guard для аутентификации пользователя:

@Injectable()
export class GoogleAuth0Guard extends BaseGuard implements CanActivate {
  public get type(): GuardType {
    return GuardType.GOOGLE
  }

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    const host: HttpArgumentsHost = context.switchToHttp()
    const request: Request = host.getRequest()
    const { authorization } = request.headers
    return this.isVerified(request, authorization && authorization.split(' ').pop())
  }
}

Как бы тогда выглядел ActionService?

@Injectable()
export class ActionService {
  @Inject()
  private readonly syncHandler: SyncHandler
  @Inject()
  private readonly queryHandler: QueryHandler
  @Inject()
  private readonly executeHandler: ExecuteHandler
  @Inject()
  private readonly disconnectHandler: DisconnectHandler

  public handleRequest(): SmartHomeResponse {
    return this.handlers.find(handler => handler.canHandle()).handle()
  }

  public get handlers(): RequestHandler[] {
    return [this.syncHandler, this.queryHandler, this.executeHandler, this.disconnectHandler]
  }
}

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

Если, например, мы хотим отдать список доступных пользователю устройств при синхронизации, этого можно добиться путем извлечения соответствующих данных из Cassandra DB по user id.

@Injectable()
export class SyncHandler implements RequestHandler {
  @Inject()
  private readonly deviceService: DeviceService

  @Inject()
  private readonly requestProvider: RequestProvider

  public get name(): SmartHomeV1Intents {
    return Action.SYNC
  }

  public async handle(): Promise<SmartHomeV1Response> {
    const devices: DeviceDto[] = await this.deviceService.getDevicesByUserId(this.requestProvider.userId)
    return {
      requestId: this.requestProvider.request.requestId,
      payload: {
        agentUserId: this.requestProvider.userId,
        devices: devices.map(device => _.omit(device, 'agentUserId'))
      }
    }
  }

  public canHandle(): boolean {
    return this.requestProvider.canHandle(this.name)
  }
}

Подобным же образом реализуются и остальные обработчики. При большом желании API можно продумать так, чтобы он был достаточно универсальным, и вести работу со всеми тремя рассмотренными решениями одновременно.

Демо

В следующем видео вы сможете наблюдать все 3 решения в действии. Сразу хотелось бы отметить, что в случае с Amazon Lambda была уже разогрета, в своем же «велосипеде» — с использованием Kaldi — присутствует определенная задержка в получении final transcribe от сервера.

Эпилог

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

Эта таблица подводит итог по вышеизложенному материалу.

PointEcho Dot (3rd Gen)Google Nest MiniCustom Bicycle
Price$49.99$49~= Microphone price
Microphone42-
Speech to TextBuilt-inBuilt-inKaldi ASR
Text to SpeechBuilt-inBuilt-inMarry TTS
Custom Commands--+
UA/RU Language--+
FrontendAlexa SkillsActions on GoogleAny
BackendAWS LambdaAnyAny
Local DeploymentAVSDev Preview+
AuthOAuthOAuthAny
Wake WordFixed — AlexaFixed — Hey/OK GoogleCustom — Snowboy
Far FieldBuilt-inBuilt-in-
Public APIGranularGenericLow level
Smart Home SDKAVS onlyLocal Deployment Only-
Speaker RecognitionIn-app Training (no API yet)In-app Training (no API yet)-
Programming LanguagesJS, Java, Python, C#, Go (AWS Lambda), C/C++ (embedded)Any (backend), JS (local deployment), C/C++ (embedded)Any (backend / frontend), C/C++ (embedded)
Setup ComplexityHighHighHigh

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

А вот готовые решения, на мой взгляд, не смогут набрать серьезные обороты, пока английский язык не станет одним из обязательных у нас в стране. Все-таки ни украинский, ни русский пока не поддерживаются ни одним из лидеров рынка. И каких-то конкретных обещаний в этом направлении пока не слышно.

Для англоговорящей части населения все сводится к следующему выбору.

С одной стороны, можно купить готовое устройство с поддержкой Alexa/Google и уповать на удачу. Ведь, если исходить из рейтинга скилов многих вендоров, люди очень негодуют от того, что их устройства либо не обнаруживаются, либо глючат при голосовом управлении. В целом это неудивительно, если принять во внимание рассмотренные выше проблемы.

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

В целом, если у кого-то есть идеи, предложения и желание развивать это направление у нас в стране, пишите.

LinkedIn

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

Лучшие комментарии пропустить

Цікаво, але в підсумку я, здається, зрозумів, що голосове керування мені не так вже й щоб і потрібне було

89 комментариев

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Так и не понял, почему вместо Sonoff было использовано радиореле, хотя он всего ли не намного больше по размерам, если снять коробку. Зато в нем есть ESP8266, который можно перешить как угодно. И самое главное, из коробки Sonoff поддерживается и Google Home и Alexa.
Плюс еще есть врезные выключатели с управлением по WIFI, которые можно вставить вместо обычных выключателей. И не нужно городить огород с накладным выключателем+радиореле. И опять же из коробки поддерживаются Google Home и Alexa.
И вообще Google Home поддерживает кучу сторонних устройств, в том числе всякий ноунеймов из китайских подвалов. У меня несколько дешевых контроллеров LED-лент управляются из Google Home.
И последнее, Google Assistant поддерживает русский язык. Пусть и в бета-режиме, пусть пока не так четко, как английский, но работает.
В результате можно купить красивые колонки с Google Assistant (не обязательно самого Google, у меня, например, JBL) в каждую комнату, готовые выключатели/smart-розетки/контроллеры, настроить за 10 минут и все готово. Причем сделать это может практически любой технически подкованный пользователь, а не продвинутый iot-разработчик.
В общем смысла статьи так не понял.

Хоть в статье это и было довольно прозрачно описано, но повторюсь: Sonoff требует прямого подвода к фазе. Если у вас к выключателям подведено питание — вопросов никаких нет. В противном, и довольно таки распространённом случае, ремонты частенько делают с учётом экономии проводов. Посему, чтобы добраться от выключателя до фазы в коробке, нужно продолбить стену и в последствии переклеить обои. Ну и делать это придётся во всех комнатах. Очень дорогой выйдет выключатель, не так ли? Да и вообще, это немного разные весовые категории. Сравнивать Соноф с реле за 2 бакса вообще бессмысленно. По большому счету, если есть желание и возможность покупать колонки по 300 баксов в каждую комнату, и прочие дорогие свистелки, то эта статья определенно не будет воспринята всерьёз. Касательно Google Assistant, JBL и русского языка: тут я уже не совсем понял, где в этом сетапе массив микрофонов? Более того, готовые решения не зря ведь сравнивались с Kaldi, ибо Kaldi можно развернуть у себя дома. А это означает то, что даже без интернета лампочку можно будет зажечь голосом, в отличие от облачных решений. В этом то и была ключевая задумка при изначальном рассмотрении данного подхода.

Если у вас к выключателям подведено питание — вопросов никаких нет.

У меня выключатели разрывают фазу. Да и какое это имеет значение, если планировалось реле прятать в плафон:

Это радиореле, которое может быть спрятано где-то на подходе (к примеру, в полом плафоне).
Сравнивать Соноф с реле за 2 бакса вообще бессмысленно

6 баксов за Соноф и 2 за радиореле — это такая большая разница?

По большому счету, если есть желание и возможность покупать колонки по 300 баксов в каждую комнату

Google Home Mini можно было на распродаже купить по $25. Тем более подход с кастомными скилами подразумевает наличие либо Google Home либо Alexa. Они есть в диаграммах и в видео. Так что их все равно для этих случаев покупать.
Для решения с Kaldi тоже прийдется покупать сервер, который должен работать круглосуточно. Сколько прийдется еще потратить на электричество?

Касательно Google Assistant, JBL и русского языка: тут я уже не совсем понял, где в этом сетапе массив микрофонов?

Вот здесь я уже не понял при чем тут массив микрофонов. Я про Google Assistant, JBL и поддержку русского языка написал в ответ на вашу фразу:

Все-таки ни украинский, ни русский пока не поддерживаются ни одним из лидеров рынка. И каких-то конкретных обещаний в этом направлении пока не слышно.
А это означает то, что даже без интернета лампочку можно будет зажечь голосом, в отличие от облачных решений. В этом то и была ключевая задумка при изначальном рассмотрении данного подхода.

К сожалению, о том, что это ключевая задумка, в статье явно нет ни слова и рассматриваются два варианта облачных сервисов. Тогда может наоборот нужно было показать возможности локального разворачивания сервисов Amazon и Google?

Мне кажется мы говорим о разных вещах. Какой это Sonoff выключатель стоит 6 баксов? Даже на aliexpress сенсорный двухкнопочник стоит около 20. В Украине ± такой же ценник. Или речь все же о голой WiFi-driven плате, требующей постоянных 5-24В?

По поводу 300 баксов и прочих дорогостоящих устройств: это был пример похожих сравнений из комментариев, когда речь шла об Apple, Lutron т.п. Каждый сам для себя выбирает оптимальный вариант по карману и удобству.

По поводу русского языка: у нас тут какой-то замкнутый круг образуется. Я писал о его поддержке в контексте умных колонок и smart home скиллов. Причем тут Google Assistant? Это лишь сервис. Его нельзя рассматривать в изоляции, ровно как и Kaldi. Вы же не будете осуществлять голосовой ввод через мобильный телефон? Потому я и намекнул о микрофонах.

Разворачивание Kaldi локально безусловно потребует сервера. Но к счастью, совсем недавно выпустили версию, оптимизированную под RPi. Так что уже можно начинать тестить на 4й версии.

Касательно локального разворачивания решений Google / Amazon и сравнения с Kaldi. В контексте офлайн работы все равно победа будет за Kaldi. Независимо от сетапа, оба провайдера при любых раскладах будут ходить для транскрайба на облачный Speech-to-Text. Посему, в статье преимущество локального деплоя рассматривалось в контексте latency и избавления от лишних облачных звеньев. Но полной независимости от интернета, к сожалению, все равно не удастся добиться.

Но полной независимости от интернета, к сожалению, все равно не удастся добиться.

Почему?

Распознавание речи в облаке будет осуществляться.

Зачем в облаке?
Потому, что облака — это святое и без них нынче нельзя, иначе поругают?

По-моему, мы друг друга не поняли. Я говорил о распознавании речи в облаках в контексте Гугла и Амазона.

Как бы не поняли, это да.
Сейчас у вас у всех нет другого варианта, как платить за облака. Такова жизнь.
И вас нет денег, чтобы сделать что-то свое и качественное в рамках текущих потребностей.
Но, мы в жопах мира должны понимать, что мы говно и у нас нет возможностей сделать не говно и весь наш выбор ждать, когда гугол и подобные снизойдут до говна.

Мне кажется мы говорим о разных вещах. Какой это Sonoff выключатель стоит 6 баксов? Даже на aliexpress сенсорный двухкнопочник стоит около 20. В Украине ± такой же ценник. Или речь все же о голой WiFi-driven плате, требующей постоянных 5-24В?

Я говорил о вот таком реле www.aliexpress.com/item/4000017013908.html, вот даже по 5 баксов. И никаких 5-24 В не нужно.

По поводу русского языка: у нас тут какой-то замкнутый круг образуется. Я писал о его поддержке в контексте умных колонок и smart home скиллов. Причем тут Google Assistant? Это лишь сервис. Его нельзя рассматривать в изоляции, ровно как и Kaldi. Вы же не будете осуществлять голосовой ввод через мобильный телефон?

Google Assistant это не сервис, а технология, которая лежит в основе как телефонных приложений, так и смарт-колонок. Причем как гугловских, так и сторонних. И по функционалу они в основном одинаковые, только гугл на свои продукты как обычно новые фичи выкатывает быстрее, а некоторые, как например совершение звонков, может запрещать сторонним производителям. Но тем не менее, мы это позволяет управлять голосом умным домом абсолютно одинаково как с телефона, так и со смарт колонки, что я собственно говоря и делаю. Поэтому можно поставить в каждую комнату колонку за 25$ и будет голосовое управление умным домом из любой его точки.
Более того, это даст все возможности Google Assistant, а не только управление умным домом. Не буду перечислять все, но это как минимум на порядок больше возможностей.
А еще Google Assistant позволяет отличать голоса пользователей и благодаря этому менять контекст. Поэтому, когда я и мой сын дают команды на проигрывание музыки, то музыка играет разная. Этот момент вообще в статье не освещен, хотя он конечно не имеет сильного значения в контексте управления умным домом.

Разворачивание Kaldi локально безусловно потребует сервера. Но к счастью, совсем недавно выпустили версию, оптимизированную под RPi. Так что уже можно начинать тестить на 4й версии.

Так у вас там кроме Kaldi есть Cassandra на Java, да еще и в докере. RPi 4 все это потянет?

В контексте офлайн работы все равно победа будет за Kaldi. Независимо от сетапа, оба провайдера при любых раскладах будут ходить для транскрайба на облачный Speech-to-Text.

Вот именно этот момент и интересен, и как раз это и хотелось бы увидеть в статье. Потому как в Android как минимум есть Offline Speech Recognition и мне кажется ваше утверждение спорно.

Калди это всего-лишь набор приплюсных лид и аппок их дергающих.
Обучение делается скриптаами на баше, что дергают аппки консольные
Ну а распознавание на обученных моделях уже можно как аппкой, так и либы подергать нужные.
Более того процессоры приличные там нужны только для обучения, а для распознавания и пенек первый сгодится. Так что RPI потянет без проблем.

Я не про это, а про Cassandra в докере. Но сейчас посмотрел, она используется только в решениях с кастомными скилами. Так что да, RPi4 должно хватить.

Для калди это нафиг не нужно. Но сам движок очень не простой и учить его сложно.
Для английского и испанского более ни менее приличные модели для него уже есть. Для остальных языков фришного ничего нет.
И тут еще такой момент, что языковые модели английского и русского-украинского сильно разные. Поэтому придется большую часть стандартного для английского подхода переделывать по русский, украинский.
А еще базы собрать.
В общем работы очень много.

Короче говоря, пока что мечта ТС о голосовом управлении домом на русском языке своими руками так и остается мечтой. И тогда подход с Kaldi на практике имеет еще меньше смысла.

Ну почему же? В статье ведь приводится пример с русской моделью. И как раз эта версия уже появилась в оптимизированном варианте. Как решится проблема с микрофонами, сразу начну тестирование.

Ничего не начнется, пока гугол о вас не вспомнит.

Да. В варианте честного ответа от подвипившего.
Но, если включить неокортекс, то есть два варианта.
1. Ждать, когда гугол таки вспомнит об украинском и платить за облако.
2. Поить меня пивом (от 3 баксов за поллитра и сигара в неделю, предпочитаю доминикану) и я сделаю вам украинский — это 2-3 года минимум.

P.S. Во втором варианте за 20 лет не нашлось ни одного человека во всем постсовке, включая и хохлов. Либо хотят божественно решение и вчера и либо давай вместе пилить госбабло (сделать категорически нельзя ничего работающего). Ты не представляешь сколько я говна отгреб, что сделал работающий синтез русское речи в ЦРТ. Это одна из причин посему я к речи сейчас только при оплате от 50 баксов в час сунусь и не меньше. Мне приятнее над всеми славянскоязычными издеваться (они просто тупые обезбяны).

Я боюсь, что в таком активном режиме, через года 2-3 мы наврядле получим результат :)))))

А я могу гарантировать результат, если не выйдет могу почку отдать. Но помимо пива мне вам придется тратиться и на сборку речевых баз и на обучение умных студней.
И вариант будет или мы это делаем и вы платите бабло или идете на хутор за бабочками и мы прекращаем извращения.

Я понял, вариант 1, ждем гугл :)

5-7 лет назад тут эта тема уже была. Пачка упоротых побредила и таки 5 лет ждали гугла. Еще 5-7 лет подождать и гугл всё сделает. А если не сделает можно еще лет 5-7. Всё одно мы тут слишком тупы (от разработки до бизнеса), чтобы что-то сделать.

Вот я не туп в разработке, но абсолютно тупой во всем, что касается бизнеса.

Ну русский-то мы получили. Глядишь скоро будет и украинский.

Он уже есть (ЦРТ питерский), но за бабло, русский тот.
Да, переход на украинский там элементарен и прост (нужно только базы собрать).
Но нахрена это нужно ЦРТ?

Насчет реле: выглядит интересно, спасибо! Но как именно оно интегрируется с Алексой и Гуглом. В описании нет не слова об этом. Где задается friendly name, на которое откликается устройство? Источников света то может быть и несколько в комнате. Или все контролируется через некий «Smart Life» app? Если так, то это как раз больше всего и смущает меня в подобного рода решениях: когда я не понимаю, кому отдаю контроль. В случае с Гуглом или Амазоном я не переживаю, ибо это зарекомендовавшие себя корпорации. Но когда появляется дополнительное китайское облако... Скажу по опыту с Xiaomi, у которых тоже есть интеграция с Alexa / Google: девайсы частенько не обнаруживаются и глючат при голосовом управлении. А что будет если китайское облако ушло в нокдаун? Девайсы попрежнему будут работать? В общем, вместо того, чтобы избавляться от лишних звеньев, мы их добавляем в случае интеграции с 3rd-party провайдерами.

Возвращаясь к более глобальным вопросам. Вы не первый, кто цепляется за лампочку, приведенную в качестве hello-world примера. Давайте возьмем более сложные кейсы: кондиционер без WiFi, светодиодная лента с неизвестным протоколом, не smart ТВ. По сути то, что можно спокойно встретить в произвольном доме или квартире нашей страны. Каким будет ваш подход?

А насчет Google Assistant и смарт колонки можно подробней? Каким образом осуществляется подвязка и голосовая идентификация устройств без smart home actions?

По RPi 4 ресурсам пока не могу сказать. Как приобрету и протестирую, тогда можно будет обсудить.

Насчет Offline Speech Recognition: тут же дело не просто в получении текста, а в распознавании базового контекста. Все эти умные колонки то рассчитаны на более широкий круг задач. Не только smart home. Сейчас вам захотелось включить лампочку, через минуту — рассказать сказку ребенку. Дабы понять ваше намерение, Амазону и Гуглу придется весь post-processing и intent detection переносить в офлайн, а не только speech recognition. С Kaldi все гораздо проще, ибо мы сами ограничиваем контекст его использования.

Но как именно оно интегрируется с Алексой и Гуглом. В описании нет не слова об этом. Где задается friendly name, на которое откликается устройство? Источников света то может быть и несколько в комнате. Или все контролируется через некий «Smart Life» app?

Вся интеграция Гугла со сторонними устройствами выглядит одинаково и кстати описана в вашей статье. Есть приложения вендора, специфические для каждого устройства, с помощью которых происходит обнаружение и настройка устройств. Также они обеспечивают специфические функции, например мигание светодиодной ленты в такт музыки с телефона. Дальше есть вендор-облако, в котором живет это устройство и фактически агент, который реализует требуемые Гугловские/Амазоновские контроллеры. Вам нужно из Google Home авторизоваться в облаке вендора, а дальше все как у вас описано — авторизационный токен есть, адрес сервера с агентом известен, туда можно отдавать команды.
Да, согласен, это дополнительное звено. Но во-первых, за легкость интеграции приходится чем-то платить. Во-вторых, конечная скорость реакции на практике меня вполне устраивает. Это не та операция, которая требует миллисекундных реакций :)

Давайте возьмем более сложные кейсы: кондиционер без WiFi, светодиодная лента с неизвестным протоколом, не smart ТВ. По сути то, что можно спокойно встретить в произвольном доме или квартире нашей страны. Каким будет ваш подход?

Sonoff — если просто вкл/выкл или Wi-Fi->RF/IR шлюз, если нужны доп. команды. Все приведенные устройства включаются в сеть и имеют как минимум IR пульт. У ленты может еще быть RF управление.
Для ленты можно еще поменять контроллер на смарт. Я себе 4 года назад делал ремонт и еще таких не было, теперь потихоньку меняю, стоят они по $7.
К телеку можно подключть Chromecast и если у вас он не совсем древний и поддерживает HDMI CEC, то из Google Home можно не только включить телек, но и попросить проиграть музыку или видео.

А насчет Google Assistant и смарт колонки можно подробней? Каким образом осуществляется подвязка и голосовая идентификация устройств без smart home actions?

Ответил выше, в вендорском приложении вы точно так же указываете имя устройства.

Насчет Offline Speech Recognition: тут же дело не просто в получении текста, а в распознавании базового контекста. Все эти умные колонки то рассчитаны на более широкий круг задач. Не только smart home. Сейчас вам захотелось включить лампочку, через минуту — рассказать сказку ребенку. Дабы понять ваше намерение, Амазону и Гуглу придется весь post-processing и intent detection переносить в офлайн, а не только speech recognition.

В описанных вами случаях контекст как раз и не нужен. Команды независимы между собой. Контекст нужен для так называемых «continued conversation», когда вы спрашиваете «Кто такой Илон Маск?», а после ответа спрашиваете «А сколько ему лет?». Такое может пока только Google Assistant и то только для US и только для гугловых продуктов. А так для ассистанта контекстом является голосовой профиль пользователя, и в рамках уже его будут подбираться какие-то предпочтения.

Ну вот как раз доп. звенья мне и не очень нравятся во всех этих схемах. И дело даже не только в реакции. Устройств много, разные вендоры, разные приложения, отсутствие полного контроля. Я бы никогда не доверил, к примеру, манипуляции со входной дверью или окнами сторонним приложениям. Правильно тут где-то в комментах высказывали мнение насчет отвязки от вендоров, унификации всего и т.п.

По поводу распознавания: я так и не понял вашего мнения насчет вынесения всего этого дела из облака в офлайн. Как машина различит, к примеру, «turn on the light in the kitchen» и «how can I turn on the light in the kitchen/car/plane» без пост-обработки? Что есть — general question, а что относится к управляющим командам в контексте умного дома? Тут как минимум должен включаться какой-то хороший классификатор для сужения области поиска до контекста конкретного домена. И если контекст, к примеру, умного дома, то фразу еще придется разобрать по кусочкам: intent detection, parameters extraction для определения устройства (поиска его идентификатора в home graph, если речь о Гугле) и управляющей команды. Да, и давайте не забывать еще об oauth. Как по мне, то тут вообще без шансов увидеть все это дело в офлайне. Ни один из провайдеров не вынесет свою базу знаний в офлайн. Даже со стратегической точки зрения это невыгодно, ибо им постоянно надо совершенствовать свои модели на наших данных.

Как машина различит, к примеру, «turn on the light in the kitchen» и «how can I turn on the light in the kitchen/car/plane» без пост-обработки?

Список команд ограничен и каждая команда имеет определенный формат. Список устройств также доступен локально. Для нативных устрой не вижу ничего сложного. Управление сторонними устройствами действительно маловероятно.

Кстати, нашел выключатель, который давно искал. www.aliexpress.com/item/32974697281.html есть вариант для питания только по фазе, т.е. им можно заменить любой выключатель, даже в домах со старой проводкой под свет, где подведена только фаза. Xiaomi Hub может управляться не только через GA или Alexa, но и из локальной сети. Так что часть проблемы можно решить уже готовыми продуктами из коробки.

Сергій, велике СПАСИБІ за круту статтю! Ну і звісно, дуже приємно бачити PlatformIO, особливо що у нас хтось його юзає.

во втором — неожиданно убогий и баговый. Если сравнивать исключительно две эти IDE, то для меня CLion выглядит более функциональным

Ми працюємо із JetBrains над офіційний додатком до PlatformIO. Прогрес можна глянути тут github.com/...​atformio-core/issues/2201

Уже є нова збірка плагіна, але він потребує дев.версії PlatformIO Core. Там ще краща інтеграція в дусі JetBrains продуктів. Якщо що цікаво «пощупати» — відпишіться у github.com/...​atformio-core/issues/2201 і я опишу більш детальніші інструкції.

Regards, Ivan @ PlatformIO

Спасибо за крутой продукт и работу над официальным CLion плагином! Вроде в последнем комментарии к issue описаны достаточно подробные шаги по установке. На днях попробую и отпишусь.

Все-таки ни украинский, ни русский пока не поддерживаются ни одним из лидеров рынка. И каких-то конкретных обещаний в этом направлении пока не слышно.

На базе Калди можно сделать украинский, но это 2-3 года работ, 3-4 человека специалиста (это на основе того что сербские ребята делали для сербского языка — 4 реальных пхд). Собрать базу, разметить (для калди это проще, чем для HTK или сфинкса). Вероятнее всего помодифицировать некоторые ее модули, обучить (1000 итераций обучения с фиксами различными).
За основу для старта работы берутся аудиокниги, но дальше придется собирать базу в реальных условиях юзания.

Увы, но аудиокниги на украинском имеют не так много общего с тем суржиком, на котором общается половина «украиноговорящего» населения страны. Это, разве что, Подеревлянского привлекать...

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

Ждите, когда гугл или эпл доберутся до украинского. Или ищите финансирования на человек 10 на лет 5 (в среднем рассчитывайте на 2500 чистыми в месяц этим работникам).
2500*10*12*5 = 1500000.
Ну или запускайте рекламу, что вот украинцы на том сайте начитывайте тексты, что мы показываем и так собирайте базу. При правильной и дорогой рекламе хомячки начитают.

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

Сбор базы — это 70% разработки в подобных вещах.

А с ютюба нельзя? Если есть базовая распознавалка и семантический анализатор, то можно находить проблемы в семантике и фиксить распознавание руками, а остальное — считать распознанным.

Вот и получатся у тебя 10 человек на 5 лет.

Это шапками кого закидать легко, если шапок много, а тут все другое. Без денег и времени ты просто ничего толком не сделаешь, а времени потратишь кучу.
И вот вначале берешь аудиокниги, затем сайт для реальной начитки, затем уже ютюб.
Но если денег много и ты можешь нанять много аннотаторов, то получится сильно быстрее (можно и в 1-2 года вписаться). Кроме того наймешь пачку спецов на разные части системы распознавания.

Да все это я уже тут писал лет 5 назад, сейчас по кругу повторяю. Думаю, через 5 опять повторю, а там уже и гугл сделает и будете юзать. Так что всего-лишь подождать, когда «Америка поможет».

При наличии дома старшего (пенсионного возраста) и младшего члена семьи голосовое управление, в том числе выдача им вербальных команд по телефону с их распознаванием и интерпретацией, не требует затрат на их автоматизацию. Так же, они поддаются вербальному программированию на алгоритмические действия по ситуациям, для самостоятельного принятия решений на действия, с обратной связью по достигнутым результатам в критических ситуациях. Даже соседки бабушки-одуванчики поддаются прошивке на связь по мобильным устройствам, если что случается.

Кажется, я начинаю понимать принципы работы ИТ-департаментов наших госкомпаний.

Если инженеру хочется поинженерить — его ничем не остановить!

А с микрофонами. Можно натыкать море (пару сотен) дешевых чувствительных электретных и потом рассчитать микрофонную решетку для сложного помещения и всего стада микрофонов.

О как. а показать как рассчитать решету для сложного помещения? а то пока мышки станьте слониками.

Показать тут не смогу (приличная база в ЦОС нужна), тебе придется кучу учебников прочитать и понять их, а рассчитать смогу, но не даром. Для даром, после нет мест тут куда эти расчеты можно еще продать, а работы много.
Есть пакеты для расчета уже готовые, в частности в матлабе, но все они требуют знаний и понимания сути. А да понимай и знания потребуется не меньше, чем для того, чтобы качественно обучить калди.

И добавлю, давно уже это не делал и нужно будет кучу вспоминать. Так что без приличной оплаты даже не сунусь в это. Потрачу кучу времени на подарок тебе, а после воткну его себе в жопу, ибо больше никому не нужно.

Я баловался как в Monocular SLAM для оптики и в DOA для решеток так что тож добровольно не сунусь. Но просто так с легкостью сказать про рассчитать, да ладно рассчитать, обрабатывать с пары сотен я как обалдел. Ну удивил то

Сказать всегда легко, но не сделать. Вся математика давно в книжках есть, железо тоже. Работы много.

Эт точно. А как известно работа нас любит )

А в антенной решетке еще нужно на каждый микрофон на бакс повесить ЦАПы по 10-20 баксов с обвязкой. А в случае решетки этого всего много. А это еще цепи питания, помехи.
А посчитать... 1-2 месяца вспомнить теорию, 1-2 недели запрограмить, юзая готовые пакеты и либы и 30 мин посчитать.
И это еще без учета того, что всё эта хрень сразу не запуститься и потом долго фиксить баги и железе и в софте.

И в итоге подобным могут развлекаться только богатые конторы.

Буквально пару дней назад набрел на такой девайс: store.matrix.one/...​2-wifi-bt-microcontroller. Хочу заказать для теста. К слову, скоро (до НГ) от alphacep появится новая Kaldi модель, заточенная под RPI.

Не уверен, что для голоса в твоем случае тебе это подойдет. да, По сути это направленный микрофон, но его диаграмму на сайте там я не увидел. С другой стороны направленные микрофоны и дороже сильно стоят.
И да, я немного постебался с микрофонной решеткой. Ее рассчитать и в реалтайме обрабатывать сигналы — это много работы и эмбедерской и програмерской.
С решетками есть 2 варианта работы. Рассчитать решетку под конкретные требования и рассчитывать сигналы в текущей конфигурации микрофонов (в том числе и меняемой).
Но, если тебе это интересно, почитай про решетки, попробуй, может что и получится.

С Калди я игрался плотно как-то месяца 4 плотно. Сложная штука, попроще всех предыдущих, но все равно сложная, если самому обучать. Но из опенсурсных это лучшее, что есть на данный момент по распознаванию речи.
Есть еще вот такое от мордокниги github.com/...​ebookresearch/wav2letter. Там тоже достаточно интересный подход, но про качество не могу сказать, не проверял.

С другой стороны, если вдруг найдешь инвесторов, то могу поиграться с микрофонными решетками. Там много работы от подбора микрофонов (дешевых) до эмбединга (много каналов и АЦП нужны) и математики с программингом.

Ох, блин. А ты крут. Столько работы сделать — это же долго и очень было.

Но есть несколько вопросов.
На какой базе учил Калди для украинского или русского?
Какой WER получил?

И как разруливаешься, если рапознавалка воспримет «включить», вместо «выключить». Это уже вопрос безопасности, например не выключит утюг, а включит.

Я думал, что за Kaldi уже никто и не спросит. :) В конечном итоге я взял готовую русскую модель от alphacep: alphacephei.com/...​kaldi/kaldi-ru-0.6.tar.gz. Она же встроена и в докер образ (линка в статье была). База там закрытая. Автор утверждает, что обучали они ее на 1к часов записей. Их демку можно потыкать тут: alphacephei.com/ru. WER в контексте умного дома я не проверял. Все же тут нужна хорошая разнородная выборка. На глаз, в идеальных условиях с определением приставок / корня проблем не было. А вот на окончаниях уже появлялось много неточностей. К примеру: выключив, выключал, включим — вот такое частенько проскакивало. Потому я дополнительно подключал библиотеку для сравнения строк. В целом, задав определенный порог similarity, проблем с интерпретацией не возникало. Но вот замечание по безопасности весьма резонное. В самом базовом случае надо анализировать текущее состояние устройства. Причем, на нескольких уровнях. Основная проблема с обычными устройствами в такой схеме в том, что они не дают никакой обратной связи. Взять ту же лампочку. Ну отправим мы допустим радиосигнал на реле. Но без доп. телодвижений мы никак не поймем, а включилась ли она на самом деле, или нет. Сигнал мог не дойти, реле могло сломаться, лампочка могла перегореть. У меня были идеи насчет доп. датчиков освещенности и тока/напряжения, которые будут независимым образом собирать информацию о текущем — реальном — состоянии устройства. Тут, как мне кажется, надо рассматривать каждый девайс индивидуально. И его влияние на окружение. В случае наличия вероятности спалить квартиру, нужно много перестраховок. Начиная с common sense checks: утюг уже включен, и если юзер опять его включает, то вероятно он имел ввиду совсем другое, значит надо переспросить. Или такое: утюг долго включен, свет уже выключен / на дворе ночь — надо спросить, все ли в порядке. Нет ответа — обрубить питание на розетке.

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

Но с другой стороны, она не выключит свет, а включит — это будет просто раздражать.
С тем же светом. В комнате часто много источников, и какой включить, какой выключить не тривиально .

И с обратными связями — их много где можно сделать, но все они усложняют и удорожают систему.

Но, я тобой восхищен. У тебя хватило сил и терпения сделать такую нехилую и навороченную систему, как хобби. У меня бы на столько терпения и сил не хватило бы.

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

З.Ы. Если что по звуку и его обработке подсказать, обращайся, попытаюсь подсказать (личка или линкедин). Сейчас я правда от звука и речи в визуальные образы перешел — в них легче работу находить.

Насчёт света: если обучать реле в режиме interlock, то мы защищены от ложных срабатываний. Если ASR услышала одно, а реле уже находится в этом состоянии, — то ничего не произойдёт (кроме неверно записанного стейта в БД, и если нет доп. источников определения состояния). А вот с self-lock — да, любая неправильно интерпретированная команда уже может очень сильно раздражать.

По разным источникам: у меня в одной из комнат и основной свет, и светодиодная лента подключены по такой схеме. Это ещё не беря во внимание кондиционер. Я им просто дал разные friendly name. За год может пару раз было такое, что колонке послышался другой источник. Но это надо очень невнятно или тихо говорить. Ну и само имя должно быть очень похожим.

По звуку: буду иметь ввиду, спасибо!

Я просто напомнил о проблеме безопасности в случае ложных срабатываний. Универсального решения я и представить не могу.
И да, я бы на твоем месте, раз ты так уж глубоко и упорно в своем хобби подумал об обратных связях, даже в виде дешевых датчиков (фотодиод на освещение, датчик воды на воду и т.п.).

Вероятности-то по безопасности посчитать можно (и при производстве подобного регуляторы такое потребуют), но это море работы и дорогой.

И по звуку. Попробуй, если не сделал, просто резать фильтром простым на 300-3200. Может чуть улучшиться распознавание. Но если на 11025 пишешь, то смысла нет, там и так режется около 4000 сверху (каждое устройство по своему, но все до найквеста).
Фазовую характеристику для речи можно не беречь, поэтому фильтры простые годятся.
Для распознавания важны первые две форманты, а они до 2000 почти всегда.

Цікаво, але в підсумку я, здається, зрозумів, що голосове керування мені не так вже й щоб і потрібне було

Так, для того, щоб аж настільки заморочитись, окрім практичних міркувань потрібно ще мати неабияку цікавість. У мене одразу виникла думка, що такий проєкт було б непогано провести разом зі своїми дітьми. Воно і самому веселіше, і їм цяцька. Ще й навчаться чомусь.

нормально заходить, якшо не таких довгим і складним шляхом. «алекса, вимкни філіпс хьо в спальні» — 5 хв роботи (розбирання як налаштувати з нуля) якшо нічого не паяти і не програмувати

Ну philips hue объективно сложно рассматривать всерьез, ибо ее уже давно хакнули: bitbucket.org/xoseperez/fauxmoesp. И там ни скиллов не надо писать, ни сервисов. Более того, за эту лампочку можно выдать абсолютно любой девайс, если его нужно только включать / выключать. А так — да, с готовыми умными устройствами ничего программировать / паять не надо. Но ведь статья была совсем не об этом. Те, кто захотел проникнуться всеми скрытыми посылами, — это уже сделали. Точку зрения не воспринявших я тоже прекрасно понимаю и ничуть не осуждаю. Каждый выбирает для себя то, что ему по душе.

ее уже давно хакнули:

прошивки оновлювати не пробували? я наприклад регулярно оновлюю кожну лампочку чи вимикач. при тому що вони наразі встановлені в 1600 кілометрах від мене. стаття несе трошки і негативних посилів. зокрема «все це складно і виключно для гіків»

я не поливаю статтю брудом. просто хотілося б щоб люди розуміли шо це не «зе онлі вей» і існують простіші (і навіть дешевші чим хью) рішення від великих виробників. айкія наприклад

Light Commands: Laser-Based Audio Injection
Attacks on Voice-Controllable Systems*
lightcommands.com

Спасибо за статью!
Наверное в ней есть зерно, но вот настолько ли рациональное ? и не стреляет ли автор в ноги себе и другим....

Давайте взглянем на «проблематику»
(Позволил себе убрать литературные обороты)

Когда и зачем нам может понадобиться голосовое управление? Я приведу буквально несколько примеров,

Ситуация номер один: .....skipped... Вы направляетесь на кухню, включаете свет, набираете печенек/конфет/[ваш вариант], наливаете чай и направляетесь к выходу по своим делам.
Дойдя до двери, вы понимаете, что надо выключить свет. .....skipped...

Ситуация номер два: .....skipped... хотели посмотреть его по телевизору. .....skipped... и усаживаетесь поудобнее .....skipped... а пульта-то нигде нет!

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

Как видно проблемы все таки две
1) забытый/невключенный свет
2) потерянный пульт от ТВ

Вот что на практике выходит:
1. Освещение.
Первое что нужно сделать — сменить на LED, экономия, выбор, и более долгий срок службы, окупаемость плохая — но мы не об этом.
Ходить по помещению и раздавать команды — забавная картина, особенно если мы про дом: в каждую комнату по колонке, питание, центрально место для нее. Такое себе удовольствие

У меня много претензий к голосовому управлению, поэтому остановился на продукции компании LUTRON. Эстетично, надежно и недешево.
Сейчас меня не парит если дочь забыла свет в ванной — при отсутствии движения он будет выключен. Я захожу в гараж и если темно — свет будет включен мгновенно. Мне не нужно включать внешнее освещение — от заката до рассвета с корректировками будет вкл/выключатся.
Если где то горит свет — в приложении это будет корректирутся . Управление светом у димминг по умолчанию.
Да у меня есть Алекса и она даже что «то умеет», но мне нужен «умный слушатель» в в каждом сортире или ванной.

2. Все смарт TV имеют встроенное голосовое управление. Или мы автоматизируем что то доисторическое

Проблема освещения давно решена производителями и успешно.

Вот что бьло б интересно так это внешние датчики
— температура (по комнатам, внешняя и интеграция с HVAC )
— влажность воздуха
— влажность почвы ( для авто полива )
— расход воды по потребителям (кухня, ванны, полив тд)
— расход электроэнергии по потребителям

— влажность почвы ( для авто полива )

Датчик центы стоит, заюзать его 10 мин работы. Сложность в создании системы полива с подачей воды, автоматизированными кранами. Вот эта часть затянет на кусок баксов только для пары горшков с цветами.
Но если у тебя большая теплица для вырашивания марихуаны, то оное уже окупится и имеет смысл.

Зональная система полива с контроллером уже есть. Контроллер берет данные о погоде и расписание для полива. Иногда забавно — идет дождь и работают спринклеры. Да и почва в разных зонах различается. Поэтому самый точный выриант полова был бы Основан на уровне влажности.
От нерационального полива в мес можно терять сотню баксов легко

Датчик около бакса стоит, ардуинка 3 бакса, ну может еще релюшка, чтобы включать и выключать полив. Всё вместе собрать и 10 строк кода в ардуинку написать — это максимум день работы, если уж совсем медленно делать.

Зачем же ардуинку, уже есть ESP.

Да пофиг — суть одно и то же.

Вот что бьло б интересно так это внешние датчики
— температура (по комнатам, внешняя и интеграция с HVAC )
— влажность воздуха
— расход электроэнергии по потребителям

fibaro + netatmo + z-wave крани на батареях та вимикач для рекуператора, розетки з вимірюванням споживання. це то шо працювало. наступним було по плану керування кондиціонером через інфраред пульт з зетвейв (пару десятків баксів на амазоні). але завів трактор і все спинилося

в каждую комнату по колонке, питание, центрально место для нее

можно сидеть ровно на попе и команды раздавать с телефона www.youtube.com/watch?v=mca2cpnWW6s, умные колонки не нужны. Таскать телефон с собой, так он всегда таскается.

Чтобы включить свет зайдя в комнату — таскать телефон ? Не проще уж руку протянуть ?

Каждому свое, включить свет можно и не таская телефон, до этого момента. Но вообще для этого существуют датчики присутствия, не путать с датчиками движения. Коридор и ванная у меня снабжены такими, рефлекс поднятия руки еще месяц после установки срабатывал))

С этого и начался вопрос — голосовое управления светом имеет мало практической пользы в быту в отличии и решений на occupation/vacation датчиков

Да будет так.

Только цены этих двух видов датчиков отличаются в несколько раз. И если датчики движения и в каждом магазине, то присутствия еще найти суметь нужно в продаже в наших задницах мира.

@SergeyKorol Спасибо за статью и спасибо за встречу. Я там тоже был. Мне очень понравилась организация мероприятия doed.nure.ua/...​api-vid-waverley-software

Спасибо и вам за участие! Рад, что понравилось!

ауч. если вдруг есть желание попробовать порабоать над Alexa в роли SDET or QA Manager могу попробовать поискать позицию. Let me know :)

такое случается, но я видел только у тех, кто уже поработал в офисе. Так чтоб нанимали сразу на удаленку не видел.

Понятно, для гастарбайтеров нее положено.

жесть!

По поему мнению, фишкой любого умного дома должна быть, по возможности, независимомсть от провайдера. Поэтому и перешивают ZigBee свистки, отвязывают устройства от родных 3rd party гейтов, перешивают ITLead тасмотой и т.п.

Кста в 2020+ ожидается некая унификация IOT устройств

Скоро ждет унификация не только рынка

IOT устройств

но и промышленной автоматизации. Например советую глянуть в сторону PLCnext от Phoenix Contact doed.nure.ua/...​ov-webinar-na-temu-plcnex

2 чаю этому господину!

Очень интересно, но

Setup Complexity

действительно High :)

Apple с его HomeKit не рассматривался?

homebridge с тонной плагинов к любому китайскому хламу и управлением через Siri поднимается в течении нескольких минут без лямбд и прочих страшных штук.

До Apple руки пока не дошли. Если Google и Amazon я мог пощупать в рамках проекта, то Apple девайс пришлось бы уже покупать, как кота в мешке. Я так понимаю, что у них альтернативная колонка — это home pod? $300, по-моему, перебор по сравнению с конкурентами. Или есть что-то более дешевое? А насчёт homebridge я пока не готов дискутировать. Вернусь из отпуска — обязательно гляну. Насчёт китайского хлама я понял посыл. Но как это будет работать с обычными кондиционерами и ТВ, к примеру? Нужно будет плагины писать? Сам по себе нодовский сервер поднять, или mqtt настроить — это не rocket science. Тут важно понимать, в чем именно будет его преимущество по сравнению с тем же Гуглом, на который уже можно заливать код напрямую для общения с девайсами? А вообще, я ведь не зря проводил сравнение с Kaldi. Облачные решения — это круто конечно. Но им нужен постоянный и хороший интернет. Без этого управлять голосом чем-либо будет невозможно. Потому в своих первых порывах я акцентировал внимание на полностью изолированном от мира решении.

Колонка, ipad, appletv — у кого что есть. Насчет кондиционеров и телевизоров — абсолютно так же: IR передатчик, если нет никаких протоколов. Хоть и не такой хардварный как в примере с малиной конечно же.

Нужно будет плагины писать?

Можно и написать, если есть большое желание и не устраивает существующее решение. Например мне пришлось подправить код под свою модель SmartTV.

в чем именно будет его преимущество по сравнению с тем же Гуглом, на который уже можно заливать код напрямую для общения с девайсами?

Не нужны никакие сервисы извне, никаких настроек, хуков, заливок кода и т.д.

Вот тут как раз основная проблема с кейсом, когда ничего из этого списка нет. :) Если бы их колонка стоила $20-30 (ну пусть даже $50), как echo dot / google mini, тогда разговор был бы совсем другим. А остальные apple устройства, на мой взгляд, не очень подходят для таких задач по причине того, что они не фокусируются на ASR проблемах. В умные колонки то не зря встраиваются массивы микрофонов и DSP чипы. Так что для чистоты эксперимента тестить Siri надо именно на HomePod. Только цена кусается. Особенно с учетом того, что такие колонки потенциально придется расставлять во всех комнатах.

Подписаться на комментарии