Presentación del Taller de ROS – Encuentro Software Libre UCM 2015

Aquí va a quedar el pdf con mi presentación del taller de ROS en el Encuentro de Software Libre de la UCM 🙂

Presentación ROS

Anuncios

Haciendo programas con opciones de consola con C++ y Boost

Cuando uno ya lleva unos años programando y se pone a ver sus códigos viejos generalmente piensa “joder, qué ***** más fea”. Mientras vamos adquiriendo más experiencia y vamos participando en proyectos más complejos, se nos va haciendo necesario el programar “como se debe”. Una de las cosas importantes al programar bien es hacer programas que sean fácil de ejecutar y que puedan configurarse mediante varias opciones sin tener que compilarse de nuevo. La idea es conseguir algo así:

programa --opción1 valor1 --opción2 valor2

Para eso, cuando trabajamos en C o C++ tenemos las variables argc y argv que contienen la cantidad de argumentos de consola y los argumentos mismos respectivamente. Con eso podríamos programar una recepción de argumentos usando esas dos variables. Pero, con tantos años que llevamos programando… ¿no se le habrá ocurrido a alguien una forma estándar de hacer eso mismo para no andar reinventando la rueda?

Boost

Boost es un conjunto de bibliotecas para C++ que abordan una cantidad inmensa de áreas, desde álgebra lineal hasta expresiones regulares, pasando por procesamiento de imágenes y multithreading [1][2]. No voy a entrar en detalles de todo lo que puede hacer Boost, ya que no lo sé y estaría vacuneando [3] si les inventara. Me voy a enfocar en este asunto particular de hacer un programa con opciones usando un módulo de Boost.

Primero hay que instalar Boost. Para eso, lo más sencillo es instalarlo como un paquete de consola. En Fedora, es llegar y correr lo siguiente:

sudo yum install boost-devel

De Boost nos interesa usar el módulo Program Options, que nos deja, valga la redun-dun-dancia, añadirle opciones a nuestros programas.

A codear

Para incluir el módulo en nuestro programa de C++, escribimos:

#include <boost/program_options.hpp>

Y declaramos nuestro espacio de nombres en una variable, para que sea más fácil de localizar:

namespace po = boost::program_options;

Ya dentro del código, generamos un objeto de tipo options_description, que guardará la información de nuestras opciones, los valores que reciben y sus descripciones.

po::options_description desc("Nuestras fabulosas opciones de programa");

Luego, con add_options vamos registrando las opciones que se van a ejecutar. Cada nombre de la opción luego será llamable en la consola de la forma –nombre. Para este ejemplo usaremos una instrucción –help que mostrará los comandos, otra instrucción –name que recibirá un nombre como string y una instrucción –age que recibirá un entero, para ejemplificar los tipos de datos.

desc.add_options()
    ("help", "Imprime este mensaje de ayuda.")
    ("name", po::value<string>(), "Un nombre como string.")
    ("age", po::value<int>(), "Una edad como entero.");

Cuando leamos las instrucciones de consola, se generará un objeto variables_map que consistirá en pares clave–>valor. Con store guardamos lo que se lee desde consola mediante parse_command_line usando la descripción en el mapa de variables. En caso de que tengamos una función que se desencadene ante la presencia de una cierta opción, usamos notify para ejecutarla.

po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);

Para verificar si una opción fue ingresada (y posiblemente, acceder a su valor si es que tiene) usamos la instrucción count con el nombre que le asignamos a la opción. Por ejemplo, para ver si se ingresó la opción –help y mostrar el mensaje de descripción de las opciones, usamos lo siguiente:

if vm.count("help")
    cout << desc << endl;

Si recibimos algún valor junto a las opciones, llamamos al mapa de variables mediante el nombre de la opción y especificamos el tipo de variable que esperamos recibir. Por ejemplo, para recibir el nombre sería algo como:

cout << vm["name"].as<string>() << endl;

Ahora, para juntar todo, aquí hay un código que recibe la instrucción –help para mostrar la lista de opciones, o las opciones –name o –age para leer una string o un int respectivamente:

#include <string>
#include <iostream>
#include <boost/program_options.hpp>

using namespace std;
namespace po = boost::program_options;

int main( int argc, char** argv )	{
  po::options_description desc("Nuestras fabulosas opciones de programa");

  desc.add_options()
    ("help", "Imprime este mensaje de ayuda.")
    ("name", po::value<string>(), "Un nombre como string.")
    ("age", po::value<int>(), "Una edad como entero.");

  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);

  if(vm.count("help"))
    cout << desc << endl;
  else{
    if(vm.count("name")){
      cout << "Nombre ingresado: " << vm["name"].as<string>() << endl;
    }
    if(vm.count("age")){
      cout << "Edad ingresada: " << vm["age"].as<int>() << endl;
    }
  }
}

Compilé el programa como “test” y al correrlo con distintas opciones obtuve los siguientes resultados:

[bruno@marelle build]$ ./test --help
Nuestras fabulosas opciones de programa:
  --help                Imprime este mensaje de ayuda.
  --name arg            Un nombre como string.
  --age arg             Una edad como entero.

[bruno@marelle build]$ ./test --name Brunaldo
Nombre ingresado: Brunaldo
[bruno@marelle build]$ ./test --name Brunaldo --age 23
Nombre ingresado: Brunaldo
Edad ingresada: 23
[bruno@marelle build]$ ./test --age 23
Edad ingresada: 23

Este es un código bastante simple. Si quieren agregar más detalles, como bloques try/catch, pueden ver el tutorial oficial en [4]. (Eso suena coqueto)

Para compilar

Para compilar el código anterior, hay que linkear el módulo que usamos de Boost. Para eso usan la opción -lboost_program_options con su compilador regalón.

c++ ../src/test.cpp -lboost_program_options

Si les gusta compilar con CMake, pueden agregar lo siguiente a su archivo CMakeLists.txt:

include_directories(${Boost_INCLUDE_DIR})
find_package(
    Boost 1.40
    COMPONENTS
	program_options
    REQUIRED)
add_executable(
    test
    src/test.cpp)
target_link_libraries(
    test
    ${Boost_LIBRARIES})

En vez de ${Boost_LIBRARIES} pueden usar ${Boost_PROGRAM_OPTIONS_LIBRARY} para linkear este módulo en específico, pero así se aseguran de poder linkear cualquier otro módulo de Boost que le vayan a agregar a su programa.

Hasta aquí llega el tutorial. Ahora, a programar, carajo.

Referencias

  1. Boost en Wikipedia: https://en.wikipedia.org/wiki/Boost_%28C%2B%2B_libraries%29
  2. Sitio de Boost: http://www.boost.org/
  3. Vacuna: http://www.mainframe.cl/diccionario/diccionario.php?palabra=vacuna&accion=Buscar
  4. Tutorial oficial de boost::program_options: http://theboostcpplibraries.com/boost.program_options

Entrando a picar más profundo en ROS

Si leyeron el artículo anterior, quizás les dieron ganas de profundizar más en ROS. Como el tutorial anterior era una introducción, no entré en tantos detalles. Ahora pretendo explicar más sobre los elementos fundamentales a entender cuando se trabaja con ROS.

Dentro de ROS existen ciertos conceptos clave que entender y algunas aplicaciones asociadas a estos elementos que nos harán la vida más sabrosa al trabajar. Dentro de esos elementos están los nodos, los tópicos, los servicios, los mensajes, el servidor de parámetros, y otros más. Más abajo iré explicándolos paso a paso y escribiendo las referencias oficiales de cada tema.

Logo ROS

ROS como un sistema distribuido

