jueves, 27 de diciembre de 2007

Probando motores

He creado una especie de prototipo de Rabotron. El prototipo se basa en una madera donde van tres ruedas. Dos ruedas son las motrices y una tercera es una rueda loca. Supuestamente cada rueda motriz va con un motor. Para la prueba desmonté un destornillador eléctrico de 12 voltios que compré con anterioridad. Por lo que solo hay una rueda motriz, lo que hace que el robot de vueltas sobre si mismo.

Lo que no he tenido claro desde un principio es que motor poner al final. Necesito un motor que pueda ir a la velocidad de una persona andando (unos 5 km/h) y a la misma vez que pueda mover el peso del robot. El peso del robot es mas o menos:

- Batería 2 kg
- Portátil 2Kg
- Estructura ? kg (metacrilato)
- Los motores ? kg

Por lo que con esto ya tenemos las primera incógnitas, ya que no se cuanto pesa el robot, aunque supongo que unos 5-6 kg.

La prueba se hizo con el susodicho motor del destornillador que funciona a 550 rpm. Las ruedas usadas son de 5cm de diámetro. Por lo que el robot iría a una velocidad de unos 5 km/h. El cálculo esta hecho en una hoja de excel como se ve a continuación:

Diámetro rueda cm*vuelta RPM cm/minuto cm/hora m/hora Km/h
5 15,70796327 550 8639,3798 518362,788 5183,62788 5,18362788

Eso es a lo que se refiera a la velocidad. Si hablamos de la potencia, no tenemos ningún dato significativo en las instrucciones del motor, así es que en la practica pasaba lo siguiente:

De 0 a 3 kg de peso: El robot se mueve con facilidad.
De 3 a 6 Kg de peso: El robot se mueve con cierta facilidad aunque se nota una disminución de la velocidad.
Más de 6 kg de peso: Mucha dificultad (con mas de 7 presenta problemas de movimiento y se para).

En lo que se refiere al consumo, cosa que me importa también bastante para poder fabricar un puente en H acorde, los motores consumen en vacío unos 2 A (amperios). Cuando mueve cierto peso este consumo sube a unos 3 o 3 y algo. Cosa que no es muy alarmante pensando que podría ser peor.

El ejemplo que se ha comentado con anterioridad sale presentado en el siguiente vídeo, donde el robot pesa 5 kg:



Lo que quiero hacer es empezar a ver que motores son los adecuados para nuestro robot rabotron. Las ruedas lo que esta claro es que son muy pequeñas, por lo que iremos a unas de al menos 10-12 cm de diámetro. Por lo que con las formulas de antes podemos ver que con 270 rpm puede ir a la velocidad de un humano y así seguirlo:

Diámetro rueda cm*vuelta RPM cm/minuto cm/hora m/hora Km/h
11 34,55751919 270 9330,53018 559831,811 5598,31811 5,59831811

Pero la pregunta es.... ¿que motor? y sobretodo.. ¿soportará el motor el peso de rabotron? Las unidades de medida son kg/cm o mNm (miliNewton por metro) pero no las tengo del todo claro para saber si puede mover dicho peso o no.

Bueno habrá que seguir investigando.

jueves, 20 de diciembre de 2007

Instalación del proyecto Rabotron paso a paso

En este post vamos ha explicar como hemos instalado nuestro proyecto desde cero, para lo que hemos instalado... Debian, eclipse, java, gspca (spca5xx), player ( player 2.0.4 ).

Bueno comencemos:

1.- Lo primero que tenemos que hacer es descargar Debian. En nuestro caso hemos decido instalar Debian Lenny (en estos momentos testing) desde el netinstall. Para el que no se aclare mucho siempre puede descargar los ISOs de Debian completos, aun que si aquí ya andamos perdidos...

2.- Una vez instalado Debian...lo tenemos todo un poco “pelao” todavía.
Seguiremos por descargar Eclipse Classic de la página oficial.
Cuando se haya descargado Eclipse simplemente es descomprimirlo donde queramos.

