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

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

Добавить комментарий

Ваш адрес email не будет опубликован.