Como mencioné en el otro artículo, ROS es un framework distribuido. Su idea principal es que los robots son sistemas en los que una falla en un componente no debe dañar el funcionamiento de la máquina completa. Para esto, se pensó para funcionar implementado en programas con funciones puntuales, los nodos, que se comunican entre sí para obtener el funcionamiento completo del robot. Análogo a lo que en otros sistemas distribuidos hace la labor de coordinar el trabajo global, como el namenode en Hadoop, en ROS existe una aplicación que coordina la ejecución de todos los otros nodos: el roscore.

Roscore

El roscore [1] es un conjunto de herramientas necesarias para hacer funcionar los demás nodos de ROS. Es la primera aplicación que se debe ejecutar para que el resto funcione. Esta aplicación se encarga de llamar al nodo Master [2], que se encarga de hacer que los nodos se puedan encontrar unos a otros y de gestionar las llamadas a tópicos y servicios. También se encarga de llamar al servidor de parámetros [3], que se utiliza para almacenar variables que deben estar disponibles para todos los nodos mediante APIs. Por último, roscore abre un nodo que sirve como la salida estándar de ROS para generar logs e impresiones por consola que se publican en el tópico “/rosout”, el nodo rosout [4].

Para lanzar el roscore, basta llamarlo desde la consola y quedará funcionando. La mayoría de las aplicaciones incluidas al instalar ROS dependen de que el roscore esté funcionando.

roscore
... logging to /home/bruno/.ros/log/1976e978-2e49-11e5-aa44-b803058568da/roslaunch-cronidea-2483.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is &lt;1GB.

started roslaunch server http://cronidea:51918/
ros_comm version 1.11.13

SUMMARY
========

PARAMETERS
 * /rosdistro: indigo
 * /rosversion: 1.11.13

NODES

auto-starting new master
process[master]: started with pid [2497]
ROS_MASTER_URI=http://cronidea:11311/

setting /run_id to 1976e978-2e49-11e5-aa44-b803058568da
process[rosout-1]: started with pid [2510]
started core service [/rosout]

También existe otra forma de ejecutar aplicaciones, que es mediante roslaunch. Al ejecutar roslaunch, se ejecutará un archivo .launch que especifica mediante una notación XML una serie de parámetros y nodos a lanzar. Al usar roslaunch, si no está corriendo el roscore, se ejecuta automáticamente antes de ejecutar los contenidos del archivo .launch.

ROS también puede ejecutarse usando varias máquinas, de las que sólo una estará corriendo el roscore. Pero como aún no he trabajado con ROS en varias máquinas, prefiero hablar de eso cuando tenga más experiencia.

Paquetes y Nodos

Como ya mencioné varias veces, ROS está pensado para funcionar con programas puntuales llamados nodos [5]. Los nodos se organizan en paquetes (packages [6]) que generalmente incluyen varios nodos de funciones similares o complementarias. Los paquetes también se puede organizar en stacks, pero eso ya es irse por las ramas para el alcance de este tutorial. Lo habitual es que el sistema de un robot utilice distintos nodos para ejecutar las tareas necesarias. Por ejemplo, un nodo puede encargarse de recibir imágenes de la cámara y publicarlas en un tópico particular, otro nodo puede leer las imágenes desde ese tópico y aplicar algún proceso de detección de objetos. Otro nodo puede leer los resultados de la detección de objetos y planificar una ruta, mientras que otro nodo leerá la ruta y aplicará los comandos de movimiento que seguirán esa ruta.

Para ejecutar un nodo, se llama la instrucción rosrun seguida del nombre del paquete y el nombre del nodo. Por ejemplo, cuando en el tutorial anterior se ejecutó el simulador de tortuga, se llamó:

rosrun turtlesim turtlesim_node

y se comenzó a ejecutar el simulador.

También se pueden pasar argumentos a los nodos que se ejecuten, por ejemplo en el nodo “image_view” del paquete “image_view” que se encarga de mostrar imágenes, se puede especificar el tópico en el que se están publicando las imágenes usando la forma:

rosrun image_view image_view image:=/camera/image

Rosnode

Rosnode [7] es una herramienta para obtener información de los nodos en ejecución. Imaginemos que está funcionando el nodo del simulador de tortuga. Si ejecutamos “rosnode list” podemos ver la lista de nodos en ejecución. En este caso mostraría:

/rosout
/turtlesim

(Nótese que el nombre con el que se registra un nodo no es necesariamente el mismo nombre con el que se ejecuta el nodo)

Ahora podemos usar “rosnode info turtlesim” y obtener información sobre los tópicos a los que publica el nodo, a los que está suscrito y los servicios que tiene abiertos.

--------------------------------------------------------------------------------
Node [/turtlesim]
Publications:
 * /turtle1/color_sensor [turtlesim/Color]
 * /rosout [rosgraph_msgs/Log]
 * /turtle1/pose [turtlesim/Pose]

Subscriptions:
 * /turtle1/cmd_vel [unknown type]

Services:
 * /turtle1/teleport_absolute
 * /turtlesim/get_loggers
 * /turtlesim/set_logger_level
 * /reset
 * /spawn
 * /clear
 * /turtle1/set_pen
 * /turtle1/teleport_relative
 * /kill

contacting node http://cronidea:36697/ ...
Pid: 8877
Connections:
 * topic: /rosout
    * to: /rosout
    * direction: outbound
    * transport: TCPROS

Existen otras opciones más con las que se puede usar rosnode. Para verlas todas, basta con escribir “rosnode” en la terminal.

Tópicos

Dentro de un robot (o una simulación), son muchos los datos que fluyen de manera constante. Imágenes, datos de acelerómetros, nubes de puntos, estados de motores, necesitan estar siendo publicados continuamente para que el sistema funcione en tiempo real. Especificar canales de comunicación cada vez que un nodo quiere enviarle datos de este tipo a otro nodo no sería eficiente por varias razones: habría que saber de antemano desde donde y hasta donde hay que enviar los datos, hay que controlar la sincronía de los envíos… Para evitar estos problemas, ROS posee una forma de comunicación del estilo Publish/Subscribe [9] en el que un elemento envía mensajes (los publica) y otro elemento recibe mensajes (se suscribe a ellos). En ROS esto se hace a través de tópicos [8]. Los tópicos son un medio de comunicación pensado en la transmisión asíncrona de mensajes. Todos los datos que tengan que ser publicados constantemente y estar disponibles para que otros nodos se publican a través de tópicos.

Los nodos son quienes abren y leen tópicos. Para poder leer o publicar en un tópico, el mensaje entregado tiene que tener un cierto tipo. Existen tipos predefinidos para comunicar distintos datos, como el “geometry_msgs/Twist” que es el mensaje estándar para comandos de movimiento, y también se pueden definir tipos de mensajes de acuerdo a las necesidades de la aplicación.

Rostopic

Rostopic [10] es la herramienta para interactuar con los tópicos de ROS. Tiene funciones similares a las de “rosnode” y otras que son propias de la naturaleza de los tópicos. Una de las funciones más comunes es “rostopic list” que nos muestra la lista de los tópicos abiertos. En este caso la ejecuté habiendo lanzado el simulador de tortuga y me mostró lo siguiente:

/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

“rostopic info” puede mostrar información de un tópico en particular, por ejemplo “rostopic info /turtle1/cmd_vel” muestra:

Type: geometry_msgs/Twist

Publishers: None

