meRobot : RaspberryPi, Nodejs & Cognitive Services (Parte 2 )

Hola a todos, en este segundo post de la serie meRobot , quisiera enseñarles como lograr que nuestro robot  «pueda hablar», reconocer comando de voz y ademas traducirlos. Pero antes de empezar es necesario que previamente hayamos instalado nodejs  sobre nuestra raspberry, les comparto entonces  un pequeño vídeo donde explico paso a paso como hacerlo, como crear una carpeta compartida usando samba y como acceder dicha carpeta desde visual studio code en nuestra maquina de desarrollo de forma remota:

Si siguieron los pasos en el vídeo, ya podemos entonces desde visual studio code acceder al folder compartido: Nota: es importante que tanto la raspberry como su equipo estén conectados a la misma red wifi (estar dentro de la misma red) para que haya visibilidad entre ambos.

  1. en visual studio code, vaya al menú file, open folder y en la siguiente ventana siga las instrucciones a continuación:

2017-03-03_9-43-01

  1. escriba en el cuadro de dirección, dígite \\raspi o el \\ el hostname que le haya asignado a su raspberrypi y presione la tecla enter, notará que le aparecerán todas las carpetas compartidas que haya configurado con samba en el explorador de windows,
  2. seguidamente acceda  a la carpeta de la lista que usted desee  y cree dentro de esta una que se llame meRobot, luego selecciónela  y haga click sobre el boton select folder para que se abrá en visual studio code, allí pondremos todos los archivos de código fuente .

Hora de codificar 

Dado que nuestra carpeta físicamente no esta en nuestra maquina de desarrollo, debemos acceder via ssh a nuestra raspberry para poder ejecutar nuestra app y instalar sus dependencias, vía node y npm respectivamente. Para ello usaremos putty.

2017-03-03_16-20-26

2017-03-03_16-23-03

Hecho lo anterior, quisiera antes de continuar, aclarar un poco como sera el modo de trabajo, nuestra aplicación la codificaremos remotamente desde visual studio code (en nuestra maquina). pero cada vez que hagamos un cambio y queramos probar  como va todo, debemos desde la terminal ssh ejecutar el comando:

node –harmony-async-await index.js

Para  habilitar la funcionalidad de «tts» o Text To Speech en nuestra raspberry, instale el modulo Pico TTS(Google Android TTS engine) desde la terminal(putty):

sudo apt-get install libttspico-utils

Luego conecta a la raspberry unos auriculares o parlantes y prueba, ejecutando el siguiente comando:

 pico2wave -w speak.wav  -l  es-ES  «Hola a todos mis seguidores»   && omxplayer speak.wav

Luego de haber instalado correctamente el paquete pico2wave sobre nuestra raspberry, y haber realizado la prueba anterior,  estamos listos para empezar a programar nuestro robot, empecemos instalando los siguientes paquetes vía npm (recuerden desde la terminal en putty):

  «dependencies»: {
    «axios»: «^0.15.3»,
    «bluebird»: «^3.5.0»,
    «boom»: «^4.3.0»,
    «guid»: «0.0.12»,
    «hapi»: «^16.1.0»,
    «inert»: «^4.1.0»,
    «ngrok»: «^2.2.6»,
    «querystring»: «^0.2.0»
  }
npm i –save axios bluebird boom hapi inert ngrok querystring guid

Luego agregue los siguientes archivos:

Archivo common.js: la función getToken, nos permite obtener el token de autenticacion para podernos conectar al api de bing speech recognition


const axios = require("axios");
module.exports = {
getToken : async(apikey)=>{
var config = {
headers: {'Ocp-Apim-Subscription-Key': apikey}
};
return axios.post("https://api.cognitive.microsoft.com/sts/v1.0/issueToken",{}, config);
}
}

view raw

common.js

hosted with ❤ by GitHub

Archivo tts.js (Text to Speech):

Esta función  recibe como parámetro una cadena que corresponde al texto que queremos que nuestro robot ¨repita¨ y como segundo parámetro  el idioma en el que queremos que lo haga(esto es posible gracias al cliente Text 2 Speech pico2wave, el cual en paso anteriores explique su instalación sobre la raspberrypi)


var exec = require('child_process').exec;
var util = require("util");
var talk = (message, lang) =>{
lang = lang === "es" ? (' -l es-ES ') : (' -l en-US ');
const cmd = util.format("pico2wave -w speak.wav %s '%s' && sox speak.wav -r 48k speak.mp3 pitch -200 && omxplayer speak.mp3", lang, message)
console.log(cmd);
exec(cmd, function(error, stdout, stderr) {
if(error)
console.error(error);
});
}
module.exports = { talk : talk }//export the talk functions to Robot.js file, This will allow him to speak

view raw

tts.js

hosted with ❤ by GitHub

Archivo stt.js (Speech to Text):

la función stt, recibe como parámetro el archivo .wav(audio) en bytes, que contiene el comando de voz que queremos que nuestro robot reconozca (el cual hemos capturado usando nuestro micrófono), y luego lo envía por post  al api de bing speech recognition, para que lo convierta a Texto. antes usamos la función  getToken, para obtener el token de autenticacion y poder realizar la petición.


