Proyecto Fin de Carrera

Robótica, software y telecomunicaciones.

Negar una variable de tipo bool

Me gustaría compartir un tip muy simple, pero que me ha costado mucho dar con él, de hecho no he encontrado nada en internet, ni en castellano ni en inglés.

Se trata de negar una variable booleana, es decir, la negación lógica que se suele representar con el símbolo ¬, de forma que si a es falso, ¬a sería cierto.

Lo que he visto por internet eran las siguientes expresiones:

-a;
!a;
!~a;
a!=a;

Pero ninguna de esas expresiones hacen lo esperado.

La solución me la ha dado un profesor del departamento de Robótica (gracias Paco), que ha dado con ella tras unas pruebas:

a=!a;

De esta forma si a=true; después de la expresión a=!a; entonces a tendrá el valor false.

 

Anuncios

31 marzo 2011 Posted by | all | | Deja un comentario

Scope, namespace y ::

Es posible que entre los errores que nos encontramos en la fase de compilación, se encuentren errores relacionados con el SCOPE. Aunque no son errores muy comunes y uno puede advertirlos con facilidad, os explicaré brevemente cómo evitarlos o solucionarlos.

En otras entradas del blog he mencionado expresiones del tipo worker::compute() o std::cout>>, la expresión de cuatro puntos :: se conoce como operador de ámbito, y su función principal es evitar los problemas de SCOPE.

El problema de SCOPE ocurre principalmente cuando se usan dos o más métodos con el mismo nombre pero con implementaciones diferentes. Por ejemplo nosotros podemos estar haciendo una calculadora para lo que incluiremos una librería matemática math.h que tiene sus propios métodos (sum(), plow(), abs()…), si nos creamos un método llamado sum(), y luego en main() llamamos a sum(), obtendríamos un claro error de SCOPE al intentar compilar, pues de este modo el compilador no puede averiguar si en main() nos referimos al sum() de math.h o al sum() que nosotros hemos creado.

Esto se soluciona con el operador de ámbito ::, de modo que en main() podríamos escribir math::sum() o main::sum() para que el compilador sepa a cual de los dos nos referimos. De forma que para asegurarnos de no tener problemas de SCOPE lo ideal sería utilizar contínuamente el operador de ámbito, sin embargo puede llegar a ser engorroso si usamos a menudo los métodos de un ámbito determinado. Es decir, imaginemos que en nuestra calculadora solemos usar con mucha frecuencia el math::sum(), y sólo algunas veces usamos main::sum(), en ese caso podemos usar:

using namespace math;

De este modo en caso de que el compilador se encuentre con sum(), da por hecho que nos referimos a math::sum().

30 marzo 2011 Posted by | all | , , | 1 comentario

Configurar componente con GUI

En la entrada anterior explicaba cómo conseguir que una GUI creada con QT Creator se comunicase con nuestro componente, sin embargo dejé pendiente la configuración para que todo eso funcionase.

El primer archivo a modificar es el config.h que se encuentra en micomponenteComp/src/, es un archivo muy simple, y tan sólo tenemos que decomentar la línea en negrita:

#ifndef CONFIG_H
#define CONFIG_H

// Comment out this line if your application has a QtGui
// #define USE_QTGUI

#define PROGRAM_NAME    “Kinect”
#define SERVER_FULL_NAME   “RoboCompKinect::Kinect”
#endif

Con eso le indicamos a la configuración que queremos usar QT para implementar nuestra GUI.

También tenemos que añadir el archivo .h con un include al worker.h, simplemente abrimos el worker.h y escribimos lo siguiente en la cabecera:

#include "nombre_de_mi_GUI.h"

Esto se hace igual que si incluimos cualquier librería, como opencv o iostream

Por último modificaremos el archivo CMakeLists.txt que se encuentra en el mismo directorio que los archivos anteriores. Simplemente añadimos las líneas en negrita, debajo de los respectivos comentarios:

# Graphical User Interfaces
SET (UIS nombre_de_mi_GUI.ui)

# Qt4
QT4_WRAP_UI( UI_HEADERS ${UIS} )

Con esto estaría todo configurado para añadir widgets y conectarlos con nuestro componente, para ello habría que repetir los pasos que se explican en la entrada anterior.

 

29 marzo 2011 Posted by | all | , , | 2 comentarios

GUI con QT4 en RoboComp

Las librerías QT de Nokia(antes Troltech) permiten crear una GUI (interfaz gráfica de usuario) para un programa con relativa facilidad, pero además de la GUI, estas librerías librerías tienen un toolkit muy completo, con máquina de estados, control de excepciones, tratamiento multimedia, etc.

RoboComp hace uso de las capacidades de QT4, tanto para la GUI como para otras tareas, por lo que si queremos crear una GUI para nuestro componente, lo mejor es hacerla con QT4.

Logo de QT