Es posible que al ejecutar Eclipse nos de un error similar a este:

Error opening the editor
java.lang.ClassNotFoundException: org.eclipse.core.runtime.Plugin
...
...


Bien, que no cunda el pánico, esto es porque Eclipse no encuentra el entorno de ejecución de java (JRE). Para solucionarlo simplemente hay que instalar el JRE, hay variar soluciones.

La elegida esta vez ha sido descargar el jdk6 de la web de sun.

3.- Pasamos a instalar el driver gspca (antiguo spca5XX) necesario para la camara web. Ya explicamos como instalar tanto el driver spca como el gspca, Para mas información leer el post http://celtico-celtico.blogspot.com/2007/09/webcam-bajo-linux.html

4.- Instalar Camorama: Esto no es obligatorio, pero podemos instalarlo para ver si el driver gspca funciona correctamente (apt-get install camorama).

5.- Ya solo nos queda instalar player 2.0.4, hemos elegido esta versión por ser estable. Estuvimos probando con la versión de player 2.1.0 descargada del CVS (explicaremos como hacerlo con eclipse en otro post) pero decidimos quedarnos con la 2.0.4 por estar todavía en pruebas y en depuración de varios bugs la versión 2.1.0.

Antes de empezar con la instalación de Player 2.0.4 hay que instalar varias librerías:

- Librería “libtool” (apt-get install libtool) necesaria al hacer bootstrap para ejecutar automáticamente las ordenes:

libtoolize –force

- Instalar automake (apt-get install automake), necesaria al hacer bootstrap para ejecutar automáticamente las ordenes:

aclocal

autoheader

automake --gnu --add-missing

- Instalar pkg-config (apt-get install pkg-config), necesaria al hacer bootstrap para ejecutar automáticamente la orden:

autoconf

- Libreria “libjpeg62-dev”(apt-get install libjpeg62-dev), necesaria para que al instalar player se instale el driver cameracompress, de lo contrario este driver no aparecerá en la instalación. Esto se ve al hacer el configure, donde aparece una lista con los drivers que se instalarán y los que no.

Una vez instaladas estas librerías solo nos queda descargar player 2.0.4.

En nuestro caso hemos descomprimido el archivo y hemos añadido el driver Rabotron a la carpeta de “server/drivers/”, una vez hecho esto pasamos a la instalar Player:

./bootstrap


Si al ejecutar bootstrap nos aparece el siguiente error:

