Голосовое управление освещением X10. Часть 2

Продолжение. Начало — Часть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 направить к нужной фразе. Использовались встроенный микрофон и еще следующий беспроводной микрофон, который и будет использоваться в рабочем варианте.

x10-9.jpg

Создание пакета для 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

x10-11.png

В 3-ей части мы рассмотрим отправку сообщений в Arduino из ROS