Difference between revisions of "Isidoro-TFM-Rovio02"

From robotica.unileon.es
Jump to: navigation, search
m
m
 
(25 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Imagen:rovio_robot.jpg|thumb|right|200px|Rovio, la cámara web móvil.]]
+
* '''Project Name:''' Rovio en tareas de vigilancia
 +
* '''Author:''' Isidoro Gayo Vélez
 +
* '''Academic Year:''' 2011-2012
 +
* '''Degree:''' Graduate
 +
* '''Tags:''' rovio, visual control
 +
* '''Technologies:''' c, openCV, qt
 +
* '''Status:''' Discontinued
 +
* '''Source License:''' [http://www.gnu.org/licenses/gpl-3.0-standalone.html GPLv3]
  
El trabajo que aquí se va a presentar trata sobre la aplicación del robot ROVIO de la empresa  [http://www.wowwee.com/en/support/rovio Wowwee Technologies] a tareas de vigilancia. Para ello se utilizará la biblioteca de visión artificial [http://es.wikipedia.org/wiki/OpenCV ''OpenCV''], que será la encargada de procesar la información proviniente de la cámara wifi de ROVIO en un ordenador portátil. Como sistema operativo de base para el desarrollo de esta aplicación se utilizará [http://es.wikipedia.org/wiki/GNU/Linux GNU/Linux], en concreto la distribución [http://www.ubuntu.com/ Ubuntu Lucid]. La aplicación será desarrollada enteramente en [http://es.wikipedia.org/wiki/C_%28lenguaje_de_programaci%C3%B3n%29 lenguaje C].
 
== Introducción ==
 
  
Pendiente de redacción...
+
=Resumen=
  
== OpenCV ==
+
El trabajo que aquí se va a presentar trata sobre la aplicación del robot ROVIO de la empresa  [http://www.wowwee.com/en/support/rovio Wowwee Technologies] a tareas de vigilancia. Para ello se utilizará la biblioteca de visión artificial [http://es.wikipedia.org/wiki/OpenCV ''OpenCV''], que será la encargada de procesar la información proviniente de la cámara wifi de ROVIO en un ordenador portátil. Como sistema operativo de base para el desarrollo de esta aplicación se utilizará [http://es.wikipedia.org/wiki/GNU/Linux GNU/Linux], en concreto la distribución [http://www.ubuntu.com/ Ubuntu Lucid]. La aplicación será desarrollada enteramente en [http://es.wikipedia.org/wiki/C_%28lenguaje_de_programaci%C3%B3n%29 lenguaje C].
  
Si se desea profundizar en OpenCV hay varias fuentes de donde obtener información. Una de ellas es el libro [http://www.amazon.com/Learning-OpenCV-Computer-Vision-Library/dp/0596516134/ref=sr_1_1?ie=UTF8&qid=1299591736&sr=8-1 ''Learning OpenCV: Computer Vision with the OpenCV Library''] escrito por Gary Bradski y Adrian Kaehler y publicado por la editorial O'Really. En la página web de OpenCV, [http://opencv.willowgarage.com/wiki/ WillowGarage], también existe abundante información para quienes no dispongan del libro en cuestión. Y en la Red hay numerosa información relacionada con el uso de la biblioteca, que si bien puede estar más o menos desperdigada, también supone una buena fuente de consulta. Por último, los ejemplos que se incluyen en la biblioteca también son una fuente de informacación utilísima. En el desarrollo de este trabajo se han utilizado todas estas fuentes en mayor o menor medida.
+
=Introducción=
  
=== Instalación de la biblioteca ===
+
Pendiente de redacción...
 
 
Para realizar la instalación de cualquier aplicación o biblioteca en GNU/Linux lo más cómodo suele ser utilizar los paquetes correspondientes. Los usuarios de Ubuntu pueden usar '''Synaptic''' (''Sistema/Administración/Gestor de paquetes Synaptic'') que es la interfaz gráfica por defecto en estos momentos para gestionar los paquetes, aunque también se puede instalar desde la línea de comandos de un terminal:
 
 
 
<geshi lang=Bash lines=0>~$ sudo aptitude install libcv4 libhighgui4 libcvaux4 libcv-dev libhighgui-dev libcvaux-dev opencv-doc</geshi>
 
 
 
Los tres primeros paquetes corresponden a la biblioteca propiamente dicha, por tanto son imprescindibles. Los tres siguientes con el sufijo ''-dev'' son los ficheros de desarrollo que también los necesitaremos si queremos desarrollar aplicaciones con esta biblioteca. El último paquete contiene programas de ejemplo y documentación sobre OpenCV y aunque no es imprescindible para trabajar, sí que es recomendable como fuente de consulta. Si se quiere contar con la última versión de las bibliotecas se puede realizar una instalación manual de las mismas a partir de sus fuentes siguiendo el proceso descrito para tal fin en [http://opencv.willowgarage.com/wiki/InstallGuide WillowGarage].
 
 
 
=== Operaciones básicas ===
 
 
 
Una vez que tenemos preparado el sistema con todo el software necesario, es el momento de la primera toma de contacto con OpenCV. Este apartado no pretende ser un tutorial completo y exhaustivo sobre cada una de las funciones que posee OpenCV si no más bien un sendero abierto que conduce directamente hacia el objetivo principal de este trabajo. Y lo mejor es comenzar poco a poco de manera que nuestra primera incursión sea lo menos traumática posible. Por tanto, los primeros programas que vamos a desarrollar consistirán en una serie de operaciones básicas que conviene aprender bien al principio pues serán imprescindibles para el desarrollo de la aplicación.
 
 
 
Las primeras operaciones que se realizarán serán mostrar una imagen y un vídeo que están guardados en el disco duro e interceptar el flujo de vídeo de una cámara para mostrarlo en pantalla.
 
 
 
==== Mostrar una imagen en pantalla ====
 
 
 
El siguiente código nos muestra en pantalla la imagen que recibe como argumento en la línea de comandos.
 
 
 
<geshi lang=C lines=0>#include "highgui.h"
 
   
 
int main(int argc, char* argv[])
 
{
 
 
 
IplImage *img = cvLoadImage(argv[1]); // Carga la imagen en memoria.
 
 
 
cvNamedWindow("imagen", CV_WINDOW_AUTOSIZE); // Crea la ventana donde mostrar la imagen
 
cvShowImage("imagen", img); // Muestra la imagen en la ventana.
 
 
 
cvWaitKey(0); // Espera que se pulse una tecla.
 
 
 
cvReleaseImage( &img ); // Retira la imagen de la ventana.
 
cvDestroyWindow("imagen"); // Libera la memoria.
 
 
 
return 0;
 
}</geshi>
 
 
 
Si guardamos este código con el nombre ''ejemplo_01.c'' y lo compilamos con g++ de la forma:
 
 
 
<geshi lang=Bash lines=0>~$ g++ -o ejemplo_01 ejemplo_01.c -Wall -I /usr/include/opencv -L /usr/lib -lm -lhighgui</geshi>
 
 
 
deberíamos poder obtener un fichero que ejecutaríamos mediante la orden
 
 
 
<geshi lang=Bash lines=0>~$./ejemplo_01 mi-imagen.xxx</geshi>
 
 
 
siendo ''mi-imagen.xxx'' el nombre del fichero con la imagen que queremos mostrar. OpenCV soporta formatos de imagen del tipo JPEG, BMP, PNG, PGM y TIFF entre otros. Si todo ha ido bien, se debería ver la ventana creada con la imagen en su interior. Si pulsamos una tecla, la ventana se cierra y finaliza el programa.
 
 
 
El código del ejemplo está bien comentado y apenas necesita explicación. Cada vez que se necesite mostrar una imagen en pantalla con OpenCV, el procedimiento será siempre el mismo, tal y como se ve en el ejemplo:
 
 
 
*Se creará una variable de tipo ''IplImage'' que guardará la imagen en forma de matriz de puntos y se indicará a la biblioteca que la cargue en memoria mediante la orden ''cvLoadImage''.
 
*Se creará la ventana donde se va a visualizar la imagen. Esto siempre es necesario, siempre hará falta un soporte sobre el que colocar las imágenes. En todo lenguaje de programación que incluya interfaces gráficas es necesario un contenedor donde se ponen o colocan los demás componentes de la interfaz y OpenCV no es una excepción.
 
*Se pedirá a la librería que muestre la imagen dentro de la ventana correspondiente mediante la orden ''cvShowImage''.
 
 
 
Una vez que ya no se necesite la imagen, será necesario liberar el espacio de memoria que ocupó la imagen para evitar saturarla. Esta operación se realiza mediante las órdenes ''cvReleaseImage'' que elimina la imagen de la ventana que la soporta y ''cvDestroyWindow'' que elimina de la memoria del equipo la ventana que contenía a la imagen mostrada.  
 
 
 
En el ejemplo anterior se podría comprobar que se ha dado el nombre/ruta de una imagen como argumento en la línea de comandos antes de cargar la imagen para no producir un error en tiempo de ejecución. También se podría comprobar que la operación de carga de la imagen ha tenido éxito --o no-- mediante la adición de código extra, pero se ha preferido evitar esto para mantener el ejemplo lo más simple y claro posible para su mejor entendimiento.
 
 
 
==== Abrir un archivo de vídeo ====
 
  
En el siguiente ejemplo se tratará de abrir un archivo de vídeo almacenado en el disco duro del ordenador para que pueda ser visualizado en pantalla.
+
=OpenCV=
  
<geshi lang=C lines=0>#include "highgui.h"
+
Si se desea profundizar en OpenCV hay varias fuentes de donde obtener información. Una de ellas es el libro [http://www.amazon.com/Learning-OpenCV-Computer-Vision-Library/dp/0596516134/ref=sr_1_1?ie=UTF8&qid=1299591736&sr=8-1 ''Learning OpenCV: Computer Vision with the OpenCV Library''] escrito por Gary Bradski y Adrian Kaehler y publicado por la editorial O'Really. En la página web de OpenCV, [http://opencv.willowgarage.com/wiki/ WillowGarage], también existe abundante información para quienes no dispongan del libro en cuestión. Y en la Red hay numerosa información relacionada con el uso de la biblioteca, que si bien puede estar más o menos desperdigada, también supone una buena fuente de consulta. Por último, los ejemplos que se incluyen en la biblioteca también son una fuente de informacación utilísima. En el desarrollo de este trabajo se han utilizado todas estas fuentes en mayor o menor medida (seguir seguir leyendo [[OpenCV]]).
   
 
int main(int argc, char* argv[])
 
{
 
cvNamedWindow("ventana_video", CV_WINDOW_AUTOSIZE);
 
CvCapture* flujo_video = cvCreateFileCapture(argv[1]);
 
IplImage *fotograma;
 
  
while(1){
+
=Segmentación de la imagen=
fotograma = cvQueryFrame(flujo_video);
 
if (!fotograma) break;
 
cvShowImage("ventana_video", fotograma);
 
  
char c = cvWaitKey(33);
+
El procesamiento de imagen digital suele comprender varios pasos intermedios antes de tener una imagen útil con la que el robot pueda realizar algún tipo de acción. Este procesamiento es necesario porque la cantidad de información proviniente de una imagen tomada desde una cámara es enorme y significaría una carga en la cpu de nuestro robot innecesaria (seguir leyendo [[Segmentación de la imagen]]).
if(c == 27) break;
 
}
 
  
cvReleaseCapture( &flujo_video );
+
=Seguimiento de objetos=
cvReleaseImage( &fotograma );
 
cvDestroyWindow("ventana_video");
 
  
return 0;
+
(Trabajo en curso) -> Pendiente de redacción
}</geshi>
 
  
En este ejemplo las cosas se hacen de forma ligeramente diferente a como se hacen en el anterior, por lo que merece la pena comentar estas diferencias. Una vez se ha creado la ventana contenedor para el vídeo con la orden ''cvNamedWindow'', se crea una estructura que almacenará el fichero mediante la orden ''cvCreateFileCapture''. Puesto que el vídeo es una sucesión de imágenes mostradas de forma contínua, necesitaremos acceder a cada una de esas imágenes para poder ir mostrándolas en pantalla. Para esto necesitamos crear otra variable, que llamaremos ''fotograma'', que será la encargada de ir tomando de una en una las imágenes contenidas en el fichero de vídeo.
+
=Anexos=
  
En el bucle infinito creado con la orden ''while(1)'' se realizan las siguientes operaciones:
+
==Anexo A: Configuración de ROVIO==
  
* Se toma un fotograma del fichero de vídeo mediante la orden ''cvQueryFrame'', que permite un acceso secuencial a cualquier flujo de vídeo tanto si está almacenado en un fichero como si proviene de una cámara.
+
El proceso de configuración de Rovio viene perfectamente explicado en el manual que se adjunta con el robot, por lo que se remite al lector a dicho manual o a su versión electrónica, descargable desde la web del [http://www.wowwee.com/static/support/rovio/manuals/Rovio_Manual_(for_v5.00_firmware).pdf fabricante] o desde [http://www.robocommunity.com/download/file/14279-77 Robocomunity]. No obstante, conviene remarcar un par de cosas a tener en cuenta para dicha configuración.  
  
* Se comprueba si se ha podido tomar el fotograma, en caso contrario se abandona el bucle --se ha producido un error debido, por ejemplo, a un fichero corrupto--.
+
Rovio se puede configurar tanto mediante el [http://www.wowwee.com/static/support/rovio/software/RovioSoftwareInstaller_v2.3.zip programa] suministrado por el fabricante como de forma manual. El programa sólo funciona en entornos Windows, por lo que habrá que tener disponible un ordenador con este sistema operativo si nos decidimos por la instalación automática. La instalación manual mediante protocolo ad-hoc puede resultar más compleja para quien no esté familiarizado con estas cosas, pero tampoco entraña dificultad alguna.
  
* Se muestra el fotograma en la ventana creada al principio del programa de forma similar a como se hizo en el ejemplo anterior.
+
El segundo punto a comentar es que no se debe olvidar dejar desmarcada la opción ''Enable User Authentication'' de la solapa ''Security'' en el menú de configuración de Rovio, tal y como se muestra en la imagen adjunta, ya que de lo contrario, jamás recibiremos imagen alguna enviada desde Rovio a nuestro software de envoltorio. Esta es una información adquirida experimentalmente de forma dolorosa, ya que hizo perder varios días de desarrollo hasta que se cayó en la cuenta.
  
* Se esperan 33 milisegundos para comprobar la pulsación de una tecla y si esta tecla corresponde a la de ''ESC'' se abandona el bucle. En caso contrario se vuelve al comienzo del bucle.
+
==Anexo B: Cambios en el software "RovioCWrapProject"==
  
* El programa finaliza liberando la memoria ocupada por las variables creadas al principio para la gestión del fichero de vídeo.
+
Lo que figura a continuación quiere ser una especie de ''changelog'' con los cambios más importantes que se han realizado en el código inicial que figura en el repositorio de Rovio a fin de poder tener una perspectiva de los pasos llevados a cabo durante el desarrollo de este trabajo.
  
Dos cosas hacen diferente a este ejemplo del anterior: el bucle que permite ir tomando una tras otra las imágenes almacenadas en el fichero de vídeo y que es necesario para mostrar todas las imágenes del vídeo, y el valor de 33 milisegundos que recibe la orden ''cvWaitKey''. Si este valor es cero, el programa esperará a que se pulse una tecla para mostrar el siguiente fotograma. Valores diferentes de cero hacen que el sistema espere esa cantidad de tiempo especificada antes de continuar con la siguiente instrucción.
+
'''12/12/2011'''
  
Un apunte más sobre el manejo de ficheros de vídeo con OpenCV. Si bien el formato de vídeo por defecto que maneja OpenCV es el AVI, también se pueden abrir ficheros de otro tipo siempre que el sistema tenga instalados los codec adecuados. En caso contrario, el programa lanzará un error relacionado con el tipo de fichero que intenta abrir.
+
* Modificada la función ''rovioAccion'' del fichero ''rcwp.c'' para incluir posibilidad de consultar a Rovio la resolución de la cámara y los fotogramas por segundo (fps) a los que nos va a enviar las imágenes. Inicialmente estos valores se incluían en el código directamente. Ahora no es necesario cambiarlos a mano, el programa los actualiza automáticamente.
  
==== Capturar vídeo de una cámara ====
+
* Modificada la función ''tracking'' del fichero ''rcwp.c'' para incluir movimientos de avance y retroceso para interaccionar respecto de la pelota.
  
En el ejemplo que sigue se muestra en pantalla el flujo de vídeo proviniente de una cámara de vídeo o de un fichero dependiendo de si en la línea de comandos del terminal se llama al programa con un argumento o no --el argumento será el nombre del fichero de vídeo a mostrar--.
+
* Eliminada la función ''centroide_pelota'' en el fichero ''rcwp.c''. Ahora el centroide de la pelota se calcula en el segmentador.
  
<geshi lang=C lines=0>#include "highgui.h"
+
* Eliminados los deslizadores de selección de color en ventana principal (''imagen_cruda'') y sustituidos por selección de color mediante clic izquierdo de ratón.
   
 
int main(int argc, char* argv[])
 
{
 
CvCapture* flujo_video;
 
cvNamedWindow("ventana_video", CV_WINDOW_AUTOSIZE);
 
IplImage *fotograma;
 
  
if(argc == 1){
+
* Se puede ajustar el margen de tolerancia del color (valores máximo y mínimo como antes) mediante deslizador en torno al color seleccionado con el ratón. Esta tolerancia se mide en píxeles y está comprendida entre 0 y 100. El margen de variación del color estará comprendido entre ''color seleccionado - tolerancia'' y ''color seleccionado + tolerancia''.
flujo_video = cvCreateCameraCapture(0);
 
}
 
else {
 
flujo_video = cvCreateFileCapture(argv[1]);
 
}
 
assert(flujo_video != NULL);
 
  
while(1){
+
* Separado segmentador de la función ''main'' a un fichero independiente (''segmentador.c'' y su cabecera correspondiente).
fotograma = cvQueryFrame(flujo_video);
 
if (!fotograma) break;
 
cvShowImage("ventana_video", fotograma);
 
  
char c = cvWaitKey(33);
+
* La ventana ''HSV'' se muestra/esconde pulsando la tecla '''t''' (por defecto está escondida).
if(c == 27) break;
 
}
 
  
cvReleaseCapture( &flujo_video );
+
* Mejorado el filtrado del segmentador (fichero ''segmentador.c''). Ahora funciona mejor incluso en condiciones de luz malas.
cvReleaseImage( &fotograma );
 
cvDestroyWindow("ventana_video");
 
  
return 0;
+
'''08/11/2011'''
}</geshi>
 
  
Como comentarios al ejemplo se mencionará que el código es similar al anterior excepto por la condición en la que se toma como fuente del flujo o bien la salida de la cámara conectada al sistema o bien el fichero de argumento. Esto es muy bueno para el programador porque le da libertad absoluta para decidir cuál será la entrada de vídeo que quiere usar sin complicar el resto del código, ya que es la propia biblioteca la que se encarga de todo el ''trabajo sucio'' relacionado con la gestión del fichero o la cámara.
+
* Incluido segmentador de imagen en función ''main''.
  
El valor cero que recibe la orden o función ''cvCrateCameraCapture'' como argumento corresponde a la fuente u origen del flujo de vídeo. Normalmente cuando sólo se tiene una cámara en el sistema es suficiente con dar el valor cero que corresponde a la constante CV_CAP_ANY de los ficheros de cabecera de OpenCV. De esta manera se consigue que el programa realice una autodetección del driver necesario para la captura. Otros valores interesantes podrían ser CV_CAP_V4L y CAP_VAL_V4L2 que corresponden al controlador de vídeo compatible con sistemas ''Video for Linux'' --vídeo nativo de GNU/Linux-- y que equivalen ambas al valor numérico 200, o el valor CV_CAP_FIREWIRE --valor numérico 300-- que correspondería a una cámara de tipo firewire. En el fichero de cabecera ''highgui.h'' se relacionan todos los valores de drivers soportados. Según el libro ''Learning OpenCV'' si queremos que el programa nos pregunte qué tipo de cámara vamos a utilizar bastará con poner un -1 como argumento de la función de captura, aunque esto sólo estaría disponible para usuarios de Windows ya que no se ha conseguido durante las pruebas del código en Ubuntu.
+
* Incluidas ventanas de visualización de ''imagen_cruda'', ''imagen_segmentada'' y ''HSV'' en la interfaz gráfica del programa (fichero ''main.c'').
  
Como comentario final sólo se añadirá que la línea ''assert(flujo_video != NULL)'' es una simple comprobación de que se ha podido o abrir el fichero o enlazar con la cámara. Es una comprobación útil y muy recomendable pues se podría dar el caso de que un determinado driver de cámara o códec de vídeo no estuviesen instalados en el sistema lo que produciría un error en la ejecución de nuestro programa.
+
* Incluidos deslizadores para seleccionar el color a segmentar (valores máximo y mínimo) en ventana ''imagen_cruda''.
  
==== Guardar vídeo en un fichero ====
+
==Anexo C: ROVIO por dentro==
 
 
Si lo que se necesita es guardar el flujo de vídeo procedente de una cámara en el disco duro, lo mejor es utilizar un código similar al que se muestra a continuación.
 
 
 
<geshi lang=C lines=0># include "cv.h"
 
# include "highgui.h"
 
# include "stdio.h"
 
 
 
int main(int argc, char* argv[]) {
 
    CvCapture* flujo_video_entrada = cvCreateCameraCapture(0);
 
 
 
    if (!flujo_video_entrada) {
 
        printf("No puedo acceder a la cámara");
 
        return 0;
 
    }
 
    IplImage* fotograma = cvQueryFrame(flujo_video_entrada);
 
 
 
    CvSize imgSize = cvSize(
 
    (int)cvGetCaptureProperty(flujo_video_entrada, CV_CAP_PROP_FRAME_WIDTH),
 
    (int)cvGetCaptureProperty(flujo_video_entrada, CV_CAP_PROP_FRAME_HEIGHT)
 
    ); // Dimensiones del fotograma
 
 
 
    CvVideoWriter *flujo_video_salida = cvCreateVideoWriter(
 
            "flujo_salida.avi", CV_FOURCC('M','J','P','G'), 24, imgSize);
 
 
 
    for (;;) {
 
        //Get a frame from the input video.
 
        fotograma = cvQueryFrame(flujo_video_entrada);
 
        cvWriteFrame(flujo_video_salida, fotograma);
 
    }
 
    cvReleaseVideoWriter(&flujo_video_salida);
 
    cvReleaseCapture(&flujo_video_entrada);
 
   
 
    return 0;
 
}</geshi>
 
 
 
Las partes más importantes de este código son la correspondiente a la función ''cvCreateVideoWriter'' y el interior del bucle ''for''. La función ''cvCreateVideoWriter'' nos permite escribir en el fichero especificado --en este caso ''flujo_salida.avi''-- con la codificación o códec MJPG todas las imágenes recogidas de la cámara detectada en el sistema a una tasa de 24 fotogramas por segundo. Las dimensiones de cada fotograma vienen definidas por el valor de ''imgSize'', que se obtiene mediante los valores ''CV_CAP_PROP_FRAME_WIDTH'' y ''CV_CAP_PROP_FRAME_HEIGHT'' --en el fichero de cabecera highgui.h se pueden ver el resto de las propiedades disponibles del fotograma--.
 
 
 
Dentro del bucle ''for'' se produce la escritura propiamente dicha del fichero de vídeo mediante la función ''cvWriteFrame''. Esta función escribe en el fichero que se le da como primer argumento la imagen o fotograma a la que se acaba de acceder desde la cámara del sistema. La actuación combinada de ambas funciones, ''cvCreateVideoWriter'' y ''cvWriteFrame'', permiten en primer lugar, reservar espacio en la memoria del sistema para almacenar el vídeo y en segundo lugar, volcarlo al disco duro.
 
 
 
Si en lugar de utilizar la cámara del sistema como fuente del flujo de vídeo se utilizase otro fichero de vídeo, modificando el código de manera adecuada se podría conseguir un convertidor de formatos de vídeo. Como argumentos de la línea de comandos se podrían utilizar el nombre del fichero fuente y el del fichero destino incluida su extensión. Convendría recordar que el cambio de formato de vídeo depende de los códec que haya instalados en el sistema. Pretender convertir o guardar un fichero en un formato que no tiene los códecs instalados producirá un error de ejecución. Si en el valor del códec se utiliza un -1 --CV_FOURCC(-1)-- el sistema pedirá que se seleccione uno de entre varios mostrados, pero sólo es sistemas Windows.
 
 
 
Se deja también propuesta la posibilidad de visualizar el vídeo de la cámara mientras se realiza la captura ya que en este ejemplo, como se habrá podido comprobar, se ha omitido esta posibilidad intencionadamente.
 
 
 
=== Eventos de teclado y ratón ===
 
 
 
Las interacciones del usuario con la aplicación que se jecuta son algo imprescindible en un programa. En este subapartado se mostrará la manera de realizar cambios en el comportamiento del programa a través de la pulsación de teclas o de los botones del ratón. También se verá cómo mostrar información relacionada con el programa sobreimpresa en las ventanas de vídeo. El ejemplo que se muestra a continuación realiza todas las acciones mencionadas.
 
 
 
<geshi lang=C lines=0>#include "cv.h"
 
#include "highgui.h"
 
#include "stdio.h"
 
#include "string.h"
 
 
 
CvFont fuente;
 
CvCapture* flujo_video;
 
IplImage* fotograma;
 
char ventana[] = "prueba";
 
int coord_x, coord_y;
 
char boton[100] = "Pulsado Boton ---", tmp[256];
 
 
 
void mouseHandler(int event, int x, int y, int flags, void *param)
 
{
 
switch(event) {
 
/* boton izquierdo pulsado */
 
case CV_EVENT_LBUTTONDOWN:
 
strcpy(boton, "Pulsado Boton Izquierdo");
 
break;
 
/* boton derecho pulsado */
 
case CV_EVENT_RBUTTONDOWN:
 
strcpy(boton, "Pulsado Boton Derecho");
 
break;
 
/* movimiento del puntero del raton */
 
case CV_EVENT_MOUSEMOVE:
 
coord_x = x;
 
coord_y = y;
 
break;
 
}
 
}
 
 
 
int main()
 
{
 
char pos_x[256], pos_y[256];
 
flujo_video = cvCaptureFromCAM(CV_CAP_ANY); 
 
int color_b = 255, offset = 15, tecla;
 
 
 
if( !flujo_video )
 
{
 
fprintf(stderr, "ERROR: fallo en la captura\n");
 
getchar();
 
        return -1;
 
        }
 
cvNamedWindow(ventana, CV_WINDOW_AUTOSIZE);
 
cvInitFont(&fuente, CV_FONT_HERSHEY_SIMPLEX, 1.0, 1.0, 0, 1, CV_AA);
 
 
 
printf( "Teclas rapidas: \n"
 
        "\tESC - sale del programa\n"
 
        "\tc - cambia color de cursor de azul a negro\n"
 
        "\tt - cambia tamaño de cursor de 15 a 30\n");
 
 
 
while(1)
 
{
 
strcpy(pos_x, "X = ");
 
strcpy(pos_y, "Y = ");
 
fotograma = cvQueryFrame(flujo_video);
 
    if(!fotograma)
 
    {
 
    fprintf(stderr, "ERROR: no hay fotogramas...\n");
 
    getchar();
 
    break;
 
    }
 
cvSetMouseCallback( ventana, mouseHandler, NULL );
 
cvPutText(fotograma, boton, cvPoint(10, 80), &fuente, cvScalar(0, 255, 0, 0));
 
 
 
sprintf(tmp, "%d", coord_x);
 
cvPutText(fotograma, strcat(pos_x, tmp), cvPoint(10, 400), &fuente, cvScalar(0, 0, 255, 0));
 
 
 
sprintf(tmp, "%d", coord_y);
 
cvPutText(fotograma, strcat(pos_y, tmp), cvPoint(10, 440), &fuente, cvScalar(0, 0, 255, 0));
 
 
 
cvRectangle(fotograma, cvPoint(coord_x - offset, coord_y - offset),
 
cvPoint(coord_x + offset, coord_y + offset), cvScalar(color_b, 0, 0, 0), 2, 5, 0);
 
 
 
tecla = cvWaitKey(10);
 
if(tecla == 27)
 
break;
 
switch( tecla )
 
        {
 
        case 't':
 
if (offset == 15)
 
offset = 30;
 
else
 
offset = 15;
 
            break;
 
        case 'c':
 
if (color_b == 255)
 
color_b = 0;
 
else
 
color_b = 255;
 
            break;
 
        default:
 
            break;
 
}
 
cvShowImage(ventana, fotograma);
 
}
 
cvReleaseCapture(&flujo_video);
 
cvDestroyWindow(ventana);
 
return 0;
 
}</geshi>
 
 
 
Las partes más interesantes de este ejemplo corresponden al código de la función ''mouseHandler'' y al interior del bucle ''while''. Este ejemplo muestra en pantalla la captura de flujo de vídeo realizada a través de la cámara por defecto del sistema y sobreimpresiona las coordenadas en las que se encuentra el cursor del ratón en la esquina inferior izquierda. En la esquina superior izquierda se imprimirá un mensaje indicando el botón que se ha pulsado del ratón y el puntero del mismo se mostrará como un cuadrado de color azul que cambiará de tamaño si se pulsa la tecla ''t'' o de color si se pulsa la tecla ''c''. La pulsación de ''ESC'' nos permitirá abandonar el programa.
 
 
 
En la función ''mouseHandler'', invocada mediante la retrollamada ''cvSetMouseCallback'', se realiza la comprobación de la pulsación de los botones de ratón a través de la variable ''event'' que recibe como argumento. El fichero de cabecera ''highgui.h'' contiene la lista de todos los eventos de ratón que pueden ser capturados mediante la retrollamada, por lo que no se describirán en este trabajo.
 
 
 
Otra de las partes de que consta la función de retrollamada es la correspondiente a los valores de las coordenadas de la ventana activa en las que tiene lugar el evento. Estos valores se recogen y devuelven en las variables argumento ''x'' e ''y'' de la función. Con ellos se puede actualizar la posición del puntero del ratón en todo momento mientras se desplaza por la ventana. El argumento correspondiente al valor ''flags'' permite gestionar la pulsación simultánea de otras teclas del teclado como pueden ser SHIFT, CONTROL, etc..., y el valor ''param'' sirve para pasar argumentos extra a la función que no estén comprendidos en los parámetros anteriores. En este trabajo no se necesitará por lo que no se explicará su uso.
 
 
 
Dentro del bucle ''while'' tienen lugar una serie de operaciones que merece la pena comentar. La primera está relaccionada con el texto que se sobreimpresiona en la pantalla activa. La función ''cvPutText'' se encarga de ello. Esta función recibe como argumentos la imagen en la que se va a imprimir el texto, que puede ser estática como la de un fichero JPG o puede cambiar de forma contínua como ocurre con las imágenes que provienen del flujo de vídeo en el ejemplo. El segundo argumento corresponde a una cadena de caracteres que es el texto que se desea mostrar en pantalla. El texto a mostrar se puede manipular previamente mediante el uso de las funciones de cadena contenidas en la biblioteca de funciones estandar de C, como es nuestro caso. El tercer argumento que recibe la función es la posición en la que va a ser mostrado el texto. El punto tiene que ser del tipo ''cvPoint'' --no vale simplemente con introducir las coordenadas--. El origen de coordenadas para los puntos es el (0, 0) que corresponde a la esquina superior izquierda de la ventana activa.
 
 
 
El tipo de fuente que se va a usar en el texto se especifica con el valor ''fuente'', que tiene que ser del tipo ''CvFont''. Este argumento necesita ser inicializado primero antes de ser usado y la forma de realizar esto es mediante la función ''cvInitFont''. Esta función utiliza como primer argumento el tipo de fuente. El listado de todos los tipos de fuente figura en el fichero de cabecera ''highgui.h''. Los argumentos 2 y 3 corresponden a la escala horizontal y vertical de la fuente. Estos argumentos sólo pueden tomar dos valores, ''1.0'' ó ''0.5''. El cuerto argumento permite escribir el texto en itálica para valores mayores de cero y menores de uno --el máximo valor 1 correspondería a una inclinación del texto de 45 grados--. Los dos últimos argumentos ofrecen la posibilidad de especificar el grosor del texto --negrita-- y el tipo de la línea. La cabecera ''cxcore.h'' contiene más información relacionada con las funciones de texto. El color de la fuente se especifica en el último parámetro de la función ''cvPutText'', concretamente con los tres primeros valores que siguen a ''cvScalar'', que corresponden a los valores BGR del color.
 
 
 
Para dibujar el puntero del ratón se utiliza la función ''cvRectangle''. Esta función recibe como argumentos la imagen sobre la que se va a dibujar el cuadrado, las posiciones de las esquinas superior izquierda e inferior derecha del cuadrado --que gracias al uso de la variable entera ''offset'' podemos cambiar su tamaño desde el teclado--, el color de los lados del cuadrado --especificados por los valores que encierra ''cvScalar'' entre paréntesis-- y el grosor de los lados del cuadrado. Si este valor es cero o negativo, el cuadrado (o rectángulo) dibujado aparecerá relleno. El valor 5 del ejemplo, que figura a continuación del 2 que define el grosor de las líneas, corresponde al tipo de línea a utilizar en el rectángulo. En el fichero de cabecera ''cxcore.h'' se puede encontrar más información relacionada con esta función.
 
 
 
Para finalizar este apartado se mencionará que la gestión de las pulsaciones de teclas se realiza mediante la función de OpenCV ''cvWaitKey'' ya vista anteriormente, por lo que no se realizará ningún comentario adicional de la misma. El código dentro de la sentencia condicional ''swtich'' es muy claro y no debería necesitar explicación alguna.
 
 
 
== Segmentación de imagen ==
 
 
 
El procesamiento de imagen digital suele comprender varios pasos intermedios antes de tener una imagen útil con la que el robot pueda realizar algún tipo de acción. Este procesamiento es necesario porque la cantidad de información proviniente de una imagen tomada desde una cámara es enorme y significaría una carga en la cpu de nuestro robot innecesaria ([[ seguir leyendo]]).
 
 
 
== Anexos ==
 
=== Anexo A: Configuración de ROVIO ===
 
 
 
Pendiente de redacción
 
 
 
=== Anexo B: Comandos de control de ROVIO(?) ===
 
 
 
Pendiente de redacción
 
 
 
=== Anexo C: ROVIO por dentro ===
 
  
 
Los datos que se muestran a continuación no están basados en ninguna especificación oficial porque no hay ninguna publicada, si no que están recogidos y ordenados de varios sitios web dedicados a ROVIO o a temas relacionados con él, principalmente de [http://www.deviceguru.com/mobile-robot-packs-wireless-web-cam/ Device Guru] y [http://www.robocommunity.com/article/15281/WowWee-Rovio-Resources-Reviews-Hacks-Conversations-and-More/ RoboComunity]. Se añaden al cuerpo del trabajo como anexo porque se considera que podrían ser interesantes de cara a una potencial y futura modificación o hackeo de ROVIO, de manera que su manejo y configuración se pueda realizar de forma completamente independiente de la plataforma software que se utilice.
 
Los datos que se muestran a continuación no están basados en ninguna especificación oficial porque no hay ninguna publicada, si no que están recogidos y ordenados de varios sitios web dedicados a ROVIO o a temas relacionados con él, principalmente de [http://www.deviceguru.com/mobile-robot-packs-wireless-web-cam/ Device Guru] y [http://www.robocommunity.com/article/15281/WowWee-Rovio-Resources-Reviews-Hacks-Conversations-and-More/ RoboComunity]. Se añaden al cuerpo del trabajo como anexo porque se considera que podrían ser interesantes de cara a una potencial y futura modificación o hackeo de ROVIO, de manera que su manejo y configuración se pueda realizar de forma completamente independiente de la plataforma software que se utilice.
  
* Procesador principal - [http://www.marvell.com/ Marvell] "PXA270M"
+
* Procesador principal - [http://www.marvell.com/ Marvell] "PXA270M", de la familia ''Xcale''.
* Frecuencia de Reloj - Hay un cristal de 24 Mhz, aunque se puede ver uno de 12 en algunos modelos.
+
* Frecuencia de Reloj - Hay un cristal de 24 Mhz y se puede ver otro de 12 cerca del primero.
* Memoria Principal - 8MB RAM; 2MB flash.
+
* Memoria Principal - 8MB de RAM (datos) y 2MB de flash (código).
  
 
* Hay al menos dos puertos serie (UARTs) en el robot, el /dev/ser0 que se usa como puerto "NS" para comunicar el sub-sistema North Star, y el /dev/ser1 que parece manejar los comandos "MCU" para el control del motor. Envía los datos a la placa de motores en el cuerpo principal del robot.
 
* Hay al menos dos puertos serie (UARTs) en el robot, el /dev/ser0 que se usa como puerto "NS" para comunicar el sub-sistema North Star, y el /dev/ser1 que parece manejar los comandos "MCU" para el control del motor. Envía los datos a la placa de motores en el cuerpo principal del robot.
Line 345: Line 84:
  
 
* Las características WiFi están basadas en la biblioteca "Libertas" de Marvell. Se conecta a características WiFi especiales del procesador ARM Marvell. Estás características son muy hackeables/modificables en el programa principal. Se puede aceder a la memoria RAM directamente usando URLs especiales.
 
* Las características WiFi están basadas en la biblioteca "Libertas" de Marvell. Se conecta a características WiFi especiales del procesador ARM Marvell. Estás características son muy hackeables/modificables en el programa principal. Se puede aceder a la memoria RAM directamente usando URLs especiales.
 
* En estos momentos, WowWee no ha publicado ningún material open-source o de fuente-abierta, de manera que el hackeo o modificaciones del robot requiere una cosiderable cantidad de ingeniería inversa.
 
  
 
* La programación de alto nivel del robot es posible utilizando URLs especiales bien desde una aplicación independiente del navegador o bien desde dentro de una página web mediante JavaScript. WowWee ha publicado especificaciones para la mayor parte de las URLs posibles. Las especificaciones del API se pueden encontrar en la página web de [http://www.wowwee.com/static/support/rovio/manuals/Rovio_API_Specifications_v1.2.pdf WowWee Techonologies]. Existe un duplicado de las mismas en [http://www.robocommunity.com/download/14278/WowWee-Rovio-API-Specifications-v1.2/ RoboCommunity].
 
* La programación de alto nivel del robot es posible utilizando URLs especiales bien desde una aplicación independiente del navegador o bien desde dentro de una página web mediante JavaScript. WowWee ha publicado especificaciones para la mayor parte de las URLs posibles. Las especificaciones del API se pueden encontrar en la página web de [http://www.wowwee.com/static/support/rovio/manuals/Rovio_API_Specifications_v1.2.pdf WowWee Techonologies]. Existe un duplicado de las mismas en [http://www.robocommunity.com/download/14278/WowWee-Rovio-API-Specifications-v1.2/ RoboCommunity].
Line 362: Line 99:
 
* Finalmente, se comenta que existe un envoltorio (wrapper) para poder operar/interactuar con ROVIO mediante el sistema operativo [http://www.ros.org ROS].
 
* Finalmente, se comenta que existe un envoltorio (wrapper) para poder operar/interactuar con ROVIO mediante el sistema operativo [http://www.ros.org ROS].
  
== Referencias(?) ==
+
=Referencias(?)=
  
 
Pendiente de redacción
 
Pendiente de redacción
  
== Enlaces Externos(?) ==
+
=Enlaces Externos(?)=
  
 
Pendiente de redacción
 
Pendiente de redacción
 +
 +
[[M%C3%A1s_informaci%C3%B3n|Más información]]
 +
 +
[[Seguir_leyendo|Seguir leyendo]]

Latest revision as of 10:52, 25 June 2014

  • Project Name: Rovio en tareas de vigilancia
  • Author: Isidoro Gayo Vélez
  • Academic Year: 2011-2012
  • Degree: Graduate
  • Tags: rovio, visual control
  • Technologies: c, openCV, qt
  • Status: Discontinued
  • Source License: GPLv3


Resumen

El trabajo que aquí se va a presentar trata sobre la aplicación del robot ROVIO de la empresa Wowwee Technologies a tareas de vigilancia. Para ello se utilizará la biblioteca de visión artificial OpenCV, que será la encargada de procesar la información proviniente de la cámara wifi de ROVIO en un ordenador portátil. Como sistema operativo de base para el desarrollo de esta aplicación se utilizará GNU/Linux, en concreto la distribución Ubuntu Lucid. La aplicación será desarrollada enteramente en lenguaje C.

Introducción

Pendiente de redacción...

OpenCV

Si se desea profundizar en OpenCV hay varias fuentes de donde obtener información. Una de ellas es el libro Learning OpenCV: Computer Vision with the OpenCV Library escrito por Gary Bradski y Adrian Kaehler y publicado por la editorial O'Really. En la página web de OpenCV, WillowGarage, también existe abundante información para quienes no dispongan del libro en cuestión. Y en la Red hay numerosa información relacionada con el uso de la biblioteca, que si bien puede estar más o menos desperdigada, también supone una buena fuente de consulta. Por último, los ejemplos que se incluyen en la biblioteca también son una fuente de informacación utilísima. En el desarrollo de este trabajo se han utilizado todas estas fuentes en mayor o menor medida (seguir seguir leyendo OpenCV).

Segmentación de la imagen

El procesamiento de imagen digital suele comprender varios pasos intermedios antes de tener una imagen útil con la que el robot pueda realizar algún tipo de acción. Este procesamiento es necesario porque la cantidad de información proviniente de una imagen tomada desde una cámara es enorme y significaría una carga en la cpu de nuestro robot innecesaria (seguir leyendo Segmentación de la imagen).

Seguimiento de objetos

(Trabajo en curso) -> Pendiente de redacción

Anexos

Anexo A: Configuración de ROVIO

El proceso de configuración de Rovio viene perfectamente explicado en el manual que se adjunta con el robot, por lo que se remite al lector a dicho manual o a su versión electrónica, descargable desde la web del fabricante o desde Robocomunity. No obstante, conviene remarcar un par de cosas a tener en cuenta para dicha configuración.

Rovio se puede configurar tanto mediante el programa suministrado por el fabricante como de forma manual. El programa sólo funciona en entornos Windows, por lo que habrá que tener disponible un ordenador con este sistema operativo si nos decidimos por la instalación automática. La instalación manual mediante protocolo ad-hoc puede resultar más compleja para quien no esté familiarizado con estas cosas, pero tampoco entraña dificultad alguna.

El segundo punto a comentar es que no se debe olvidar dejar desmarcada la opción Enable User Authentication de la solapa Security en el menú de configuración de Rovio, tal y como se muestra en la imagen adjunta, ya que de lo contrario, jamás recibiremos imagen alguna enviada desde Rovio a nuestro software de envoltorio. Esta es una información adquirida experimentalmente de forma dolorosa, ya que hizo perder varios días de desarrollo hasta que se cayó en la cuenta.

Anexo B: Cambios en el software "RovioCWrapProject"

Lo que figura a continuación quiere ser una especie de changelog con los cambios más importantes que se han realizado en el código inicial que figura en el repositorio de Rovio a fin de poder tener una perspectiva de los pasos llevados a cabo durante el desarrollo de este trabajo.

12/12/2011

  • Modificada la función rovioAccion del fichero rcwp.c para incluir posibilidad de consultar a Rovio la resolución de la cámara y los fotogramas por segundo (fps) a los que nos va a enviar las imágenes. Inicialmente estos valores se incluían en el código directamente. Ahora no es necesario cambiarlos a mano, el programa los actualiza automáticamente.
  • Modificada la función tracking del fichero rcwp.c para incluir movimientos de avance y retroceso para interaccionar respecto de la pelota.
  • Eliminada la función centroide_pelota en el fichero rcwp.c. Ahora el centroide de la pelota se calcula en el segmentador.
  • Eliminados los deslizadores de selección de color en ventana principal (imagen_cruda) y sustituidos por selección de color mediante clic izquierdo de ratón.
  • Se puede ajustar el margen de tolerancia del color (valores máximo y mínimo como antes) mediante deslizador en torno al color seleccionado con el ratón. Esta tolerancia se mide en píxeles y está comprendida entre 0 y 100. El margen de variación del color estará comprendido entre color seleccionado - tolerancia y color seleccionado + tolerancia.
  • Separado segmentador de la función main a un fichero independiente (segmentador.c y su cabecera correspondiente).
  • La ventana HSV se muestra/esconde pulsando la tecla t (por defecto está escondida).
  • Mejorado el filtrado del segmentador (fichero segmentador.c). Ahora funciona mejor incluso en condiciones de luz malas.

08/11/2011

  • Incluido segmentador de imagen en función main.
  • Incluidas ventanas de visualización de imagen_cruda, imagen_segmentada y HSV en la interfaz gráfica del programa (fichero main.c).
  • Incluidos deslizadores para seleccionar el color a segmentar (valores máximo y mínimo) en ventana imagen_cruda.

Anexo C: ROVIO por dentro

Los datos que se muestran a continuación no están basados en ninguna especificación oficial porque no hay ninguna publicada, si no que están recogidos y ordenados de varios sitios web dedicados a ROVIO o a temas relacionados con él, principalmente de Device Guru y RoboComunity. Se añaden al cuerpo del trabajo como anexo porque se considera que podrían ser interesantes de cara a una potencial y futura modificación o hackeo de ROVIO, de manera que su manejo y configuración se pueda realizar de forma completamente independiente de la plataforma software que se utilice.

  • Procesador principal - Marvell "PXA270M", de la familia Xcale.
  • Frecuencia de Reloj - Hay un cristal de 24 Mhz y se puede ver otro de 12 cerca del primero.
  • Memoria Principal - 8MB de RAM (datos) y 2MB de flash (código).
  • Hay al menos dos puertos serie (UARTs) en el robot, el /dev/ser0 que se usa como puerto "NS" para comunicar el sub-sistema North Star, y el /dev/ser1 que parece manejar los comandos "MCU" para el control del motor. Envía los datos a la placa de motores en el cuerpo principal del robot.
  • El puerto USB del robot se puede usar de dos formas diferentes. Si se conecta el USB al ordenador antes de encender el robot, los 2Mb de memoria flash aparecerán como un volumen de disco sin formatear. Este método es peligroso pues podría dejar al robot totalmente inservible si se procediese al formateo. En el segundo caso, si se conecta el USB después de que se enciende el robot, el puerto proporciona una conexión serie que es necesaria par la conficuración inicial de ROVIO.
  • El procesador ARM ejecuta el servidor web, controla la webcam, el flujo de audio y vídeo (flujo multimedia) y realiza el control general del robot. Ejecuta el sistema operativo de fuente-abierta "eCOS". El servidor del flujo multimedia está basado en una variante del servidor de flujo "Spook". Utiliza el protocolo "RTSP" para enviar el audio y vídeo desde la cámara web y micrófono de ROVIO a nuestro ordenador. El envío del audio desde el ordenador al ROVIO (para ser reproducido en el altavoz del robot) se realiza mediante un método propietario (ver GetAudio.cgi en el API del robot).
  • Las características WiFi están basadas en la biblioteca "Libertas" de Marvell. Se conecta a características WiFi especiales del procesador ARM Marvell. Estás características son muy hackeables/modificables en el programa principal. Se puede aceder a la memoria RAM directamente usando URLs especiales.
  • La programación de alto nivel del robot es posible utilizando URLs especiales bien desde una aplicación independiente del navegador o bien desde dentro de una página web mediante JavaScript. WowWee ha publicado especificaciones para la mayor parte de las URLs posibles. Las especificaciones del API se pueden encontrar en la página web de WowWee Techonologies. Existe un duplicado de las mismas en RoboCommunity.
  • Dentro de ROVIO (en el segmento superior del cuello, justo detrás de la cámara web) está el detector NorthStar que forma parte del sistema TrueTrack(tm) para localizar al robot en una habitación respecto de un faro (o punto fijo). Es un módulo separado que tiene su propio procesador digital de señal como cerebro. Se conecta a la CPU principal mediante un puerto serie. Se puede leer más sobre NorthStar aquí http://www.evolution.com/products/northstar/. Se puede encontrar información relacionada con modificaciones en el sistema TrueTrack(tm) en este enlace http://www.thingiverse.com/thing:7101.
  • La cámara web usada en el ROVIO es una OV7670 de la firma OmniVision, aunque hay que decir que el firmware incluido en el robot puede controlar otros modelos.
  • El códec de audio y el controlador del altavoz están basados en el integrado WM8976, fabricado por Wolfson Microelectronics. Se puede acceder sin problemas a su hoja de características a través de una búsqueda en internet, ya que en su página web parece no encontrarse disponible.
  • Un integrado marcado como 8051uC (WinBond W99100DG) parece proporcionar cierto control periférico pero los detalles están pendientes de conformación.
  • En la página web de RoboCommunity se encuentran publicados los ficheros fuente del código interno de ROVIO. Estas fuentes han sido publicadas recientemente por WowWee Technologies y permiten modificar y reprogramar parcialmente el comportamiento de ROVIO. En este hilo del foro de RoboCommunity se puede encontrar información relacionada con el sistema operativo eCos y el código fuente de ROVIO para este sistema.
  • Finalmente, se comenta que existe un envoltorio (wrapper) para poder operar/interactuar con ROVIO mediante el sistema operativo ROS.

Referencias(?)

Pendiente de redacción

Enlaces Externos(?)

Pendiente de redacción

Más información

Seguir leyendo