Продолжение. Начало - Часть1
Преобразование голоса в текст
Для преобразования голоса в текст будем использовать сервис Google Speech, который предоставляет API, позволяющее использовать сервис в своих приложениях. Технология следующая (подробно см. http://ab-log.ru/smart-house/speech/speech-recognition):
В Linux необходимо установить пакеты sox и flac.
apt-get install sox apt-get install flac
Далее пишем bash-скрипт.
#!/bin/bash while [ true ]; do rec -q -c 1 -r 16000 current.wav silence 1 0.2 3% 1 0.2 3% flac -f -s current.wav -o current.flac php textfromgoogle.php done
Программа sox слушает микрофон и когда обнаруживает наличие звука, записывает фрагмент в wav файл. Записывать звук нужно с частотой 16КГц. Далее этот wav файл конвертируется в формат FLAC. После этого запускается PHP-скрипт, в котором реализовано обращение к Google Speech API .
Вот сам скрипт textfromgoogle.php
// Используем cURL для формирования HTTP POST-запроса к Google API // Пакет php5-curl $file_to_upload = array('myfile'=>'@current.flac'); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,"https://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium&lang=ru-RU"); curl_setopt($ch, CURLOPT_POST,1); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: audio/x-flac; rate=16000")); curl_setopt($ch, CURLOPT_POSTFIELDS, $file_to_upload); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); $result=curl_exec ($ch); curl_close ($ch); // Google возвращает JSON, поэтому парсим стандартной функцией. Доступна в PHP 5.2 $json_array = json_decode($result, true); $voice_cmd = $json_array["hypotheses"][0]["utterance"]; // Вывод на экран if(isset($json_array["hypotheses"][0])) echo $voice_cmd;
Запускаем bash-скрипт и проверяем. Задержка выдачи результата несколько секунд, результат распознования не очень хороший, гораздо хуже Google search для планшета HTC. Но при правильном выборе слов (видимо звонкие слова) , четком произношении можно получить достаточно приемлемый результат. Ниже немного изменим скрипт, чтобы отклонения результата от Google Speech API направить к нужной фразе. Использовались встроенный микрофон и еще следующий беспроводной микрофон, который и будет использоваться в рабочем варианте.
Создание пакета для ROS
На нетбуке запущена ROS (операционная система для роботов). ROS - это фреймворк для программирования роботов, предоставляющий функционал для распределённой работы. Сайт проекта - http://www.ros.org .Достаточно хороший русскоязычный туториал здесь. Использование ROS необязательно, достаточно написать скрипт на python для преобразования голоса в текст и отправки результата в Arduino. Но в дальнейшем предполагается управлять приборами X10 с передвигающегося робота, поэтому использую ROS.
Создаем новый проект в ROS (сразу указываем зависимости пакета )
cd ~/ros_pkgs roscreate-pkg vp_x10_voice std_msgs rospy
Далее устанавливаем систему зависимостей командой rosdep
rosdep install vp_x10_voice
Теперь можно построить наш пакет
rosmake vp_x10_voice
Пакет построен
Теперь создадим узел node_x10_voicetotext, который осуществляет операции преобразования голоса в текст с помощью Google Search API, затем немного "причесывает" ответ и публикующий результат как сообщения в тему x10_voicetotext. Узел - это термин для обозначения исполняемого файла, который подключен к сети ROS. Узел создадим в директории nodes.
roscd vp_x10_voice mkdir nodes cd nodes touch node_x10_voicetotext.py
Вот содержимое файла node_x10_voicetotext.py
#!/usr/bin/env python #-*-coding:utf-8 -*- import roslib; roslib.load_manifest('vp_x10_voice') import rospy import subprocess import shlex from std_msgs.msg import String from std_msgs.msg import Int16 fraza=["люстра","лента","лампа","ночник","елка", "люстра включить","лента гори","лампа включить","ночник включить","елка гори", "отбой", "программа 1","программа 2","программа 3","программа 4","программа 5"] matrix = [["люстра","быстро"], ["лента"], ["лампа","лапа"], ["ночник","начни"], ["елка","елочка"], ["люстра включить","люстра ключи","быстро включить"], ["лента гори","mp3"], ["лампа включить","лампа включи","лампа ключи"], ["ночник ключи","ночник включить","ночник включи","ночник горит","ночник гори"], ["елка гори","елочка гори","елка горит"], ["отбой","всем спать","я спать"], ["программа 1","драма 1"], ["программа 2","драма 2"], ["программа 3","драма 3","программа тв"], ["программа 4","драма 4"], ["программа 5","драма 5"]] def talker(): pub = rospy.Publisher('x10_voicetotext', Int16) rospy.init_node('node_x10_voicetotext') while not rospy.is_shutdown(): rospy.loginfo("ожидание записи с микрофона") subprocess.Popen('rec -q -c 1 -r 16000 current.wav silence 1 0.2 3% 1 0.2 3%',shell=True,cwd = '/home/petin/ros_pkgs/vp_x10_voice/nodes/').communicate() rospy.loginfo("wav - ok") subprocess.Popen('flac -f -s current.wav -o current.flac',shell=True,cwd = '/home/petin/ros_pkgs/vp_x10_voice/nodes/').communicate() rospy.loginfo("flac - ok") proc3=subprocess.Popen('php textfromgoogle.php',shell=True, stdout = subprocess.PIPE,cwd = '/home/petin/ros_pkgs/vp_x10_voice/nodes/') result=proc3.communicate()[0] str1 = "result api google = %s"%result rospy.loginfo(str1) str2="" index=0 for ind,elements in enumerate(matrix): for element in elements: if result.count(element)>0: str2=fraza[ind] index=ind+1 if(index>0): pub.publish(index+1) rospy.loginfo(str2) if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass
Список фраз (команд) моей комнаты (4 позиции, включающиеся приборами x10). фразы подбирались для слов лучшего распознавания
- "люстра" - отключить верхний свет
- "лента" - отключить светодиодную ленту верхнего света
- "лампа" - отключить светильник на рабочем столе
- "ночник" - отключить светильник возле дивана
- "елка" - отключитьгирлянда на елке (зарезервировано для Нового года)
- "люстра включить" - включить верхний свет
- "лента гори" - включить светодиодную ленту верхнего света
- "лампа включить" - включить светильник на рабочем столе
- "ночник включить" - отключить светильник возле дивана
- "елка гори" - включить гирлянда на елке
- "отбой" - выключить все
- "программа 1",
- "программа 2",
- "программа 3",
- "программа 4",
- "программа 5"
Команды "программа 1", "программа 2", "программа 3", "программа 4", "программа 5" -серии команд для одновременного действия с приборами по одной команде - например включить верхний свет, ночник, елку
(определим позже)
Файл node_x10_voicetotext.py делаем исполняемым. Запускаем.
Первый терминал
roscore
Второй терминал
rosrun vp_x10_voice node_x10_voicetotext.py
Проверяем, что в списке узлов появился узел
rosnode list
Ответ
/node_x10_voicetotext /rosout
В списке тем - тема
rostopic list
Ответ
/rosout /rosout_agg /x10_voicetotext
Видео результата работы скрипта преобразования голоса в текст и публикация в тему ROS
Узел node_x10_voicetotext публикует номер выбранной программы (сообщения типа Int16) в тему. Создадим файл node_x10_texttocommand.py, который создаст узел node_x10_texttocommand, который будет получать сообщения из темы x10_voicetotext и отправлять последовательность команд в тему data_to_x10. Последовательность команд будем отправлять как сообщения типа X10. Создадим файл, описывающий тип сообщений X10. Создадим в директории проекта vp_x10_voice папку msg, а в ней файл X10.msg следующего содержания
int16 command1 int16 command2 int16 repeatTime
Заново пересобираем проект
roscd vp_x10_voice rosdep install vp_x10_voice std_msgs rospy rosmake vp_x10_voice
И содержимое файла node_x10_texttocommand.py (с программами пока не определился, поэтому каждая программа просто выключает весь свет)
#!/usr/bin/env python #-*-coding:utf-8 -*- # Слушатель x10_voicetotext # # и отправка команд и установка параметров # для управления ardrone 2.0 # import roslib; roslib.load_manifest('vp_x10_voice') import rospy import subprocess import shlex from vp_x10_voice.msg import X10 from std_msgs.msg import String from std_msgs.msg import Int16 from std_msgs.msg import Empty HOUSE_A=6 #B00110 UNIT_1=12 #B01100 UNIT_2=28 #B11100 UNIT_3=4 #B00100 UNIT_4=20 #B10100 UNIT_5=2 #B00010 UNIT_6=18 #B10010 UNIT_7=10 #B01010 UNIT_8=26 #B11010 UNIT_9=14 #B01110 UNIT_10=30 #B11110 UNIT_11=6 #B00110 UNIT_12=22 #B10110 UNIT_13=0 #B00000 UNIT_14=16 #B10000 UNIT_15=8 #B01000 UNIT_16=24 #B11000 ALL_UNITS_OFF=1 #B00001 ALL_LIGHTS_ON=3 #B00011 ON=5 #B00101 OFF=7 #B00111 DIM=9 #B01001 BRIGHT=11 #B01011 ALL_LIGHTS_OFF=13 #B01101 arr_commands=[[[HOUSE_A,UNIT_2,1],[HOUSE_A,OFF,1]], # люстра выключить [[HOUSE_A,UNIT_3,1],[HOUSE_A,OFF,1]], # лента RGB выключить [[HOUSE_A,UNIT_4,1],[HOUSE_A,OFF,1]], # лампа выключить [[HOUSE_A,UNIT_5,1],[HOUSE_A,OFF,1]], # ночник выключить [[HOUSE_A,UNIT_6,1],[HOUSE_A,OFF,1]], # гирлянда елочная выключить [[HOUSE_A,UNIT_2,1],[HOUSE_A,ON,1]], # люстра включить [[HOUSE_A,UNIT_3,1],[HOUSE_A,ON,1]], # лента RGB включить [[HOUSE_A,UNIT_4,1],[HOUSE_A,ON,1]], # лампа включить [[HOUSE_A,UNIT_5,1],[HOUSE_A,ON,1]], # ночник включить [[HOUSE_A,UNIT_6,1],[HOUSE_A,ON,1]], # гирлянда елочная включить [[HOUSE_A,UNIT_5,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,ON,1],[HOUSE_A,UNIT_4,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_2,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,OFF,1]], #отбой [[HOUSE_A,UNIT_5,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,ON,1],[HOUSE_A,UNIT_4,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_2,1],[HOUSE_A,ON,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,OFF,1]], #программа 1 [[HOUSE_A,UNIT_5,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_4,1],[HOUSE_A,ON,1],[HOUSE_A,UNIT_2,1],[HOUSE_A,OFF,1],[HOUSE_A,UNIT_6,1],[HOUSE_A,OFF,1]], #программа 2 [[HOUSE_A,ALL_LIGHTS_OFF,1]], # программа 3 [[HOUSE_A,ALL_LIGHTS_OFF,1]], # программа 4 [[HOUSE_A,ALL_LIGHTS_OFF,1]] # программа 5 ] def controller(data): index = data.data data1=X10() for ind,elements in enumerate(arr_commands[index-1]): data1.command1=elements[0] data1.command2=elements[1] data1.repeatTime=elements[2] pub1 = rospy.Publisher('data_to_x10', X10) pub1.publish(data1) rospy.loginfo(data1) def listener(): rospy.init_node('node_x10_texttocommand') sub = rospy.Subscriber("x10_voicetotext",Int16,controller) rospy.spin() if __name__ == '__main__': listener()
Смотрим сообщения, выдаваемые в тему data_to_x10
rostopic echo data_to_x10
В 3-ей части мы рассмотрим отправку сообщений в Arduino из ROS
Комментарии (7) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
Какие требования к интернет-каналу при задействовании сервиса Google Speech API для устройств домашней автоматизации? Я так понимаю на распознавание необходимо какое-то время.
[Автор]
Чуствительность микрофона выставлена так, что команды необходимо произносить достаточно громко, чтобы фоновый звук от телевизора не вызывал постоянных обращений к серверу Google, поэтому обращения к серверу минимальны.
Главное добиться, чтобы формировался небольшой файл current.wav, т.е. после произнесения фразы sox сразу определял "тишину",
rec -q -c 1 -r 16000 current.wav silence 1 0.2 3% 1 0.2 3%
В ближайшем будущем думаю поменять распознавание Google Speech (кстати, на смартфонах Google Speech выдает гораздо лучшее распознование, возможно там есть предварительная обработка голоса перед отправкой запрос на сервер) на распознование голоса с помощью движка julius. На английском языке при создании своего словаря результат очень хороший (надеюсь скоро закончю и опубликую проект управления квадрокоптера голосом с помощью движка julius). Но с русским языком там пока проблеммно.
[Автор]
[Автор]
[Автор]