const util = require("util");
const Guid = require("guid");
const querystring = require('querystring');
const path = require("path");
const bluebird = require("bluebird");
const axios = require("axios");
const fs = bluebird.promisifyAll(require("fs"));
const {getToken} = require("./common");
const stt = async(BING_SPEECH_API_KEY, audioAsBInary)=>{
var authToken = await getToken(BING_SPEECH_API_KEY), params = "";
/* URI Params. Refer to the README file for more information. */
var params = {
"scenarios":"smd",
"appid": "D4D52672-91D7-4C74-8AD8-42B1D98141A5",
"locale" :"es-ES",
"device.os": "wp7",
"version":"3.0",
"format": "json",
"instanceid":"565D69FF-E928-4B7E-87DA-9A750B96D9E3",
"requestid" : Guid.raw()
};
var uri = "https://speech.platform.bing.com/recognize?"+querystring.stringify(params);
var config = {
headers:{
"Authorization" : "Bearer " + authToken.data,
"content-type" : "audio/wav; codec=\"audio/pcm\"; samplerate=16000"
}
}
/*var audioAsBInary = await fs.readFileAsync("./speak.wav"); */ return await axios.post(uri,audioAsBInary,config ); //just making a request
}
module.exports = stt;//we need to export the function, because the idea is invoke the function from the index file

view raw

stt.js

hosted with ❤ by GitHub

Archivo Robot.js

Este archivo es una clase en la cual iremos  agregando las funciones de nuestro Robot


const tts = require("./tts");//Text to Speech
const stt = require("./stt");//Speech to Text
class Robot{
constructor(name, config){
this.name = name;
this.config = config;
}
//just the phrase that you want the robot repeat for you
textToSpeech(phrase){
tts.talk("Hi all from raspberry pi");
}
//.wav file as binary, it comming from web interface
//we could be more strict and put all the code inside a try/ catch block, we are just
//assuming that everything'll be perfect :), is just a robot
async speechToText(audioAsBinary){
var response = await stt(this.config.BING_SPEECH_API_KEY, audioAsBinary); return response.data.results;
}
}
module.exports = Robot; //we are exporting the class Robot, to be used at the index file

view raw

Robot.js

hosted with ❤ by GitHub

Archivo config.js:

En el archivo config.js, iremos agregando los keys de las Apis que iremos usando en el transcurso de los tutoriales. para obtenerlos, vasta con seguir  la documentación de la pagina de Microsoft cognitive services  o el tutorial : Microsoft Cognitive Services Notes: Emotion API (Node.js)


{
"BING_SPEECH_API_KEY" : "<PUT YOUR BING SPEECH API>"//from https://www.microsoft.com/cognitive-services/en-US/subscriptions
}

view raw

config.js

hosted with ❤ by GitHub

Archivo index.js:

Este es quizás el mas importante aquí definimos  nuestro servidor hapi, el cual servirá como interfaz de comunicación entre nuestro robot y el mundo:


//const robot = require("./Robot")("Cyg");
const hapi = require("hapi");
const boom = require("boom");
const path = require("path");
const ngrok = require('ngrok');
const bluebird = require("bluebird");
const fs = require("fs");
const Robot = require("./Robot");
const config = JSON.parse(fs.readFileSync("./config.json","utf8"));
const meRobot = new Robot("Cyg", config);
const port = 8080;
const server = new hapi.Server();
server.connection({ port: port, host: '0.0.0.0' });
async function setup() {
await server.register(require("inert"));
}
ngrok.connect(port,function (err, url) {
setup().then(() => {
server.route({
path: "/stt",
method: "POST",
config: {
payload: {
output: 'stream',
maxBytes: 1048576 * 10, /*10MB*/
parse: true,
allow: 'multipart/form-data'
}
},
handler: (request,reply)=>{
if(request.payload.audio){
var audioAsBinary = request.payload.audio._data;
//we order to the robot that please, convert the audio into Text
//for guessing the command that the user is sending, for example take a picture
meRobot.speechToText(audioAsBinary)
//handler to sucess request
.then((data)=>{ return reply(data)})
//hanlder to error request
.catch((err)=> { console.log(err); return reply(err)});
}
}
});
//the route just send to user the index.html file where all happen
server.route({
path: "/robot",
method: "GET",
handler: {
file: {
path: "./views/index.html"
}
}
});
//once ngrok have generated the uri, the using it
//we redirect the user at /robot route
server.route({
path: "/",
method: "GET",
handler: (request,reply)=>{
return reply().redirect(`${url}/robot`);
}
});
//this route let us handler the static content as
//jquery, axios and others frontend component (css, images etc.)
server.route({
path: "/assets/{path*}",
method: "GET",
handler: {
directory: {
path: "./public"
}
}
});
server.start(() => console.log(`the port is running at ${server.info.port}`));
})
});
//process.on('SIGINT', function() { ngrok.disconnect(); ngrok.kill(); /* kills ngrok process */ });

view raw

index.js

hosted with ❤ by GitHub

El archivo app.js en el cual esta la lógica para acceder al micrófono, generar el archivo .wav y enviarlo al servidor y el resto de código, están en el repositorio en github:

https://github.com/haruiz/meRobot/

Esto es todo por ahora, cualquier inquietud o problema que tengan, no duden en contactarme

 

Saludos

 

 

Anuncio publicitario