Para empezar, podemos ojear la información que nos da la propia Nokia aquí, sin embargo podemos usar QT Assistant que es un asistente con toda la documentación que necesitan los desarrolladores que usen QT. Como nosotros desarrollamos con KDevelop, tenemos la ventaja que este IDE (Entorno de Programación Integrado) integra a QT Assistant de forma que su uso es muy cómodo.

Siguiendo los tutoriales, uno se da cuenta que es muy fácil crear una primera GUI con QT4, más aún cuando usamos un programa de edición de GUI como QT Creator. Sin embargo a la hora de unir la GUI que puedes hacer siguiendo un tutorial, con el componente creado en RoboComp, parece que la tarea se complica.

En realidad no es tan complicado, los pasos son muy simples, crear un método SLOT, crear un widget con SIGNAL, y conectarlos.

El método SLOT no es más que un método (en C y otros lenguajes se llaman funciones) que tratará la comunicación con la señal, y se crea como cualquier otro método que queramos. Un ejemplo podría ser capturar el valor de un slider(barra desplazadora, fader), o ejecutar cualquier tarea al ser presionado un botón.

Para crear un método en RoboComp, primero lo declaramos en worker.h, debajo de public slots:

public slots:
void compute();
void pb_say_hello();

Luego lo desarrollamos en worker.cpp debajo de compute{}:

void Worker::pb_say_hello()
{   
   std::cout << "Hello!";
}

Con esto tenemos creado nuestro método SLOT listo para conectar a un widget con SIGNAL, el nombre pb_say_hello puede ser cualquiera, y el método puede tener argumentos de entrada o de salida, depende de nuestras necesidades, aunque sí suele ser conveniente que podamos identificar el método con la GUI, por ello pb_ hace referencia a “push button”, pero vale cualquier referencia.

El siguiente paso es crear el widget con SIGNAL, los widgets son cualquier elemento gráfico de una GUI, como una barra de desplazamiento, un botón, la caja de texto, un frame de video, texto no editable o la propia ventana que sujeta a todos los demás elementos. Los widgets con SIGNAL son aquellos que permiten la interacción del usuario con el programa, por ejemplo widgets con SIGNAL son los botones (dicen si están presionados o no al programa), las cajas de texto editable (pueden comunicar su texto al programa), o los sliders (pueden variar su posición), sin embargo los frames de video, los textos fijos o las ventanas que siven de soporte a otros widgets, no emiten SIGNAL.

Con QT Creator crear un widget es algo realmente sencillo, el único detalle que tenemos que tener en cuenta es darle un nombre al widget para que los podamos asociar cómodamente al método SLOT. De forma que en vez de tener botón1, botón2, botón3… tengamos say_hello_pb, stop_pb, close_pb…

Simplemente queda crear la conexión, ésta la hacemos en worker.cpp en el constructor. Si no sabeís qué es o dónde está el constructor, de forma rápida diré que en el constructor es donde se inicializan todas las variables que usemos y suele ser lo primero que encontramos en worker.cpp, se llama:

 Worker::Worker(...) : Qwidget(parent)
{
...
}

Entre esos corchetes será donde realizemos la conexión entre el método SLOT y el widget con SIGNAL. Según he visto en otros componentes de RoboComp, esto lo hacen al final del constructor, así que sería mejor seguir el orden y añadirlo en la parte baja del constructor. Quedaría así:

 Worker::Worker(...) : Qwidget(parent)
{
...
...
connect(  say_hello_pb,SIGNAL( clicked() ),this,SLOT( pb_say_hello() )  );
}

Como se puede advertir en la línea anterior, say_hello_pb es el widged, pues no lleva los paréntesis () característicos de los métodos. Además, say_hello_pb activa una señal cuando llama a su propio método clicked(), ya que un botón puede emitir una señal al dejar de pulsarlo, el nombre de estos métodos se pueden consultar de la información de QT, pero el propio KDevelop te ayuda sugiriéndote los métodos. Lo siguiente en aparecer es this, que se refiere a quién tiene el SLOT a conectar, en nuestro caso es worker, y cuando llegue la señal, se ejecutará el método pb_say_hello().

En principio con esto bastaría, y sería el proceso que hay que realizar para que cada widget de nuestra GUI pueda conectarse con nuestro componente, sin embargo antes de esto, deberíamos realizar algunas modificaciones, como añadir el .h de nuestra GUI en worker.h o modificar archivos como el ../etc/config o el ../src/CMakeLists.txt, esto lo contaré en otra entrada, pero si no queréis esperar podeís dejar un comentario con las dudas.

29 marzo 2011 Posted by | all | , , | 1 comentario

Novedades en KinectComp

