Saltar al contenido

Para la parte linguistica del proyecto de generación automática de lenguaje, he encontrado dos paquetes interesantes en ruby: ruby linguistics y ruby wordnet.

Ruby linguistics comprende una serie de utilidades para manejar palabras de forma "linguistica": permite cosas como estas:

  • pluralizar una palabra: "box".en.plural # => "boxes"
  • añadir artículo indefinido: "book".en.a # => "a book"
  • generar el gerundio: "runs".en.present_participle # => "running"
  • calcular ordinales de números: 5.en.ordinal # => "5th"
  • escribir un número en letra: 5.en.numwords # => "five"
  • cuantificar objetos: "cow".en.quantify(5) # => "several cows" || "cow".en.quantify( 1005 ) # => "thousands of cows"
  • infinitivos: "leaving".en.infinitive # => "leave"
  • generar conjunciones:

animals = %w{dog cow ox chicken goose goat cow dog rooster llama pig goat dog cat cat dog cow goat goose goose ox alpaca}
puts "The farm has: " + animals.en.conjunction
Produce:
The farm has: four dogs, three cows, three geese, three goats,two oxen, two cats, a chicken, a rooster, a llama, a pig, and an alpaca

Los ejemplos están tomados de la página del proyecto dónde hay más ejemplos.

Ruby wordnet es un interfaz ruby a Wordnet, una base de datos de palabras organizada de forma semántica. En esta base de datos las palabras se agrupan en synsets (conjuntos de palabras sinónimas) que se relacionan entre ellas con las siguientes relaciones:

  • Hiperonimia e hiponimia: una palabra es hipónima de otra si posée los rasgos semánticos de otra más general.
  • Holonimia y meronimia: si una palabra es parte de otra (ventana es una parte de una casa).
  • Términos coordinados: dos palabras son términos coordinados si comparten un hiperónimo.

He hecho una prueba simple con linguistics y wordnet que pongo aquí cómo ejemplo. En este caso se busca el synset de unas palabras ("happiness", "young" y "man") y se eligen aleatoriamente las palabras de cada synset para formar el mensaje resultado:

require 'rubygems'
require 'linguistics'
require 'wordnet'

Linguistics::use( :en )

puts "Your future"

happiness_synonyms = "happiness".en.synset(1,:noun).synonyms
happiness_word = happiness_synonyms[rand(happiness_synonyms.length)]

young_synonyms = "young".en.synset(1,:adjective).synonyms
young_word = young_synonyms[rand(young_synonyms.length)]

man_synonyms = "man".en.synset(1,:noun).synonyms
man_word = man_synonyms[rand(man_synonyms.length)]

message = "I wish you #{happiness_word}, #{young_word} #{man_word}."

puts message

Una prueba muy sencilla, pero que para poder ejecutarlo hay que resolver un par de problemillas:

  • Para instalar wordnet para ruby (gem install wordnet) hay que tener instalado previamente wordnet (en ubuntu, es simplemente sudo apt-get install wordnet) y el binding de berkeley db para ruby (en ubuntu, sudo apt-get install libdb-ruby1.8). Después de instalar, hay que migrar la base de datos wordnet a berkeley db, con un script incluido en ruby wordnet (./convertdb.rb)
  • Ruby linguistics, al menos en la versión que tengo instalada, la 1.0.5 (gem install Linguistics) está anticuado. Hay que sustituir la función lookupSynsets por lookup_synsets en el fichero wordnet.rb.

La ejecución de este programilla genera frases cómo esta:

I wish you felicity, offspring man.

I wish you happiness, young man.

I wish you happiness, offspring adult male.

Todavía muy simple para lo que quiero conseguir, pero parece que estas librerías pueden ser útiles.

Llegó el momento de conectar los dos módulos de software que he estado preparando: el controlador de servos de arduino y un programa ruby para detectar la presencia online de un usuario.

Para enviar mensajes por el puerto serie desde un programa ruby, estoy usando la librería ruby-serialport. Se trata de una librería relativamente antigua, que no se actualiza desde 2004, pero que funciona bien, aunque con algún problema que he encontrado.

Cómo se puede ver en un ejemplo de la documentación de arduino, la librería es muy sencilla de usar:

1.- Primero se incluye la dependencia:

require "serialport.so"

2.- Después se inicializa la comunicación con el puerto serie:

#params for serial port
port_str = "/dev/ttyUSB0" #may be different for you
baud_rate = 115200
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE

sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity)

3.- Se leen y/o escriben datos según se necesite:

sp.putc(GO_UP_SYMBOL)
printf("%c", sp.getc)

4.- Por último se se cierra el puerto cuando se deja de usar:

sp.close

El problema que he encontrado con esta librería, es que para leer y escribir en el puerto serie, he creado una clase específica. Al incluir la clase en mi programa principal, no podía acceder al puerto serie, ya que se obtenía el siguiente error:

(eval):1: (eval):1:in `private_class_method': undefined method `create' for class `Class' (NameError)
from (eval):1
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from ./serialpresencebot.rb:1
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from ./presencebotlauncher.rb:3

Finalmente, he tenido que incluir la librería en el programa principal y eliminar el "require" de la clase que controla el puerto serie para que funcionase. Quizá se trate de un problema de funcionamiento de ruby, pero parece más probable que se trate de un problema con la librería ruby-serialport. En cualquier caso, es sencillo de resolver.

1

Me gusta la robótica porque es el punto dónde se mezcla el mundo físico con el mundo "programado". En la mayoría de aplicaciones que programamos en ordenador, la interacción con el mundo físico se limita al teclado-ratón-monitor. Cómo mucho añadimos sonido y no siempre se suele hacer, salvo en los juegos.

Así que cuando vi un producto conceptual cómo availabot inmediatamente me pareció interesante y después de analizarlo pensé en hacer algo parecido. Al igual que a los autores de availabot, me ha llevado 2 años ponerme a ello, pero ya estoy trabajando en una versión preliminar de "presencebot".

La idea es sencilla: representar de alguna forma física la presencia on-line, en este caso la presencia en messenger. Tengo pensado implementar una variante con facebook y/o jabber, e incluso aumentar la funcionalidad en el futuro haciendo que se le puedan enviar comandos tipo "levanta", "saluda", etc. En el caso de availabot, la presencia física se trata de un muñeco parecido físicamente al dueño. En mi caso se limita por ahora a mover un servo conectado a una placa arduino. Si encuentro un muñeco apropiado, el servo movería el muñeco igual que availabot. Pero es posible realizar cualquier tipo de presencia física que nos interese: una campana que suena, una lámpara que se enciende, un muñeco que baila, cualquier cosa.

Inicialmente lo implementaré con ruby, el lenguaje más rápido que conozco para hacer prototipos y usará la biblioteca msn live presence, con un api rest. La elección de msn es porque tiene un api muy sencillo de usar y porque msn messenger está muy extendido. Además la ventaja de un api rest, es que se puede acceder desde cualquier punto con acceso a internet, aunque la desventaja es que tarda un poco en reaccionar. La parte física estará implementada con arduino, con su lenguaje de programación propio y al igual que availabot, se conectará por usb.