CraftDuino v2.0
  • - это CraftDuino - наш вариант полностью Arduino-совместимой платы.
  • CraftDuino - настоящий конструктор, для очень быстрого прототипирования и реализации идей.
  • Любая возможность автоматизировать что-то с лёгкостью реализуется с CraftDuino!
Просто добавьте CraftDuino!

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

Продолжение. Начало — Часть1

Преобразование голоса в текст

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

В 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 для планшетов. Но при правильном выборе слов (видимо звонкие слова), четком произношении можно получить достаточно приемлемый результат. Ниже немного изменим скрипт, чтобы отклонения результата от Google Speech API направить к нужной фразе. Использовались встроенный микрофон и еще следующий беспроводной микрофон, который и будет использоваться в рабочем варианте.



Создание пакета для ROS

На нетбуке запущена ROS (операционная система для роботов). Еще версия electric!!!

Создаем новый проект в 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

Комментарии (3)

RSS свернуть / развернуть
+
0
а если хочется голосовое управление в каждой комнате, то надо по компу на комнату или может быть есть какие то средства записывать звук и отдавать на сервер для обработки через гугл?
avatar

businka76

  • 23 апреля 2015, 13:41
+
0
в этом примере весь речевой трафик в комнате оцифровывается и отдается в гугл тем самым Вы публикуете все разговоры в комнате. как-то не очень? возвращаясь к вопросу способа который по требованию запишет именно команду и передаст на сервер. встречал лентяйки всякие — нажимаешь кнопку — говоришь.
avatar

businka76

  • 23 апреля 2015, 13:44
+
0
Проект давний — только начинал изучать ROS, хотелось попробовать, тем более контроллер X10 поломался
Делал на одну комнату, без каких-то глобальных задач — поэтому так
Способ нажимаешь кнопку — говоришь — мне неудобен
Есть вариант использования вместо google автономно julius (пробовал здесь robocraft.ru/blog/2888.html),
но нет акустической модели для русского языка,
а написать русские слова английской транскрипцией — не получилось
avatar

victoruni

  • 23 апреля 2015, 14:16

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