En Robolab seguimos trasteando con la Kinect, con su calibración y con el cómo pasar los datos de imagen y profundidad a coordenadas en 3D. La semana pasada se hicieron algunos cambios en las interfaces (archivos .ice) kinect y rgbd. Lo que me lleva a modificar mi código para trabajar con estas nuevas interfaces. Estos cambios son debidos a la calibración de la kinect y al ajuste que se aplica para hacer coincidir los datos de profundidad sobre la imagen RGB o bien la imagen sobre la profundidad.

De momento, estos métodos son getDataRGBZinIR() y getDataRGBZinRGB(), ambos métodos nos devuelven la Z en lugar de la DEPTH (profundidad), ya que es más útil porque podríamos pasar esa información a un sistema cartesiano con coordenadas XYZ, además para mi caso concreto, también es más útil pues los movimientos que haremos sobre la cámara serán movimientos planos en lugar de movimientos esféricos con centro en la Kinect.

Diferencias entre el concepto de Depth y Z.

Diferencias entre el concepto de Depth y Z.

Respecto a los métodos de conseguir los datos de imagen, es debido a que la cámara IR y la cámara RGB están desplazadas en el eje X (suponiendo un sistema cartesiano de tres dimensiones XYZ), y por tanto es importante usar como referencia una cámara u otra.

Estos cambios han hecho que me interese más aún por el componente que gestiona la Kinect en RoboComp y por el mismo driver de OpenKinect. He realizado algunas modificaciones en KinectComp de forma que sea más versátil y potencialmente más capaz. Por ejemplo, es interesante que en la interfaz de KinectComp (archivos .ice) estén definidos métodos con todas las posibilidades de la Kinect, pues aunque no se puedan implementar en este momento, es posible dejar esos métodos vacios y desarrollarlos más adelante sin interferir con los programas o componentes que se conectan a KinectComp.

Por este motivo he estado pensando en varios métodos, además de los existentes. Un método casi obligado sería el de calibrar la Kinect. Ya que sería conveniente poder calibrar cada Kinect antes de obtener los datos de imagen y profundidad, hasta ahora KinectComp calibraba la Kinect con unos valores de calibración genéricos proporcionados por Nicolas Burrus en su wiki. He modificado el código para que use variables en esa calibración, ya que cada Kinect tiene unos valores específicos asociados, de forma que con un método del tipo setCalibration(vectorCalibration[]) podamos pasar los valores específicos de nuestra Kinect en caso de que los conozcamos.

Otros métodos son asociados al LED de la Kinect de forma que podamos cambiar y leer su estado, esto puede ser útil para nuestra aplicación como un método de comunicación entre el programa y el usuario. Por ejemplo, si el usuario está demasiado cerca de la kinect, ponemos el LED en rojo, y si está a una distancia óptima se pone a verde.

Tambien sería ideal tener métodos para el audio o el acerelómetro de la Kinect, aunque en este momento no se le vaya a dar uso, porque aún no está bien implementado en el driver.

Como apunte final respecto a los métodos, creo que es conveniente evitar los parámetro en funciones siempre que se pueda, por ejemplo, se podría usar el siguiente método para la modificación del color del LED:

void setLEDcolor(int color);

Sin embargo, veo mucho más interesante crear la misma funcionalidad con un serie de métodos, pues el tener varios métodos no es algo necesariamente negativo:

void setLEDred();

void setLEDgreen();

void setLEDorange();

28 marzo 2011 Posted by | all | , , , | 1 comentario

Mejorar el código con variables

Si miramos cualquier libro sobre programación, nos encontraremos una gran cantidad de consejos y trucos para mejorar el código, estos normalmente se van convirtiendo en normas universales, ya que su origen viene de la experiencia de la mayoría de programadores.

Algunos consejos tratan de comentar adecuadamente lo que se escribe, usar nombres de funciones y variables que sean simples, pero que a su vez describan de forma clara su propósito. Otros hacen mención a la identación del código, de forma que aún sin sintaxis coloreada podamos observar la estructura de todo el archivo.

Hoy yo os voy a recomendar otra: No usar valores en las llamadas a funciones o en el procesado de información, mejor usar variables.

Esto tiene mchas ventajas, pero principalmente son 2:

  • Código más legible.
  • Fácil modificación del código.

4 marzo 2011 Posted by | all | | Deja un comentario

Por qué calibrar la Kinect

Hace tiempo publiqué una entrada en la que hablaba del último cambio de rumbo del proyecto, cambio que entre otras cosas me llevaba a usar la kinect. En la siguiente, os contaba sobre la kinect, y los primeros problemas que tenía para hacerla funcionar, pero que finalmente solucioné.

Trabajando con ella conseguí pintar encima de la imagen de la cámara de vídeo en función de la profundidad que detectaba el sistema de infrarojos, y conseguí pintar en otro color, los objetos a determinada distancia y determinado margen de error, todo controlado con unos sliders (barras desplazadoras) de QT4. En otra entrada comentaré lo que he aprendido sobre la GUI con QT4 y el tema de los sliders.