Subscribers:
 * /turtlesim (http://cronidea:49932/)

Como sabemos que el tópico cmd_vel de la tortuga recibe comandos del tipo geometry_msgs/Twist (también podríamos haberlo sabido llamando directamente a “rostopic type /turtle1/cmd_vel”), podemos publicar un mensaje en este tópico para que la tortuga se mueva. Para publicar un mensaje usamos “rostopic pub”.
Escribimos:

rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'

y verificamos que la tortuga del simulador se movió en la dirección indicada.

Figura 1: Tortuga moviéndose luego de un mensaje publicado en /turtle1/cmd_vel
Figura 1: Tortuga moviéndose luego de un mensaje publicado en /turtle1/cmd_vel

Para monitorear los mensajes publicados en un tópico, podemos usar “rostopic echo”. Antes de enviar el mensaje al simulador de la tortuga, ejecuté “rostopic echo /turtle1/cmd_vel” en una terminal y me mostró esto:

linear:
  x: 2.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 1.8
---

Existen varias opciones más con las que se puede ejecutar rostopic. Para verlas todas basta con escribir “rostopic” en la consola.

Servicios

Existe otra forma de comunicación en la que sólo en ciertas ocasiones necesitamos que se ejecute un procedimiento o necesitamos que la llamada venga acompañada de una respuesta. Esto en ciertos contextos se llama Remote Procedure Call o RPC, que permite que procesos intercambien información de manera directa. En ROS esta forma de comunicación se lleva a cabo mediante los servicios.

Los servicios se definen mediante un nombre y dos tipos de servicio: uno de consulta y otro de respuesta.

Rosservice

Una de las formas en las que se puede llamar un servicio es mediante rosservice. Imaginemos que estamos corriendo el simulador de tortuga y que queremos añadir una tortuga más a la simulación. Si ejecutamos “rosservice list” podemos ver que hay un servicio “/spawn” abierto.

/clear
/kill
/reset
/rosout/get_loggers
/rosout/set_logger_level
/spawn
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/get_loggers
/turtlesim/set_logger_level

Ejecutando “rosservice info /spawn” podemos ver información sobre este servicio:

Node: /turtlesim
URI: rosrpc://cronidea:59998
Type: turtlesim/Spawn
Args: x y theta name

Ahí se puede ver que este servicio requiere 4 argumentos para llamarse, así que para generar una nueva tortuga podemos escribir:

rosservice call /spawn 5 7 1.57 William

Y tendremos una nueva tortuga llamada William en la simulación.

Figura 2: William, “el musculoso”, ha entrado en escena

Ahora, si llamamos a “rosservice list” podemos ver que se han habilitado servicios nuevos:

/William/set_pen
/William/teleport_absolute
/William/teleport_relative
/clear
/kill
/reset
/rosout/get_loggers
/rosout/set_logger_level
/spawn
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/get_loggers
/turtlesim/set_logger_level

Podemos llamar a uno de ellos para cambiar la posición de William.

rosservice call /William/teleport_absolute 1 1 2.5

Y William juntará su Ki y se teletransportará.

Figura 3: William teletransportado
Figura 3: William teletransportado (It’s over 9000!)

Para ver todas las opciones de rosservice hay que llamarlo en la terminal.

Servidor de Parámetros

El servidor de parámetros es un espacio común para almacenar variables para todos los nodos. Es gestionado por el Master. Para ejemplificar el uso del servidor de parámetros es conveniente usar ejemplos más avanzados, por lo que los detalles pueden quedar para otra ocasión. Un ejemplo a grandes rasgos puede ser el lanzar un robot en simulación. Un robot se puede especificar en un archivo .xacro que es una sintaxis simplificada del estándar .urdf. Un nodo puede procesar el archivo .xacro y generar el .urdf correspondiente. Este archivo quedará en el servidor de parámetros, desde donde un nodo puede enviarlo al simulador. Así mismo en el servidor de parámetros se pueden guardar valores como por ejemplo las constantes PID del controlador de un motor.

Rosparam

Rosparam [12] es la herramienta estándar para interactuar con el servidor de parámetros desde la línea de comandos. Si llamamos a “rosparam list” vemos todos los parámetros almacenados. Al abrir el roscore se muestra algo así:

/rosdistro
/roslaunch/uris/host_cronidea__36626
/rosversion
/run_id

Lo mismo que antes: si queremos ver todas las opciones, escribimos rosparam en la consola.

Para ir cerrando

Así como para ir terminando el tutorial, estos son los elementos principales con los que se desarrollan las aplicaciones de ROS. Cuando se quieren desarrollar aplicaciones nuevas, generalmente el proceso consiste en implementar un paquete y programar sus nodos usando las bibliotecas disponibles de ROS (hasta donde sé, lo más común es trabajar con C++ y Python). Espero que a alguien le sea de utilidad este paseo por los elementos principales y que se motive a seguir aprendiendo. En tal caso, yo me motivaré a hacer más tutoriales.

Referencias

  1. Roscore: http://wiki.ros.org/roscore
  2. ROS Master: http://wiki.ros.org/Master
  3. ROS Parameter Server: http://wiki.ros.org/Parameter%20Server
  4. Rosout: http://wiki.ros.org/rosout
  5. ROS Nodes: http://wiki.ros.org/Nodes
  6. ROS Packages: http://wiki.ros.org/rosbuild/Packages
  7. Rosnode: http://wiki.ros.org/rosnode
  8. ROS Topics: http://wiki.ros.org/Topics
  9. Publish Subscribe Pattern: https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
  10. Rostopic: http://wiki.ros.org/rostopic
  11. ROS Parameter Server: http://wiki.ros.org/Parameter%20Server
  12. Rosparam: http://wiki.ros.org/rosparam

Entrando al mundo de la robótica contemporánea con ROS

En los últimos años, la robótica ha dado saltos tremendos que si bien, no ha alcanzado esas sociedades extremadamente automatizadas que nos muestra la ciencia ficción (R. Daneel Olivaw [1], la humanidad necesita tu apoyo), ha logrado grandes progresos y contribuciones y, de lo que trata el tema de hoy, ha motivado a más gente a entrar al mundo de la robótica.

Es común ver, cuando se habla de educación en robótica, que en carreras de orientación tecnológica y que no necesariamente son de especialización robótica se hacen talleres en los que los participantes pueden interactuar con pequeños robots, o armar los suyos propios con diversos sensores y actuadores. Esto es una experiencia altamente recomendable y disfrutable para personas con cualquier nivel de experiencia, incluso niños o personas sin formación en áreas tecnológicas. Pero cuando uno quiere profundizar e investigar en el área se pregunta, ¿cómo se puede cruzar esa barrera que separa el taller de robótica de la robótica comercial que uno ve en la TV o en internet? Bueno, hacer un primer acercamiento no es tan difícil.

Las personas que me conocen de cerca me habrán escuchado hablar en algunas ocasiones sobre el Software Libre y la importancia de las tecnologías abiertas. Aún hay quien dice que las tecnologías abiertas no son oportunidades para competir en el mercado… pero yo creo que a esas personas les falta un poco de visión de mundo. Basta considerar el hecho anunciado recientemente de que 486 de los 500 supercomputadores más rápidos del mundo funcionan con Linux [2][3]. Bajo mi perspectiva, vivimos en una sociedad tan interconectada que es imposible que un sólo individuo se adueñe de una idea. Las ideas complejas que benefician a mucha gente, se desarrollan desde una colectividad, ya que es en la colectividad dónde pueden nutrirse y refinarse a una velocidad que las hace usables. Así, vamos poco a poco pasando de una economía enfocada en productos (ideas desde un único agente) a una economía basada en servicios, o sea, “qué es lo que hacemos con una idea”. Y en el mundo de la robótica hace tiempo se dieron cuenta de esto, de que el avance es demasiado rápido como para intentar hacerlo todo desde un único lugar para poder competir. Así es como nació ROS, uno de los frameworks de robótica más robustos y populares en la actualidad.

ROS: Robotic Operating System

Los orígenes de ROS se remontan al Laboratorio de Inteligencia Artificial de Stanford y posteriormente su desarrollo fue acogido por la incubadora de robótica Willow Garage [4]. Estos esfuerzos han dado origen a una gran comunidad y a un conjunto de herramientas abiertas que permiten avanzar sin tener que reinventar la rueda.

Logo ROSTécnicamente, ROS no es un sistema operativo. Generalmente, se lo considera un middleware o un framework y ambas expresiones son más acertadas. Si hablamos de sistemas operativos, lo común es que asociemos ROS a Ubuntu, que es donde funcionan la mayoría de sus aplicaciones. Existen iniciativas para utilizar ROS junto a otros sistemas operativos, pero lo más común es usar ROS junto con Ubuntu (existen formas de instalarlo en Fedora, pero no todos los paquetes funcionan [5]), así que está de más decir que es un framework pensado para trabajar en Linux (¡yay!).

ROS funciona en un sistema basado en nodos. Tenemos un nodo maestro que gestiona las comunicaciones entre los distintos nodos que se van conectando. Cada nodo puede ser un programa que implemente las bibliotecas de ROS y desarrolle una función puntual, así se logran diseños modularizados en los que el sistema general no depende de manera crítica en la presencia de un componente (“Por una yegua coja no vamos a perder la trilla.”). Y esto último es algo fundamental en la robótica, ya que el sistema debe ser robusto a errores imprevistos. Incluso, los nodos pueden ejecutarse en varias máquinas para aligerar el cómputo y desarrollarlo de manera distribuida.

rqt_graph: aplicación de ROS que permite ver los nodos funcionando y su comunicación mediante los tópicos
Figura 1: rqt_graph, una aplicación de ROS que permite ver los nodos funcionando y su comunicación mediante los tópicos

La comunicación en ROS se realiza mediante tópicos y servicios. Los tópicos son canales de comunicación al estilo publisher-subscriber [6]. Un nodo puede abrir un tópico en el que publica un tipo de dato particular. Por ejemplo, yo tengo un dron Parrot AR.Drone 2.0 (ya hablaré de esa criatura, ahora que llegó su repuesto y puedo volver a usarlo <3) que mediante un nodo llamado ardrone_autonomy [7] puede publicar las imágenes que se reciben del dron en el tópico ardrone/front/image_raw. Luego otra aplicación se comporta como suscriptor de ese tópico y lee los datos publicados. Así se separa el proceso de producir datos y de procesarlos, lo que hace posible el que los distintos nodos estén desarrollados en distintos lenguajes de programación. Existen tipos de datos predefinidos para levantar tópicos para las distintas labores habituales en la comunicación en robótica, ya sea recibir datos de sensores o publicar comandos de control. Además, se pueden definir nuevos tipos de mensajes de acuerdo a las necesidades particulares de cada aplicación.

Los servicios son interfaces que un nodo abre a una determinada función. Otro nodo cliente, puede realizar una llamada a un servicio y solicitar que otro nodo realice una tarea, e incluso que envíe cierta información de vuelta. A diferencia de los tópicos, que están constantemente publicando independientemente de si son leídos o no, los servicios funcionan de acuerdo a cuando son llamados. Un ejemplo de servicio es el abierto por el simulador Gazebo, que permite reiniciar una simulación, devolviendo todos los robots instanciados a sus posiciones originales; para ejecutarlo, basta con hacer una llamada a /gazebo/reset_simulation. También hay tipos de mensajes para los servicios y se pueden desarrollar otros nuevos.

Además, ROS cuenta con diversas herramientas que ayudan a trabajar sobre esta arquitectura orientada a nodos. Por ejemplo, están las herramientas rqt, como rqt_graph (Figura 1) que permite ver un grafo con todos los nodos funcionando y los tópicos como flechas desde los nodos que publican hacia los nodos que están suscritos.

También ROS cuenta con un servidor de parámetros que guarda variables y datos que sean comunes a todo el entorno que se está ejecutando, como por ejemplo el modelo de un robot que se esté simulando o las especificaciones de un robot real con el que se esté conectado.

Instalar ROS

El desarrollo de ROS se ha enfocado fuertemente en Ubuntu como plataforma principal. Existen otras plataformas sobre las que se puede instalar [8], pero yo tengo una instalación sobre Ubuntu, así que sobre esta puedo hablar con conocimiento de causa. ROS se basa en distribuciones, que son cambios mayores en sus herramientas principales junto con conjuntos de paquetes compatibles con estas herramientas. Las distribuciones llevan nombres de acuerdo a las letras del abecedario y sus logos tienen que ver con tortugas (creo que tiene que ver con que el ejemplo más básico en ROS se basa en gráficos de tortuga [9]). La versión actual es Jade Turtle, pero yo tengo instalada la versión anterior, que es Indigoo Igloo y es una de las versiones con más soporte (y sobre la que se desarrollaron unos módulos para el dron con los que quiero empezar a trabajar). Para comenzar, yo creo que es recomendable instalar cualquiera de las dos ya que la mayoría del desarrollo se ha estandarizado. Por ahora, para seguir con el conocimiento de causa, vamos con Indigo.

Instalar ROS en Ubuntu es relativamente sencillo si se hace instalando desde los repositorios. Se puede instalar desde los archivos fuente, pero no se recomienda a menos que la ocasión lo requiera. Los pasos de la instalación se pueden encontrar en inglés en [11].

Mi versión de Ubuntu es la 14.04.2 (Trusty Tahr). Indigo es compatible con las versiones 13.10 y 14.04.

Para comenzar la instalación, hay que asegurarse de que los repositorios permitan la instalación desde los componentes “restricted”, “universe” y “multiverse”. En mi instalación venían habilitados por defecto, así que no tuve que hacer ningún cambio. En caso de necesitar configurarlos, se pueden seleccionar en “Sistema > Administración > Orígenes del Software”o revisar el artículo [10].

Luego se configura el sistema para que acepte paquetes desde packages.ros.org y se acepta su clave:

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net --recv-key 0xB01FA116

Es conveniente revisar que el índice de paquetes esté actualizado ejecutando:

sudo apt-get update

Luego, basta instalar el paquete que incluye todas las aplicaciones de ROS. El paquete más completo, y el que yo instalé, es ros-indigo-desktop-full que incluye todas las herramientas gráficas y las bibliotecas genéricas de robótica.

sudo apt-get install ros-indigo-desktop-full

También existen alternativas que incluyen menos cosas (porque creo recordar que esa versión pesaba como un giga) como ros-indigo-desktop y ros-indigo-ros-base.

sudo apt-get install ros-indigo-desktop

Además, se pueden instalar los paquetes de manera individual de la forma:

sudo apt-get install ros-indigo-NOMBRE_DEL_PAQUETE

Para gestionar las dependencias de paquetes de ROS que vayamos a instalar, se debe instalar rosdep. Esto sirve cuando vayamos a probar algún paquete descargado como código fuente.

sudo rosdep init
rosdep update

Para dejar el sistema listo para usar, hay que registrar las variables de entorno de ROS. Esto se puede hacer añadiendo un archivo de configuración predeterminado al final del archivo .bashrc.

echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc
source ~/.bashrc

Para seleccionar las variables de entorno de una versión particular de ROS, se puede ejecutar source con el archivo predeterminado.

source /opt/ros/indigo/setup.bash

Otro paquete conveniente de instalar es rosinstall que permite instalar aplicaciones y gestionar sus dependencias de manera más automática.

sudo apt-get install python-rosinstall

Para verificar que todo está instalado y funcionando, abrimos una terminal y escribimos “roscore” y presionamos Enter. Deberíamos ver lo siguiente:

... logging to /home/bruno/.ros/log/70729674-2d80-11e5-9a3d-b803058568da/roslaunch-cronidea-3040.log
Checking log directory for disk usage. This may take awhile.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is &lt;1GB.

started roslaunch server http://cronidea:60080/
ros_comm version 1.11.13

SUMMARY
========

PARAMETERS
 * /rosdistro: indigo
 * /rosversion: 1.11.13

NODES

auto-starting new master
process[master]: started with pid [3053]
ROS_MASTER_URI=http://cronidea:11311/

setting /run_id to 70729674-2d80-11e5-9a3d-b803058568da
process[rosout-1]: started with pid [3066]
started core service [/rosout]

Esto significa que lanzamos el nodo principal (master) de ROS y sus componentes, y que ahora podemos empezar a abrir otros nodos.

Un primer ejemplo de funcionamiento

Para usar ROS, es necesario que esté corriendo el roscore. Si lo hicimos funcionar como se mencionó recién, ahora abrimos otra consola y lanzamos el nodo del simulador de tortuga. Este simulador dibuja una tortuga en la pantalla y queda a la escucha de comandos de control para moverla en dos dimensiones. Esta tortuga recibe mensajes del tipo “Twist” en el tópico “/turtle1/cmd_vel”, que es el tipo de mensajes estándar que se utiliza para controlar un robot. Puede servir para hacer demostraciones o incluso poner a prueba ciertos algoritmos.

Para lanzar el simulador de tortuga, escribimos:

rosrun turtlesim turtlesim_node

Las aplicaciones de ros se organizan en paquetes, los que pueden contener varios nodos. En este caso “rosrun” busca en el paquete “turtlesim” el nodo “turtlesim_node”. Si todo funciona bien, deberíamos ver en la consola algo como:

[ INFO] [1437246767.585809489]: Starting turtlesim with node name /turtlesim
[ INFO] [1437246767.603619905]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

y una imagen con el simulador funcionando.

Figura 2: turtlesim_node funcionando
Figura 2: turtlesim_node funcionando

Al lanzar el simulador de tortuga, se abrirán nuevos tópicos. El simulador abre el tópico mencionado antes “/turtle1/cmd_vel” en el que recibe comandos de movimiento, y dos tópicos más: “/turtle1/color_sensor” y “/turtle1/pose” en los que publica el color del pixel en el que está y la posición que tiene en la imagen. Para ver los tópicos actualmente abiertos, se puede ejecutar en otra terminal:

rostopic list

y aparecerá algo como:

/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

Ahora que la tortuga está suscrita a un tópico en el que recibe comandos, podemos publicar comandos a ese tópico. Para hacer un primer ejemplo, usaremos el nodo “turtle_teleop_key” que viene en el paquete “turtlesim”. Este nodo recibe las entradas de las flechas del teclado y publica mensajes para controlar la tortuga.

rosrun turtlesim turtle_teleop_key

Deberíamos ver:

Reading from keyboard
---------------------------
Use arrow keys to move the turtle.

Usando las teclas, la tortuga comenzará a moverse.

Figura 3: turtlesim_node recibiendo comandos de control
Figura 3: turtlesim_node recibiendo comandos de control

Si abrimos otra consola y lanzamos rqt_graph, podemos ver todos los nodos funcionando y sus respectivos tópicos.

rosrun rqt_graph rqt_graph
Figura 4: rqt_graph con los nodos y tópicos de la tortuga
Figura 4: rqt_graph con los nodos y tópicos de la tortuga

Dónde seguir

En este tutorial, mostré un poco sobre cómo funciona ROS, uno de los frameworks de robótica más populares en la actualidad. Para ver su popularidad, basta considerar la cantidad de robots del DARPA Robotics Challenge que lograron grandes resultados usándolo (18 de 23 equipos [12]). Estos mismos principios mostrados en el breve ejemplo de la tortuga, son los que subyacen a toda la arquitectura de ROS y permiten desarrollar complejas aplicaciones distribuidas de alto desempeño.

Para seguir aprendiendo, los pasos siguientes son profundizar en los nodos, los tópicos, los servicios y aplicaciones como el simulador Gazebo o el visualizador RViz. Un buen punto de partida son los tutoriales oficiales de ROS [13]. Además, pretendo hacer más tutoriales para ir profundizando en varios temas que he aplicado.

Referencias

  1. R. Daneel Olivaw en Wikipedia: https://es.wikipedia.org/wiki/R._Daneel_Olivaw
  2. “It’s Official: Linux is the King of Supercomputing”: http://fossbytes.com/supercomputer-operating-system-linux/
  3. Top 500 Supercomputers: http://www.top500.org/statistics/details/osfam/1
  4. Historia de ROS: http://www.ros.org/history/
  5. Instalando ROS en Fedora 19: http://www.jann.cc/2013/04/29/building_ros_on_fedora_19.html
  6. Patrón Publisher-Subscriber: https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
  7. Nodo ROS ardrone_autonomy: http://ardrone-autonomy.readthedocs.org/en/latest/
  8. Instalación de ROS Indigo: http://wiki.ros.org/indigo/Installation
  9. Gráficas Tortuga: https://es.wikipedia.org/wiki/Gr%C3%A1ficas_tortuga
  10. Repositorios de Ubuntu: http://doc.ubuntu-es.org/Repositorios_desde_Ubuntu
  11. Instalación de ROS en Ubuntu: http://wiki.ros.org/indigo/Installation/Ubuntu
  12. ROS y la revolución en la robótica: http://www.zdnet.com/article/long-live-ros-why-the-robotics-revolution-is-being-driven-by-open-source-development/
  13. Tutoriales de ROS: http://wiki.ros.org/ROS/Tutorials/

Una selfie al estilo de la Inteligencia Artificial de Google

Hace algunos días llegó a mi correo un artículo [2] de pyimagesearch.com, en sitio de tutoriales sobre imágenes en Python del que ya he aprendido bastantes cosas. En este artículo hablaban sobre “la creación de imágenes artísticas usando sueños guiados” en base a los “Deep Dreams” o “Sueños Profundos” de Google, los que podemos describir como imágenes obtenidas desde las redes de Deep Learning de Google. Una explicación bastante completa de este tema puede ser encontrada en [3], pero para contextualizar podemos decir a grandes rasgos que Google está utilizando técnicas de Inteligencia Artificial para aprender patrones de una gran cantidad de imágenes (una GRAN cantidad de imágenes) para luego poder describir imágenes nuevas en base a los patrones aprendidos. Esto es lo que se conoce como Deep Learning, o el aprendizaje de patrones desde Redes Neuronales Artificiales de gran profundidad, que ya ha dado grandes logros como la descripción automatizada de imágenes en base a texto [7].

Una de las razones por las que se volvió llamativa la nueva investigación de Google para el público, fue que mediante estos modelos generados por el Deep Learning, se hizo el proceso inverso a la obtención de patrones. Es decir, en base a una imagen resultante, se le pide al sistema obtener los patrones que componen la imagen. Esto dio como resultado imágenes muy bizarras en las que la imagen original se convierte en una combinación de líneas y figuras, y luego de formas como ojos o caras de animales. La explicación completa desde Google se puede leer (en inglés) en [8].

Fuente: GoogleResearch Blog
Fuente: GoogleResearch Blog [8]

En el artículo que llegó a mi correo, se habla de un módulo de Python que permite utilizar este modelo generado por Google para producir una imagen usando otra imagen como guía. O sea, usamos una imagen de base y usamos otra imagen que orienta la búsqueda de patrones en el modelo de Google para que la imagen que entregamos se exprese como una combinación de patrones relacionados a la imagen que usamos de guía.

El módulo de Python que se nombra es bat-country, que se presenta en un artículo anterior del mismo sitio [4]. Por curiosidad, intenté instalarlo y aproveché de hacer un tutorial para registrar la experiencia.

Instalación de bat-country

Hay varias cosas que instalar antes de hacer el experimento. Voy a partir por lo que me jodió por más tiempo: instalar bat-country.

Instalar bat-country no es difícil, pero me quitó tiempo porque sus dependencias se demoran un rato en instalar (matplotlib, te estoy mirando a ti) y si uno no tiene ciertas dependencias de las dependencias, la instalación falla al final y hay que descargar todo de nuevo (sí, matplotlib, te sigo mirando a ti). Las dependencias de matplotlib que me hicieron tener que descargar todo de nuevo son freetype y png. Para los usuarios de sistemas debian como Ubuntu, encontré un tutorial de cómo instalarlas [6], pero como yo uso Fedora, los instalé con yum:

sudo yum install freetype-devel libpng-devel

Otra cosa que tuve que instalar antes de ejecutar el script de instalación de bat-country, fue scipy. Scipy es un conjunto de herramientas científicas para Python, entre las que se incluye el conocido módulo Numpy. Intenté instalar Scipy mediante pip, pero se puso pesado buscando otros módulos, así que como era tarde y yo quería ver luego si el experimento resultaba, lo instalé directamente desde Yum:

sudo yum install scipy

Ahora que estamos seguros de que la ausencia de freetype y png no nos va a obligar a descargar matplotlib de nuevo, empezamos a instalar bat-country (entiéndanme, con mi internet fue todo un asunto el descargar todo otra vez). Existen dos formas de instalar bat-country. Una es usando directamente los repositorios de pip, la herramienta para instalar módulos de Python. La otra es descargando directamente bat-country desde el repositorio de GitHub (y luego usando pip. Me gusta decir pip, suena chistoso).

Para instalar bat-country mediante pip:

pip install bat-country

Para instalar bat-country desde GitHub:

#Nos ubicamos en alguna carpeta de nuestro sistema y descargamos el repositorio
git clone https://github.com/jrosebr1/bat-country.git
#Descomprimimos el archivo
cd bat-country
pip install -r requirements.txt
python setup.py install

Mientras se instalan las dependencias del archivo requirements.txt podemos ir a darnos una vuelta por ahí, beber algo (esta vez fue sólo Pepsi) o ponernos a perder el tiempo en algo improductivo como Agar.io (aunque no lo hice esta vez. Fucking lag).

Ahora tenemos la biblioteca mágica instalada, ¿estamos listos para probar la magia?

Instalando Caffe [8]

La verdad es que no estamos listos. El poder utilizar un modelo de Deep Learning no es una cosa sencilla, pues se requiere una gran arquitectura funcionando por debajo. En este caso, bat-country se basa en el framework Caffe, desarrollado en Berkeley.

Caffe es un framework de Deep Learning. Es un conjunto de herramientas diseñado para implementar aplicaciones al nivel del estado-del-arte en aprendizaje automatizado, capaz de funcionar con un altísimo desempeño aprovechando al máximo los recursos de la máquina. Tiene la opción de funcionar sobre GPU, pero este no es mi caso… -se va a un rincón y llora-.

Para instalar Caffe, debemos descargarlo desde GitHub y compilarlo en nuestra máquina. Para eso, nos ubicamos en alguna carpeta en la que queramos instalarlo. Yo tengo una carpeta llamada src en mi carpeta personal para probar programas, así que lo descargué ahí.

cd ~/src
git clone https://github.com/BVLC/caffe.git
#Descomprimimos el archivo y entramos a la carpeta
cd caffe-master

Antes de compilar Caffe, se deben instalar algunas dependencias. Estas dependencias salen explicadas en la página oficial de instalación de Caffe. Aquí hay un tutorial para Ubuntu [10] y uno para Fedora, que es el que pongo a continuación [11].

sudo yum install protobuf-devel leveldb-devel snappy-devel opencv-devel boost-devel hdf5-devel
#Existen otras dependencias que se aplican para versiones recientes
sudo yum install gflags-devel glog-devel lmdb-devel
#Si no se encontraron ciertas dependencias, se pueden instalar las siguientes
# glog
wget https://google-glog.googlecode.com/files/glog-0.3.3.tar.gz
tar zxvf glog-0.3.3.tar.gz
cd glog-0.3.3
./configure
make && make install
# gflags
wget https://github.com/schuhschuh/gflags/archive/master.zip
unzip master.zip
cd gflags-master
mkdir build && cd build
export CXXFLAGS="-fPIC" && cmake .. && make VERBOSE=1
make && make install
# lmdb
git clone git://gitorious.org/mdb/mdb.git
cd mdb/libraries/liblmdb
make && make install

Para que Caffe funcione, necesita un módulo BLAS (Basic Linear Algebra Subprograms) funcionando en el sistema. Este tipo de módulos sirven para paralelizar operaciones vectoriales y matriciales a bajo nivel. No me manejo en el tema, así que no voy a intentar profundizar mucho (igual les dejo el artículo de Wikipedia, por si acaso: [12]). Existen varias alternativas para instalaciones de módulos BLAS como ATLAS, OpenBLAS y MKL (que es específico para CPUs Intel). El que viene por defecto en Caffe es ATLAS, pero para hacer la instalación más rápida, yo instalé OpenBLAS desde Yum:

sudo yum install openblas openblas-devel

(Nótese que es importante instalar openblas-devel, creo que openblas-dev en sistemas Debian, porque ahí se incluyen las bibliotecas que Caffe importará al compilar).

Ahora deberíamos estar dentro de la carpeta de caffe (caffe-master en mi caso) para empezar la compilación. Para compilar, necesitamos primero copiar la plantilla del Makefile que se incluye en Caffe y configurar ciertos parámetros. Primero, debemos seleccionar si utilizaremos la GPU o la CPU. Luego debemos seleccionar qué módulo de BLAS utilizaremos y luego indicar las rutas de las bibliotecas del módulo BLAS.

#Copiamos la plantilla de Makefile incluida en la carpeta de Caffe
cp Makefile.config.example Makefile.config
#Luego se configura el Makefile (explicación más abajo)
#Se ejecutan las instrucciones make
make all
make test
make runtest

Las configuraciones que hay que hacer en el Makefile son:

  1. Seleccionar la opción de compilar solamente con CPU (si tienen GPU pueden saltarse este paso).  Para eso hay que descomentar la línea que dice CPU_ONLY := 1
  2. Seleccionar el módulo BLAS. Bajo una línea que dice “#BLAS choice:” hay que modificar para que diga “BLAS := open” si estamos usando openblas, o las opciones que ahí salen indicadas para el módulo correspondiente de BLAS.
  3. Si cambiamos la opción de BLAS que viene por defecto, debemos indicar las rutas en las que se encuentran las bibliotecas a incluir. En mi caso, tuve que especificar estas dos líneas (recuerdo que así es para Fedora. Para otro sistema quizás las rutas sean distintas) “BLAS_INCLUDE := /usr/include/openblas/” y “BLAS_LIB := /usr/lib/”.

Habiendo hecho lo anterior, se ejecutan los comandos make y se espera que pase lo mejor. Si no aparece ningún error, significa que al fin Caffe está compilado y listo para funcionar.

Preparar los wrappers de Python (pycaffe)

No se desanimen, esto es cortito. Para poder correr bat-country, se deben activar los wrappers de Python. Los wrappers son funciones que encapsulan la llamada a una aplicación para usarse en otro lenguaje. Esto nos deja llamar a Caffe desde Python.

Primero debemos instalar las dependencias de Python necesarias para hacer funcionar Caffe (recuerden que seguimos dentro de la carpeta de Caffe). Aquí vuelve a aparecer el compadre pip:

for req in $(cat requirements.txt); do pip install $req; done

Luego, añadimos la ruta de Caffe a la variable de entorno PYTHONPATH para que Python pueda encontrar el módulo de Caffe. Ejecutamos lo siguiente en la consola:

export PYTHONPATH=$HOME/src/caffe-master/python:$PYTHONPATH

Esta instrucción modificará la variable de entorno sólo mientras la consola esté abierta. Para no tener que ejecutarla siempre, añadí esa instrucción al final de mi archivo .bashrc, lo que es totalmente opcional.

Después de esto, llamamos a make para generar el wrapper pycaffe:

sudo make pycaffe

Con esto ya podemos usar Caffe desde Python, y a la vez, el módulo bat-country.

Usando bat-country

En [8] se muestra el siguiente código, con el que se puede probar el módulo bat country:

# import the necessary packages
from batcountry import BatCountry
from PIL import Image
import numpy as np
import argparse

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-b", "--base-model", required=True, help="base model path")
ap.add_argument("-l", "--layer", type=str, default="inception_4c/output",
	help="layer of CNN to use")
ap.add_argument("-i", "--image", required=True, help="path to base image")
ap.add_argument("-g", "--guide-image", required=True, help="path to guide image")
ap.add_argument("-o", "--output", required=True, help="path to output image")
args = ap.parse_args()

# we can't stop here...
bc = BatCountry(args.base_model)
features = bc.prepare_guide(Image.open(args.guide_image), end=args.layer)
image = bc.dream(np.float32(Image.open(args.image)), end=args.layer,
	iter_n=20, objective_fn=BatCountry.guided_objective,
	objective_features=features)
bc.cleanup()

# write the output image to file
result = Image.fromarray(np.uint8(image))
result.save(args.output)

Ese código se debe guardar en un archivo .py. Antes de ejecutarlo hay que hacer una última cosa. No se puede empezar la fiesta si no está el anfitrión principal: el modelo de Deep Learning generado por Google. Este modelo se puede descargar mediante Caffe. Para descargar el modelo abrimos una consola y nos ubicamos en la carpeta donde quedó Caffe. Para descargar el modelo ejecutamos un script que viene incluido de antemano:

./scripts/download_model_binary.py models/bvlc_googlenet/

Ese  código se encargará de descargar el modelo y guardarlo en la carpeta “models” como la carpeta “bvlc_googlenet”.

Ahora ya podemos ejecutar bat-country. Para ejecutar el código, se necesita especificar cuatro cosas:

  1. El modelo base: Aquí es donde especificamos el modelo con el que vamos a procesar las imágenes. Lo especificamos con “-b nombre_de_modelo” o “–base-model nombre_de_modelo”. Este “nombre_de_modelo” debe ser la ruta de la carpeta del modelo que vamos a usar. Para eso se puede apuntar con la ruta completa a la carpeta “models/bvlc_googlenet” dentro de Caffe, pero para no hacer la ruta tan larga, yo copié la carpeta “bvlc_googlenet” a la misma carpeta del script de Python. Al final esta parte quedó como “-b bvlc_googlenet”.
  2. La imagen de entrada: Esta imagen de entrada es la que queremos “interpretar” por el modelo. En este caso yo me tomé una foto (a fin de cuentas, todo esto fue con el fin de conseguir una selfie engorrosa y poco convencional) y la especifiqué con “-i imagen.jpg”.
  3. La imagen de guía: Para que la búsqueda de patrones para describir la imagen no sea completamente exhaustiva, le entregamos una imagen de guía, que va a darle prioridad a los patrones que se encuentren dentro de ella. Por ejemplo, si ponen una imagen de ojos, se puede conseguir una de esas composiciones bizarras en las que las figuras se componen de muchas formas de ojos. Se especifica con “-g imagen”
  4. La imagen de salida: Bueno, la descripción lo dice todo. Se especifica con “-o archivo_de_salida”.

En una línea de código, y asumiendo que el archivo de Python se llama bat-country-test.py y las imágenes están en la misma carpeta, se puede ejecutar todo con:

python bat-country-test.py -b bvlc_googlenet -i input.jpg -g guide.jpg -o output.jpg

Después hay que esperar un rato. Para generar la imagen de abajo, en mi PC se demoró como 400 segundos.

En mi caso, usé las siguientes imágenes:

Primero, una selfie como imagen de entrada.

Sé que tengo cara de zombie. Pero no se puede pedir mucho a las tres de la mañana.
Sé que tengo cara de zombie. Pero no se puede pedir mucho a las tres de la mañana.

Luego, pillé una imagen de un bosque en Google. Me gustan los bosques. Esta fue la imagen de guía.

¿Cierto que son bonitos los bosques?
¿Cierto que son bonitos los bosques?

Y taratarán… esta cosa fue lo que resultó.

No, no hagan chistes sobre Jesús. Mantengamos este blog como un espacio laico, por favor.
No, no hagan chistes sobre Jesús. Mantengamos este blog como un espacio laico, por favor.

Conclusión

Sí, es una forma terrible de conseguir una foto de perfil. Lo interesante, es que a través de todo este experimento me pude acercar a un montón de tecnologías nuevas. Sirvió un poco para ver cómo un modelo de aprendizaje profundo puede lograr reconstruir imágenes en base a patrones con los que se haya entrenado. Hasta ahora era muy poco lo que se conocía sobre los patrones generados internamente en las redes neuronales artificiales. El hecho de poder visualizarlas, ayuda de gran manera a que podamos entender mejor su funcionamiento y sus capacidades, y también, por qué no, entender un poco mejor las redes neuronales naturales que tenemos en la ca’eza.

Referencias:

  1. pyimagesearch.com
  2. Generating art with deep guided dreaming
  3. Las increíbles imágenes de los sueños de la inteligencia artificial de Google
  4. bat-country: an extendible, lightweight Python package for deep dreaming with Caffe and Convolutional Neural Networks
  5. bat-country en GitHub
  6. Matplotlib and its local dependencies
  7. How Google “Translates” Pictures into Words Using Vector Space Mathematics
  8. http://googleresearch.blogspot.com/2015/06/inceptionism-going-deeper-into-neural.html
  9. Caffe
  10. Instalación de Caffe para Ubuntu
  11. Instalación de Caffe para Fedora
  12. BLAS en Wikipedia

Creando una revista con Software Libre – Parte 1: Planificación General

Una revista es un proyecto serio. Debemos partir aclarando eso. Aunque el contenido de una revista sea lo menos serio del mundo, el proceso de desarrollarla y tenerla lista dentro de los plazos implica planificación. Por eso dedicaré un capítulo a ciertos temas referentes a la planificación de una revista.

Planificación General

Primero, al iniciar una revista, se debe definir con claridad el objetivo que se persigue con ella, la forma que adoptará el equipo de trabajo y las responsabilidades que cada uno tomará. Además, es opcional añadir una sección en la que se defina el protocolo a seguir para publicar en la revista. El nivel de rigidez que uno tenga en su plan general dependerá del tamaño del proyecto y de la cercanía con la gente con la que uno trabaje.

Como inspiración para el plan general se estudió el lineamiento editorial usado en la HDMagazine donde definen muy prolijamente los objetivos que persiguen y su método de trabajo.

Para realizar un documento de plan general se puede usar cualquier método de edición de documentos. Pero como no hay nada más maravilloso que el Software Libre, usaremos LibreOffice Writer para crear el documento.

Es recomendable hacer el documento con bastante cuidado y usando bien los títulos para que luego sea fácil de navegar. Para esto es importante marcar cada título con su correcto formato y usando los distintos niveles para organizar subsecciones. Para dar formato de título basta con seleccionar el texto y en el estilo seleccionar un tipo de encabezado.

Creando un encabezado
Creando un encabezado

Quizás a alguien le pueda parecer algo para principiantes el explicar esto, pero es importante mencionarlo porque así el documento queda mucho más legible y organizado si se desea pasar a PDF y compartir con otros miembros del equipo. No se debe olvidar el usar distintos niveles de encabezados, porque al generar el índice automático (razón por la que usamos los estilos de títulos) los distintos niveles de encabezados harán un índice mucho más fácil de navegar. El nivel mayor es el 1 (para el título del documento) y luego van aumentando mientras aumenta lo específico del tema designado por el título.

Para generar un índice automático basta ir a Insertar -> Índices y Tablas -> Índices, con lo que aparece un menú de configuración en el que se le podrá dar un título y al cliquear Aceptar se generará un índice automático en base a todos los títulos.

Índice Automático
Crear un índice automático

Al generar el índice, aparecerán todos los títulos del texto indentados de manera adecuada y su respectiva página:

Índice Generado
Índice Generado

Al seleccionar la opción “Exportar en formato PDF” en el menú “Archivo”, se generará un PDF con un índice que permitirá recorrer el documento simplemente haciendo clic en las secciones.

Índice en el documento PDF
Índice en el documento PDF

Si se hace cualquier modificación en los títulos del documento, basta hacer clic derecho en el índice y seleccionar “Actualizar Índice/Tabla” para que los contenidos se actualicen.

Ahora pasemos a lo verdaderamente importante: el contenido del documento.

Como lo mencioné, lo que pongamos en el plan general depende del nivel de rigidez con el que vayamos a dirigir la creación de la revista. Pero para tener un inicio, aquí dejaré las secciones generales que nosotros estamos usando para nuestro proyecto.

Sobre la Revista

Aquí se incluye una descripción general sobre el objetivo de la revista, los principios y valores que regirán su producción o en su defecto la “misión” y la “visión” del proyecto, además del público al que se dirige.

Lineamientos temáticos

Aquí se define el tipo de contenido que va a ser incluido en la revista, para así definir un marco de trabajo claro para la edición. Es importante definir bien esto, porque así los redactores no se desvían del tema principal de la revista y si lo hacen, existe un respaldo para decirles que su trabajo está equivocado y reducir los conflictos.

Equipo de trabajo y tipos de colaboradores

Aquí se define la estructura que tendrá el equipo de trabajo que participará de la revista y las responsabilidades de cada uno.

Generalmente la dirección está compuesta por un director general que es responsable de coordinar el funcionamiento general de la revista y asegurarse de que los plazos se cumplan, un editor en jefe que se encargue de que el contenido publicado sea el adecuado y un director de arte que se encargue de vigilar el aspecto estético de la revista. Como nosotros tenemos una filosofía más comunitaria al movernos en torno al software libre, formamos un consejo editorial en el que nos repartimos esas tres labores de acuerdo a como sea necesario en el momento. Además se pueden añadir cargos como “comunicaciones” o “marketing” si es que son necesarios.

Luego se definen los tipos de redactores. En nuestro caso consideramos dos grupos: un grupo permanente que se compromete a contribuir con un artículo por cada edición de la revista, para asegurar un mínimo de contenido, y un grupo de redactores temporales que redactan artículos para ciertas ediciones en particular y no adquieren mayor compromiso con el equipo de la revista.

Sobre el proceso de desarrollo de una edición

Aquí se definen las “burocracias”. Es importante aclarar la forma de enviar las contribuciones a la revista y los parámetros con los que se van a evaluar, para así evitarse problemas a largo plazo y asegurar que en la revista se publicará contenido de calidad.

Sobre los medios de difusión

Aquí se describen, como su nombre lo dice, los medios a través de los que se distribuirá la revista cuando esté terminada. Pueden ser medios impresos o digitales. Además, si es necesario, se puede asignar un responsable de distribución que se encargue de coordinar este proceso.

Esas son las secciones que yo considero importantes. Eso no niega que en el futuro pueda añadir otras o modificarlas. Uno siempre debe preocuparse de que toda la organización sea en pos de que la revista sea desarrollada adecuadamente y no se debe tomar la creación de un plan general como una obligación, sino como un complemento para mejorar el ambiente de trabajo.

Para terminar esta edición del tutorial, dejo disponible para descargar la plantilla del plan general que yo tengo por si alguien quiere usarla. Es un archivo .odt que pueden descargar haciendo clic Aquí.

En la próxima edición de este tutorial también tendremos el tema de la planificación. Pero ya empezaremos a hablar de cómo planificar la primera edición de una revista y echaremos un vistazo general a Scribus, el que será nuestro compañero en la maquetación y edición.

Cualquier duda u opinión la pueden dejar en los comentarios y también pueden suscribirse para que lleguen las notificaciones de las nuevas publicaciones a sus correos.

Creando una revista con Software Libre – Parte 0: Introducción

Con unos amigos de la unviersidad decidimos empezar a trabajar en una revista cuyo tema principal sea difundir la tecnología y la cultura libre. Para empezar el trabajo, he estado aprendiendo ciertas cosas sobre el campo de la “autoedición” (desktop publishing). La autoedición es el área de trabajo en la que uno, a través de diversas herramientas, puede maquetar y diseñar documentos gráficos para hacer revistas, afiches, panfletos, flyers y publicaciones similares.

Se me ocurrió que lo que he ido aprendiendo sobre la creación de revistas no puede quedar sólo ahí. Ese conocimiento debe servir a mucha más gente interesada en crear sus propias revistas, por lo que empezaré una serie de tutoriales en los que iré explicando distintas fases y herramientas que pueden ser utilizadas para crear sus revistas. Lo importante aquí, es que todas las herramientas utilizadas en la serie de tutoriales van a ser libres, es decir, podrán distribuir sus creaciones sin limitaciones y sin estar atados a asquerosas licencias privatizadoras.

Es importante mencionar que a través del uso de herramientas libres, los medios que crearemos serán libres. Esto implica que nosotros seremos absolutos dueños de los contenidos que distribuyamos, lo que nos da un tremendo poder libertario. Si los medios de comunicación de masas nos ocultan información o no nos dan información de calidad, nosotros mismos debemos tomar la información en nuestras manos y comunicarla.

Libre Graphics Magazine
Libre Graphics Magazine
(Click en la imagen para ver la original en Flickr)

Sin agregar mucho más, ahora hay que mencionar los temas generales que se tratarán en esta serie de tutoriales:

  • Maquetación de documentos con Scribus: Una de las áreas principales en las que trabajaremos será la maquetación de documentos. Por maquetar nos referimos a definir una estructura física sobre la que pondremos el contenido. Esto incluye definir espacios donde irán los títulos, los textos, imágenes de fondo, distribución de columnas y todo lo que tenga que ver con forma.
  • Diseño de algunos elementos gráficos: Este tutorial no será principalmente sobre diseño gráfico, sino que se enfocará en la creación de una revista. Sin embargo, también se tratarán algunos temas sobre creación de gráficos para lo que se tocarán algunos programas como Inkscape o quizás GIMP (aunque en este último no tengo mucha experiencia). Quizás sean apartados dentro de esta misma serie de tutoriales o tengan sus series de tutoriales propias. Todo dependerá de mi tiempo.
  • Obtención de recursos: Para una publicación necesitaremos tener un respaldo de elementos gráficos que nos sirvan para darle un mejor acabado a nuestras creaciones. Para esto, mostraremos distintas fuentes de las que obtener recursos como fuentes e imágenes que sean libres de uso.
  • Otros: Hay muchos temas que no son de carácter técnico, que requieren bastante análisis en las fases de planificación de una revista como pueden ser el diseño de storyboards o establecer un equipo de trabajo con el que podamos trabajar en conjunto nuestra revista (Sí, necesitarán de un equipo de trabajo, así que empiezen a armar un grupo pequeño de gente con la que tengan un buen ambiente de trabajo para que su proyecto sea exitoso.

En general, estos temas se tocarán en los tutoriales y quizás otros más se sumarán en el camino. Espero ir los sacando de forma periódica y que a alguien le sean de utilidad. Si es así, estoy dispuesto a recibir sus comentarios y preguntas.

Pronto vendrá el primer tutorial en el que hablaremos de la planificación de una revista.

PD: Esperamos terminar nuestra primera edición de la revista para el día 10 de octubre, día en el que realizaremos el encuentro Regional de Software Libre en la Universidad Católica del Maule, en Talca. Si todo sale bien, ese mismo día estrenaremos la revista y posiblemente hagamos una charla sobre los temas de estos tutoriales. Aprovecho de dejar la invitación a cualquier persona que lea esta publicación a informarse en la página en Facebook de NULL (nuestro grupo de difusión de Software Libre) y a visitar nuestro sitio web donde pronto tendremos más información sobre el evento.