server/libplayerdrivers/Makefile.am:26: shell find $(top_builddir: non-POSIX variable name

server/libplayerdrivers/Makefile.am:26: (probably a GNU make extension)

hay que instalar la versión 1.9 de automake (apt-get install automake1.9) y volver a ejecutar bootstrap.

Una vez termine, ejecutamos el comando:

./configure

Al ejecutar configure nos dice los drivers que se instalarán y los que no serán instalados junto con la causa por la cual no pueden ser instalados:

The following device drivers will be included:

acts, amtecpowercube, aodv, bumpersafe, nd, cameracompress, camerav4l, canonvcc4, clodbuster, cmucam2, cmvision, dummy, er1, fakelocalize, festival, flockofbirds, garminnmea, iwspy, khepera, laserbar, laserbarcode, lasercspace, laserposeinterpolator, laserrescan, lasersafe, laservisualbarcode, laservisualbw, linuxjoystick, laserposeinterpolator, logfile, mapcspace, microstrain, obot, p2os, erratic, wbr914, ptu46, relay, rflex, sicklms200, sicknav200, sickpls, sicks3000, sphere, sonyevid30, urglaser, vfh, vmapfile, roomba, rabotron, wavefront, insideM300, skyetekM1, mica2

The following device drivers will NOT be included:

acoustics -- disabled by default; use --enable-acoustics to enable
amcl -- couldn't find (at least one of) gsl/gsl_version.h
artoolkitplus -- couldn't find required package artoolkitplus >= 2.0.2
...

Como vemos, en drivers instalados, aparecen los drivers de cameracompress y rabotron.

El siguiente paso es ejecutar las ordenes:
./make
./make install

Ya tenemos instalado Player!!. Ahora para ejecutarlo simplemente habrá que ejecutar:

./player ficheroCfg.cfg

Ahora CUIDADO, puede que nos de un error similar al siguiente al intentar arrancar player:

player: error while loading shared libraries: libplayerdrivers.so.2: cannot open shared object file: No such file or directory

En este caso hay que exportar el path:

export LD_LIBRARY_PATH=/usr/local/lib

Un consejo es que se ponga en el fichero “.bashrc” para que cada vez que se habrá un terminal se ejecute automaticamente.

Ya podemos ejecutar player en nuestro sistema:

./player ficheroCFG.cfg

* Part of the Player/Stage/Gazebo Project [http://playerstage.sourceforge.net].
* Copyright (C) 2000 - 2006 Brian Gerkey, Richard Vaughan, Andrew Howard,
* Nate Koenig, and contributors. Released under the GNU General Public License.
* Player comes with ABSOLUTELY NO WARRANTY. This is free software, and you
* are welcome to redistribute it under certain conditions; see COPYING
* for details.

Listening on ports: 6665

Ahora solo nos quedaría arrancar el proyecto y verlo rular...

Adicionalmente recordar que subimos un post en el que indicábamos como grabar video de nuestro escritorio Linux con el programa “recordMyDesktop”: http://celtico-celtico.blogspot.com/2007/11/grabacin-de-vdeo-en-linux.html

viernes, 14 de diciembre de 2007

Camino hacia la integración

Voy a permitirme hacer una analogía entre nuestro proyecto y una situación de la vida real que podría ocurrir.

Supongamos que existe una gran cordillera que separa dos países. Por intereses económicos y comerciales se quieren comunicar ambos países. Por lo que se decide hacerlo mediante un camino que atravesará con un túnel la gran cordillera.

Para que los dos países tengan los mismos gastos y esfuerzos, se decide que el país A haga un túnel hacia el país B y viceversa. La intención no es crear dos túneles distintos, sino que más bien es encontrarse los dos túneles a la mitad de la cordillera, formando así uno único.

¿Que os parece?
¿fácil?
¿difícil?

El que haya contestado debería de reflexionar sobre algunos aspectos clave que no han sido presentados. Por ejemplo, ¿de que países estamos hablando? No es lo mismo que se quieran comunicar dos países del tercer mundo con economías muy limitadas, que lo hagan dos superpotencias con sus ingenieros de caminos y sus excavadoras. ¿Y si hablamos de en que año situamos esto? Quizás hemos pensado en una época actual, pero si pensamos en cuando el cavernicola iba con un taparabos y una lanza para cazar... se nos viene a la cabeza la palabra utopía.

Hablando meramente del proyecto. Los dos componentes hemos empezado por polos opuestos (cada uno a un lado de la cordillera). }TOR{ ha estado programando en JAVA peleándose con la visión y ha ido bajando de niveles tocando el servidor de player. Por otro lado, celtico ha ido desde lo más bajo hacia arriba, soldando la placa de motores, programando los microcontroladores y creando el driver que se integra en player.

Ahora si que es el momento. Es el momento de la integración, si todo ha ido bien los túneles se encontrarán sin problemas, sino tendremos que ir buscando soluciones. Ahora hago la misma pregunta que antes sobre la integración de nuestro proyecto Rabotron....

¿Que os parece?
¿fácil?
¿difícil?

Pues la contestación es, como antes se ha explicado ... "depende". Tenemos que tener en cuenta que hablamos de tecnologías en constante desarrollo (player y javaclient), hablamos de hardware creado por estudiantes, de escasa información en la red.... Todo esto nos hace tener un cumulo de sensaciones de posibilidad de éxito en esta etapa o de fracaso.

Nunca se sabe, es el momento. Vamos.

miércoles, 5 de diciembre de 2007

Vista general de Rabotron

Como se puede observar ya hay gran cantidad de posts sobre el trabajo que se está realizando. Pero a veces no estoy seguro si la gente tiene claro la ubicación general del proyecto y más despues de hablar esta tarde con mi colega skool. Es por ello que quiero comentar brevemente la visión general del proyecto.

Rabotron se compone basicamente de un portatil en el que va instalado un servidor llamado PLAYER (en nuestro caso la version 2.1.0 compilada desde el cvs). Hemos tenido que compilarla manualmente nosotros ya que necesitamos gestionar una placa de motores para el movimiento de rabotron.

Es por ello que creamos un driver en lenguaje c , lo integramos en player y lo compilamos. No ocurre igual con la camara web (para la vision) ya que ya existe un driver creado v4l (video for linux) y es el que vamos a usar.

El servidor lo manejará un cliente creado a partir de Javaclient 2.0 (programamos en java). Dicho cliente tambien va instalado en el mismo portatil y se comunica con player a traves del puerto 6665.

Los perifericos usados es una camara web a traves de usb y una placa diseñada y creada integramente por nosotros encargada de gestionar el movimiento del robot. La placa es la que contiene el pic16f876 que programada en emsamblador se comunica por rs-232 con el driver de player.

Dicho esto, una secuencia logica de funcionamiento es: la camara web recibe una imagen y la envia de forma vertical (segun el dibujo de mas abajo) hasta javaclient2. Aqui la valoramos y decidimos el movimiento de rabotron segun las necesidades del entorno. El movimiento se le indica a player a traver del puerto 6665, gracias al driver creado se comunica con la placa por puerto serie y esta es la que lo trasforma en impulsos para las ruedas del robot.

Espero que todo haya quedado un poco más claro. Os dejo un dibujo aclaratorio:

Aplicando filtro Equalize (¿mejorando?)

El filtro realiza una mejora sustancial de la imagen capturada bajo circunstancias pobres de luz ó exponiendo a la víctima de Rabotron a una luz que no sea blanca, mientras que si realizamos en filtro en campo abierto no es tanta la ganancia que podamos llegar a tener comparada con la pérdida de tiempo que tenemos al aplicar la función anterior. El tiempo dirá si realmente merece la pena que realizamos el filtro, para empezar lo que hemos hecho es optimizar un poco el código para que la función tarde menos en ejecutarse, el código actual tarda un tiempo (calculado mediante pruebas) aproximado del 30% al 40% menor al código inicial de la función equalize.

El código en java del filtro de ecualizado optimizado es el siguiente:
public int [] filtroEqualizeTOR()
{
double max=255;
double[] binR= new double [tamano/3];
double[] binG= new double [tamano/3];
double[] binB= new double [tamano/3];
int [] binAux = new int [tamano];

double maxiR=0,maxiG=0,maxiB=0;
double miniR=255,miniG=255,miniB=255;
int aux=1;

//sacamos valor del pixel minimo y maximo para cada componente RGB
for (int k = 0; k < tamano/3; k++) { aux= k*3; binR[k] = imageRGB[aux]; binG[k+1] = imageRGB[aux+1]; binB[k+2] = imageRGB[aux+2]; } Arrays.sort(binR); Arrays.sort(binG); Arrays.sort(binB); maxiR=binR[(tamano/3)-1]; maxiG=binG[(tamano/3)-1]; maxiB=binB[(tamano/3)-1]; miniR=binR[0]; miniG=binG[0]; miniB=binB[0]; for (int k = 0; k < tamano/3; k++) { aux=k*3; binAux[aux]=(int)(((imageRGB[aux]-miniR)/(maxiR-miniR))* (double)max); binAux[aux+1]=(int)(((imageRGB[aux+1]-miniG)/(maxiG-miniG))*(double)max); binAux[aux+2]=(int)(((imageRGB[aux+2]-miniB)/(maxiB-miniB))*(double)max); } return binAux; }

Al estar trabajando con imágenes de 320x240 no es excesivo el tiempo ganado, pero todo lo que podamos recortar es tiempo que ganamos a la hora de ejecutar la orden a Rabotron para que se mueva. Para la demostración de este filtro voy a poner solamente un par imágenes de la ejecucion: En la parte superior de la ventana se ve la imagen captura sin aplicarle ningún tipo de filtro Mientras que en el canvas de abajo de la ventana se ve la misma imagen pero aplicándole el filtro de ecualización. En esta primera imagen podemos ver como al mejorar filtrar la imagen también mejoramos el binarizado de la misma al buscar a nuestra víctima, en este caso el color a buscar es el de la mano.

- Imagen 1-

En la segunda imagen ( Imagen 2 ) simplemente se ve la mejora realizada sobre el color de la imagen (Arriba sin aplicar filtro, Abajo tras aplicar el filtro), no mostramos el binariza:


-Imagen 2-

martes, 4 de diciembre de 2007

cambio comunicacion puerto serie

Hola a todos,

He cambiado el protocolo de comunicación del puerto serie. Lo he hecho mas que nada pensando en un mejor rendimiento de la comunicación. Antiguamente enviaba carácter a carácter desde el puerto serie del ordenado a la placa controladora de motores y el pic era el que daba confirmaciones de envío.


El esquema como recordatorio es algo así:


El cambio reside pues en la forma de envío. A partir de ahora no se hará de carácter a carácter, sino que se enviará una trama entera y es el pic el encargado de interpretarla, definiendo así nuestro propio protocolo.

La trama enviada tiene tres limitadores. Primero una "d" indicando que se refiere al motor derecho, despues va un numero (negativo o positivo) de 0 hasta 127 que indica la potencia del motor derecho. Luego hay un limitador "i" indicando que se refiere al motor izquierdo, al igual que antes después aparece un numero que indica la potencia de dicho motor. El ultimo carácter de la trama es el limitador "f", que indica que la trama ha finalizado.

Un ejemplo de trama:

d

2

5

i

-

6

4

f


Por supuesto que la longitud de la trama es variable, ponemos la máxima dimensión y la mínima:

d

-

1

1

5

i

-

1

2

7

f

d

5

i

7

f


Por ultimo y si por si es de ayuda os posteo el código del pic para recibir las tramas, interpretarlas y crear los impulsos necesarios para el control de los motores:


/*
----------------------------------------------------------------------
#include <16f876a.h> // Definiciones del PIC 16F876A
#include // Para el atoi
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT // Los Fuses de siempre
#use delay(clock=4000000) // Oscilador a 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// RS232 Estándar



// CONSTANTES /////////////////////////////////////////////////////////////////

int const lenbuff=12; // Longitud de buffer--> Ajustar



// VARIABLES EN RAM ///////////////////////////////////////////////////////////

int motder,motizq;
int sentido, sentidoder, sentidoizq;
int xbuff=0x00; // Índice: siguiente char en cbuff
char cbuff[lenbuff]; // Buffer
char rcvchar=0x00; // último carácter recibido
int1 flagcommand=0; // Flag para indicar comando disponible
char arg[lenbuff]; // para pasar el negativo
int cont=0; // contador de recibidos




// Declaración de Funciones ///////////////////////////////////////////////////

void inicializa(void);
void inicbuff(void); // Borra buffer
int addcbuff(char c); // añade carácter recibido al buffer
void procesa_comando(void); // Procesa comando
int aentero(void); // Pasa el buffer a un entero
int procesatrama(void); // Procesa una trama recibida


// INTERRUPCIONES /////////////////////////////////////////////////////////////


#int_rda

void serial_isr() // Interrupción recepción serie USART
{
while ( cbuff[cont-1]!='f' ) // entramos al bucle while
{
if ( kbhit () )
{
cbuff[cont]=getc(); // en la variable keypress se guardan los caracteres
cont++;
}
}
printf("Recibo: %s ",cbuff);
cont=0;
flagcommand=1; // Comando en Main
procesatrama(); //procesa la trama
inicbuff(); // Borra buffer
}




// Desarrollo de Funciones ////////////////////////////////////////////////////


void inicializa()
{
motder=motizq=0;
sentido=sentidoder=sentidoizq=0;
xbuff=0x00; // Índice: siguiente char en cbuff
inicbuff(); //char cbuff[lenbuff]; // Buffer
rcvchar=0x00; // último carácter recibido
flagcommand=0; // Flag para indicar comando disponible
}


void inicbuff(void) // Inicia a \0 cbuff -------------------
{
int i;

for(i=0;i Recibo d: %c ",cbuff[contador]);

contador++; //1

if(cbuff[contador]=='-')
{
sentidoder=0;
contador++;
printf("PT--> Recibo negativo der");
}
else
sentidoder=1;

i=0;

do
{ // Extraemos argumento D del buffer
arg[i]=cbuff[contador];
i++;
printf("PT--> Recibo num der: %c ",cbuff[contador]);
}while(cbuff[++contador ]!='i'); // hasta que llegue I


motder=atoi(arg); //valor motor derecha

printf("PT--> Mot der: %d ",motder);

//inicbuff(); //iniciamos el buffer de arg

for(i=0;i Recibo i: %c ",cbuff[contador]);

contador++; //en izq

if(cbuff[contador]=='-')
{
printf("PT--> Recibo negativo izq");
sentidoizq=0;
contador++;
}
else
sentidoizq=1;

i=0;

do
{ // Extraemos argumento D del buffer
arg[i]=cbuff[contador];
i++;
printf("PT--> Recibo num izq: %c ",cbuff[contador]);
contador++;
}while(cbuff[contador]!='f' && cbuff[contador]!=0x00); // hasta que llegue F


motizq=atoi(arg); //valor motor derecha

printf("PT--> Mot izq: %d ",motizq);

retorno = 1;
return retorno;
}


// Programa Principal /////////////////////////////////////////////////////////

void main()
{

setup_ccp1(CCP_PWM); //ccp1 modo PWM
setup_ccp2(CCP_PWM); //ccp2 modo PWM

setup_timer_2(T2_DIV_BY_16, 127, 1); // 488Hz (con XT=4MHz) esta frecuencia la podes variar cambiando los valores, fijate en el datasheet del micro que uses, que vienen las relaciones para calcular lo inherente a la frecuencia y al duty.
inicbuff(); // Borra buffer al inicio

printf("Soy el Pic y estoy activo\n");
enable_interrupts(int_rda); // Habilita Interrupción RDA
enable_interrupts(global); // Habilita interrupciones

do {

if(flagcommand) procesa_comando(); // Si hay comando pendiente
// de procesar ... lo procesa.

} while (TRUE);

}

// Procesador de Comandos /////////////////////////////////////////////////////

void procesa_comando(void)
{

flagcommand=0; // Desactivo flag de comando pendiente.

printf ("procesa comando: %d\n",motder);
printf ("procesa comando: %d\n",motizq);

if(sentidoder!=0)
{
printf ("Enciende DER\n");
output_high(PIN_C3);
}
else
{
printf ("Apaga DER\n");
output_low(PIN_C3);
}

set_pwm1_duty(motder); // Se cambia el ancho con el valor obtenido en canal 0.

if(sentidoizq!=0)
{
printf ("Enciende IZQ\n");
output_high(PIN_C4);
output_high(PIN_C5);
}
else
{
printf ("Apaga IZQ\n");
output_low(PIN_C4);
output_low(PIN_C5);
}

set_pwm2_duty(motizq); // Se cambia el ancho con el valor obtenido en canal 0.

inicializa(); // Borra buffer
}

----------------------------------------------------------------------
*/