Al observar la imagen resultante por la pantalla se apreciaba claramente un nuevo problema, y era que la imagen de la cámara RGB y la información de profundidad que nos da el sistema infrarojo no coincidían. Se puede apreciar claramente en la siguiente imagen. El error se incrementaba con la distancia, pero una barra vertical derecha sin información de profundidad permanecía constante.

Entonces, tenía que buscar una explicación al problema, y una vez entendido, intentar solucionarlo.

La explicación de la barra vertical derecha puede observarse en la siguiente imagen, se trata de la diferencia del ángulo de visión de ambas cámaras (IR y RGB) y la distancia ente ellas:

En cuanto al otro error es algo más complejo. Por una parte tenemos que entender que existe una traslación entre la imagen de la cámara RGB y de la imagen captada por la cámara IR debido a la separación que hay entre ambas. Por otro lado hay que tener en cuenta que la lámpara IR está desplazada hacia la derecha respecto a la cámara IR, lo que generará una “sombra IR” a la izquierda de cualquier objeto.

En este caso, la cámara infraroja sólo podría ver la cara b.

La traslación entre los ejes focales de las dos cámaras (IR y RGB), se puede calcular a groso modo midiendo la distancia entre las dos cámaras, que nos la podría dar el fabricante. Sin embargo, hay que tenen en cuenta que la precision en la fase de construcción de la Kinect no es perfecta, y por este motivo cada Kinect tiene una traslación específica. Es más, estamos suponiendo que existe una traslación entre los dos ejes focales, pero que estos se mantienen paralelos entre sí, cuando en realidad, por el mismo motivo anterior, cada kinect tiene una desviación característica entre esos dos ejes.

De ahí la necesidad de calibrar cada kinect con la que queramos trabajar.

Hasta ahora he encontrado 2 formas de calibrar las kinects, una que desarrollan desde la gente de ROS(un sistema operativo para robots), y otra que se está desarrollando en el entorno de OpenKinect.

La gente de ROS ha calculado los valores de las matrices de Traslación y Rotación genéricos que podemos encontrarlos aquí.

Por su parte, Nicolas Burrus junto con la comunidad OpenKinect desarrollan un programa llamado RGBDemo para que cada uno calibre su kinect, calculando los valores de las matrices basándose en el método de la calibración de cámaras estéreo de OpenCV. Además están trabajando en añadir características al programa como estabilizador de imagen de profundidad, relleno de huecos predecibles, detección de objetos, tracking humano, etc.

3 marzo 2011 Posted by | all | , , , , | 3 comentarios

El problema del CMakeLists.txt

Poco a poco voy entendiendo los mensajes que obtengo tras una compilación fallida.

Básicamente los errores pueden ser de los siguientes tipos:

  • Sintaxis: Estos son los que tenía con frecuencia, pero que poco a poco van desapareciendo, el motivo era tener en la cabeza mucho más fresca la sintaxis de MATLAB que la de C. Ocurre cuando no cierras un paréntesis, no pones un ; o no escribes bien las palabras reservadas.
  • Declaración: Estos errores son los que más rápido desaparecen cuando uno se ha enterado de la Programación Orientada a Objetos. Pero a veces tenemos despistes.
  • Errores de inicialización o proceso: Estos errores son bastante más comunes, al menos para mí. Por poner un ejemplo, a la hora de rellenar una QImage y salirme de los límites que yo mismo he establecido para esa imagen.
  • Errores con los MOC: Aún no tengo muy claro lo que son los MOC (MetaObjectC), pero lo que es cierto, que se tienen que crear en uns etapa concreta durante la compilación, si no es así, obtendrás errores del tipo MOC. En este caso la solución es borrar todos los MOC que se han generado en el intento de compilar, y posteriormente volver a compilar todo de nuevo. No es un error común, pero si te pasa es importante identificarlo, en ver de revisar tu código muchas veces sin encontrar ningún fallo.
  • Errores con el CMakeLists.txt: El CMakeLists.txt es ese archivo que contiene todo lo necesario para que la compilación se a correcta, por lo tanto, a veces, si la compilación no es correcta es porque falta o sobra algo en el CMakeLists.txt.

De momento no termino de entender la estructura del archivo CMakeLists.txt, ni su sintaxis, por lo que al principio me costaba mucho editarlo. El truco está en ser observador, es decir, mirar los CMakeLists.txt de otro componentes similares al tuyo. Lo mejor en estos casos es usar un programa comparador, que te resalta las diferencias entre dos archivos. Yo personalmente uso Meld, que es gráfico, claro e intuitivo, pero tambien podeís usar KDiff3 o el comando “diff” en un terminal.

1 marzo 2011 Posted by | all | , | 3 comentarios

   

A %d blogueros les gusta esto: