Archivo de la categoría: GNU/Linux

Portfolio de proyectos

He ordenado un poco todo lo que tengo hecho, y lo he puesto en un portfolio de proyectos, bien claro. Enlaces a la wiki y a este mismo bloc (que ya tienen más de 12 años), la web de insectos, la de retroplaneta, y programación de diversas cosas, sobretodo mapas.

Quería un diseño muy minimalista, en blanco y negro, y creo que lo he conseguido.

OpenCV: detectar círculos

Una vez ya sabemos detectar las rectas, ahora vamos a la parte de detectar los círculos. OpenCV nos da el círculo de mejor ajuste, con su centro y su radio. Por sólo nos falta aplicar el algoritmo de calcular el error de ajuste, como se explica en el anterior post.

Como fichero de entrada tenemos un CSV con los nombres de los participantes, el nombre del fichero de las rectas, y el nombre del fichero de los círculos. Como salida tenemos el fichero resultados.txt con las puntuaciones obtenidas por cada uno de los participantes.

Así pues, ahora sólo falta llevarlo a la práctica. Espero que sea pronto.

Medir la rodondez de un círculo

Después de resolver el problema de medir la linearidad de una recta, ahora toca medir la redondez de un círculo. En la imagen hay una captura de la solución que hemos implementado.

Hay bastante literatura sobre el tema, pero he preferido buscar la solución por mi cuenta. Dado un círculo, openCV ya nos da el círculo de mejor ajuste, con el centre y el radio. La primera idea es recorrer todos los puntos del círculo y hacer el sumatorio de todas las diferencias entre el punto y el radio. El valor da una buena idea de cómo de bueno es el círculo, en términos absolutos. Pero si ahora escalamos x2 o por x0.5 nuestro círculo, los resultados serán diferentes. Por tanto, no he de buscar un error absoluto, sino un error relativo.

La segunda idea es medir áreas. Concretamente, el cociente entre la diferencia de áreas del círculo dibujado y el círculo aproximado, dividido por el área del círculo. Como se ve en el papel, ahora sí que obtenemos un valor relativo de la rodondez del círculo. Una cosa que hemos de tener en cuenta es que cuando sumamos áreas, hay que coger el valor absoluto de la diferencia, para que no haya cancelaciones.

Cuanto más se aproxima a 0 el valor, más bueno es el círculo. Si el valor lo resto de 1, entonces el círculo perfecto valdrá R=1, y R=0.98 no sería tan bueno. Ahora falta implementarlo en mi programa.

Experimento del péndulo para encontrar la constante de la gravitación g

Este verano estuve haciendo pruebas para captar la seal de un sensor LDR al ordenador, con un programa C++ que leía por el puerto serie los datos que enviaba el Arduino. Y se comprobaba como la luminosidad al atarcecer disminuye como una sigmoide (1).

Estas eran las primeras pruebas para el experimento que quería hacer y documentar, que es la búsqueda de la constante g a partir del experimento del péndulo. Resumiendo, medimos el periodo del péndulo para diferentes longitudes, y podemos encontrar la constante g=9.81 m/s2. Sabemos que el periodo del péndulo no depende de la masa, sino sólo de la longitud de la cuerda y de la constante g obviamente.

En la foto podemos ver el esquema del experimento. Tenemos un péndulo; un fotodetector con Arduino capaz de detectar el paso del péndulo; enviamos por el puerto serie al ordenador los datos; un programa C++/SDL lee los datos del puerto serie; procesamos los datos con Python y encontramos la recta de regresión; calculamos g y el error correspondiente. Todo esto es lo que desarrollaremos en el siguiente post.

A parte del experimento de encontrar la constant g, la idea es hacer una serie de experimentos que sigan el mismo esquema. Si tenemos diversos experimentos preparados, se podría hacer más adelante una propuesta didáctica para Secundaria.

Aprenendiendo LaTeX: el Tirant lo Blanc

Sería el mes de febrero o marzo que me puse a lidiar y estudiar LaTeX. El motivo principal era la edición de ecuaciones y la notación científica, que es donde LaTeX presenta grandes ventajas. Detrás de este trabajo hay la idea de hacer unos artículos (y/o libro) sobre aspectos curiosos y cotidianos relacionados con las ecuaciones diferenciales y métodos numéricos con Python.

Lejos de este objetivo científico, con el objetivo de aprender LaTeX, me puse a editar el Tirant lo Blanc (hubiese podido coger otra novela más corta!). Han pasado unos meses y al final he tenido tiempo de acabar mi edición del Tirant lo Blanc, que tengo el gusto de presentar en primicia mundial.

Como el text es muy largo (el pdf tiene casi 1000 páginas), se ha utilizado diversas técnicas de programación y substitución automática de texto, para así generar rápidamente el LaTex a partir de los textos originales (en formato txt o html). Esto quiere decir que, con los conocimientos adquiridos, la generación de un pdf de un texto o novela histórica la podría hacer de forma bastante rápida.

  • Edición del Tirant con LaTeX (pdf): Tirant lo Blanc
  • El texto original que proviene de cervantesvirtual.com: enlace
  • El Tirant lo Blanc en la Wikipedia: enlace

Detección de los dardos: Primeras pruebas

Ya tengo el prototipo del mueble, y ya puedo empezar a hacer pruebas para detectar los dardos. Como se ve en el video, la detección es muy buena. Si nos fijamos en la configuración, la webcam esta en el mismo plano XY que la diana. Esto creo que tiene ventajas en la detección del dardo; y desventajas: necesitaré 2 webcams, y posiblemente 3 webcams para posicionar con fiabilidad el dardo en la diana.

Utilizamos la librería OpenCV sobre C++, i el flujo básico consiste en hacer una captura de la webcam; lanzar el dardo; y hacer otra captura. Entonces se hace la diferencia de las imágenes; se convierte a binario; se aplica una máscara para eliminar buena parte del dardo. En este momento hemos de ver la part de abajo del dardo, y la punta bien clara. Tenemos un número limitado de puntos blancos. Con estos puntos calculamos el momento, que sería el centro de gravedad de los puntos. Y haciendo un bucle sobre todos los puntos puedo detectar el punt más inferior, que sería la punta del dardo. Finalmente, para eliminar posibles errores, calculo la distancia entre la punta y el momento, que ha de ser un valor pequeño.

Aún queda mucha faena. Por una parte, hay que traducir la detección del dardo a los posibles sectores donde está el dardo. Después añadir otra webcam. Y si añado una tercera webcam, seguramente necesitaré algún USB Hub. El objetivo es utilizar una sola Raspberry Pi 3 para todo el proyecto.

Fresar una foto en blanco y negro con CNC

Estos días estamos viviendo unos días inciertos en Catalunya. Es necesario salir a la calle y defensar el gobierno legítimo, rehusar de pleno la aplicación del artículo 155 de la Constitución Española, y no dejar pasar la oportunidad que representa las elecciones del 21D. Así que hemos hecho unos carteles para llevarlos a la manifestación del 11N, y todas las que sean necesarias. Actualmente, tenemos en las prisiones españolas: Jordi Sánchez, Jordi Cuixart, Oriol Junqueras, Dolors Bassa, Meritxell Borràs, Josep Rull, Raül Romeva, Carles Mundó, Joaquim Forn y Jordi Turull. Y el número puede aumentar.

Aquí expongo mi workflow para hacer un cartel en blanco y negro, a partir de una foto.

1. GIMP. A la hora de escoger la foto hemos de procurar que el fondo sea claro y contrastado con la foto, de manera que el GIMP detecte bien los contornos. Abrimos la foto con el GIMP, y desaturamos la foto (convertiéndola a escala de grises), y con la herramienta Umbral de color blanco-negro, escogemos la imagen que queremos fresar.

2. LibreCAD es el software que utilizo para generar el DXF. Importo la imagen, y creo una capa para definir las líneas de fresado. Como la broca que utilizaré es de 2mm, escojo un trazo de 2mm, y de esta manera me puedo hacer una buena idea de cómo quedará el resultado final. Resigo todos los contornos, lo hago de manera manual.

3. Todavía en el LibreCAD, he de definir los puentes de manera que no haya ninguna isla que se pueda desprender cuando después haga el fresado. Genero el fichero dxf.

4. Vuelvo al GIMP y relleno de negro las superfícies que quedarán vacías. De esta manera me puedo hacer una buena idea del resultado final, y prever el éxito de la pieza.

5. Con el script de python dxf2gcode puedo definir las propiedades del fresado. Concretamente, la profundidad del corte. Si la plancha de madera que utilizaré es de 3mm, la profundidad será de 3mm. Genero el fichero G-Code.

6. Con LinuxCNC ya puedo fresar la pieza, con una broca de 2mm. Una vez finalizado el trabajo, se lija y se corrigen posibles imperfecciones.

Todo el proceso está explicado en el video. En la imagen se ve cómo queda el resultado final con la foto de Raül Romeva, ahora en prisión. Ahora sólo queda hacer los carteles, y salir a la calle. La lucha continua.

Referencias:

script cncstroke: mejorando la conversión DXF a G-Code, II

Creo un proyecto con LibreCAD, creo la capa »stroke», y aquí metemos las polilíneas, tal como se ha explicado en el anterior artículo [1], y tal como se muestra en la imagen. Con lenguaje C++, lo primero que hacemos es leer el fichero dxf y detectar la capa stroke, con todos sus puntos. En el LibreCAD todas las líneas son iguales, tienen un origen y un final. Pero a mi me interesa detectar las polilíneas, que son las líneas enlazadas de manera que el punt final de una línea es el mismo que el punto de origen de la línea siguiente. Recordemos la estructura de datos que se utiliza:

struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble length;
};

struct polilinia {
\tstd::vector vlinia;
\tdouble length;
};

std::vector vpolilinia;
Recorremos todas las líneas, y vamos añadiendo elementos al vector polilínea, y dentro de cada polilínea vamos añadiendo elementos al vector línea. Para cada línea y para cada polilínea, tenemos calculada su longitud. Una vez tenemos rellenado el vector de polilíneas con sus líneas, ya podemos recorrer todas las polilíneas y generar la salida G-Code, de manera que el inicio de una polilínea está a profundidad -2mm, y el final de la polilínea está a profundidad 0mm, tal como se aprecia en este trozo del G-Code resultante, y en la imagen:

(* POLILINE #2 *)
G0 X 30 Y 30
G0 Z 3.000
F150
G1 Z -2.0
F400
G1 X 50 Y 40 Z -1.11
G1 X 60 Y 30 Z -0.55
G1 X 70 Y 40 Z 0
F150
G0 Z 5.000

Como se aprecia en el G-Code, posicionamos la broca en el punto (30,30), agujereamos hasta -2.0mm, y vamos pasando por tres puntos sucesivos mientras vamos levantando de forma proporcional la broca hasta llegar al punto (70,40), punto en que la broca deja de atacar la superficie. Así conseguimos un trazo variable, tal como se aprecia en la imagen. Con esta técnica podremos perfilar detalles en los trabajos CNC, como pueden ser los cabellos de un retrato, o el trazo en una caligrafía.

Como se aprecia en la imagen (extraída del proyecto de fresar el retrato de Albert Einstein en blanco y negro, en otra entrada mostraremos el resultado final), se ve el resultado de aplicar el script cncstroke para las polilíneas agrupadas en la capa stroke del proyecto LibreCAD. Vemos dos trazos que acaban en punta (en la part izquierda de la imagen, sobre madera), y otro trazo, donde no se ha aplicado el script, que no acaba en punta. En la parte derecha de la imagen vemos un trozo del proyecto LibreCAD donde se han definido estas líneas (dos que pertenecen a la capa cncstroke, y otra que se fresa normal). Así pues, con un uso correcto del proyecto LibreCAD, podemos aumentar la resolución del fresado CNC, consiguiendo un efecto más real y detallado.

Puedes descargar y estudiar el código del script cncstroke desde el siguiente enlace [2] .
Referències:

Nota aclarativa: En el proyecto cncstroke utilizamos el término polilínea como sucesión de líneas enlazadas. Ahora bien, para generar estas líneas enlazadas, en LibreCAD, utilizamos la herramienta línea (y no la herramienta polilínea) (En una futura versión se estudiará la posibilidad de utilizar la herramienta polilínea, o ambas).

script cncstroke: mejorando la conversión DXF a G-Code, I

Uno de los trabajos típicos que se hacen con la CNC es el retrato de personajes en formato blanco y negro. Por ejemplo, son conocidos los retratos del Che Guevara o Albert Einstein. Estos días estoy trabajando con la imagen de Ovidi Montllor [1].

La idea es sencilla y conocida. Importo la imagen en LibreCAD [2] (el software CAD que utilizo, que es open source), defino la silueta, y defino unas áreas de hatching que seran fresades (rebajadas). Utilizo el programa dxf2gcode [3] para exportar a G-Code, y freso todo el trabajo a una profundidad de 1.5mm. La limitación que tengo es que la fresa que utilizo es de 2mm de diámetro, y esto limita la resolución final que pudo alcanzar.

Estoy programando un pequeño script (cncstroke [4]) para mejorar la resolución de mis trabajos. Imaginemos por ejemplo los cabellos de Albert Einstein. Lo ideal es que las mechas de cabello acaben en punta, haciendo un trazo que pase de grueso a fino. Esto se consigue haciendo que la profundidad de ataque de la broca sea variable, desde 1.5mm de profundidad en el inicio del trazo hasta 0mm al final del trazo. Pero para que el invento funcione, no he de utilizar una broca normal, sino una broca V-Shape (con punta de 40 grados). De esta manera conseguiré el efecto de hacer un trazo que pase de 2mm de ancho en el origen a 0 mm en el extremo. Esta técnica también será muy útil para fresar letras que simulen trazos variables.

La idea es que en els mis proyectos de LibreCAD tendré una capa con la silueta básica (que fresaré con una broca de 2mm), y una capa (que siempre nombraré stroke) que constará de polilíneas que representarán el trazo variable desde 2mm hasta a 0 mm de ancho. Con esta segunda capa podré hacer los detalles como cabellos o caligrafía de letras.

En este primer artículo sólo expongo la estructura de datos que me permitirá gestionar la generación del G-Code a partir del proyecto de LibreCAD. Cuando recorra (programáticamente) el contenido de un proyecto LibreCAD (formato dxf), tendré que buscar la definición de la capa stroke y de los puntos que contiene. Esto es relativamente fácil de hacer. Esta sucesión de puntos se pueden agrupar en polilíneas, entendiendo que una polilínea es un conjunto de líneas que van empalmadas.

Como el número de poliíneas es variable, y el número de líneas que contiene una polilínea también es variable, la mejor manera de programar esta estructura con lenguaje C++ es utilizar vectores.

struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble dist;
};

struct polilinia {
\tstd::vector vlinia;
\tdouble dist;
};

std::vector vpolilinia;

Una línea consta de un punto origen y de un punto final (fácilmente se podrá calcular la longitud de esta línea). Una polilínea consta de un conjunto de líneas, y la longitud de la polilínea será la suma de les longitudes individuales.

A continuación mostramos el ejemplo mínimo de cómo se pueden gestionar diferentes polilíneas, cada una con un número variable de líneas.

// g++ -o polilinia polilinia.cpp
#include
#include
#include
#include
#include

struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble length;
};

struct polilinia {
\tstd::vector vlinia;
\tdouble length;
};

std::vector vpolilinia;

using namespace std;

int main() {
\t
\tdouble lengthlinia,lengthpolilinia;
\tpolilinia polilinia1;\t
\tlinia linia1;

\tlengthpolilinia=0;

\tlinia1.x0=34;
\tlinia1.y0=30;
\tlinia1.x1=120;
\tlinia1.y1=122;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;

\tpolilinia1.vlinia.push_back(linia1);

\tlinia1.x0=320;
\tlinia1.y0=322;
\tlinia1.x1=420;
\tlinia1.y1=422;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;

\tpolilinia1.vlinia.push_back(linia1);
\t
\tpolilinia1.length = lengthpolilinia;
\t
\tvpolilinia.push_back(polilinia1);
\t
\t//cout << vpolilinia.at(0).vlinia.size() << endl;

\t//anem per una altra polilinia
\tpolilinia1.vlinia.clear();
\tlengthpolilinia=0;

\tlinia1.x0=134;
\tlinia1.y0=130;
\tlinia1.x1=220;
\tlinia1.y1=222;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;

\tpolilinia1.vlinia.push_back(linia1);

\tlinia1.x0=220;
\tlinia1.y0=222;
\tlinia1.x1=320;
\tlinia1.y1=322;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;

\tpolilinia1.vlinia.push_back(linia1);

\tlinia1.x0=220;
\tlinia1.y0=222;
\tlinia1.x1=320;
\tlinia1.y1=322;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;

\tpolilinia1.vlinia.push_back(linia1);

\tpolilinia1.length = lengthpolilinia;

\tvpolilinia.push_back(polilinia1);\t

\t//cout << vpolilinia.at(1).vlinia.size() << endl;
\t//cout << vpolilinia.size() << endl;

\t//recorrem totes les polilínies
\tfor (int i=0; i
I la sortida per pantalla:

$ ./polilinia
polilinia #0. length: 267.358
linia #0. length: 125.936
linia #1. length: 141.421
polilinia #1. length: 408.779
linia #0. length: 125.936
linia #1. length: 141.421
linia #2. length: 141.421

En el siguiente artículo [5] se explica la implementación del proyecto cncstroke para generar G-Code con z-depth variable.

Referencias:

Nota aclarativa: En el proyecto cncstroke utilizamos el término polilínea como sucesión de líneas enlazadas. Ahora bien, para generar estas líneas enlazadas, en LibreCAD, utilizamos la herramienta línea (y no la herramienta polilínea) (En una futura versión se estudiará la posibilidad de utilizar la herramienta polilínea, o ambas).

Juego del Simon. Extraescolar de Robótica en el Balmes

Ya hemos acabado la extraescolar de Robótica al Balmes. Este año hemos programado el juego del Simon con una Raspberry Pi. Toda la documentación (y la programación didáctica) está en la wiki [1].

Durante estas 13 sesiones hemos ido trabajando sobre diferentes temas, que nos llevaban al objetivo final de hacer un prototipo de Simon:

  • Hemos programado el script con Python, viajando por las diferentes versiones a medida que necesitábamos incorporar nuevas funcionalidades.
  • Hemos conectado botones y LEDs a los pines GPIO de la RPi.
  • Hemos visto cómo, utilizando transistores, podemos disparar desde los pines GPIO señales de 12V para encender tiras de LEDs blancos.
  • Hemos discutido sobre la alimentación de todo el sistema, y alternativas.
  • Hemos discutido sobre cómo producir el sonido y su amplificación.
  • Hemos diseñado el mueble final, utilizando herramientas de fabricación digital como la impresora 3D y la fresadora CNC.

Al final ha sido un curso muy multidisciplinar, hemos tocado muchas cosas, aunque por falta de tiempo, conocimientos e infraestructura, muchas cosas las ha implementado el professor y las ha mostrado en clase. Pero al final la experiencia ha sido enriquecedora para todos.

El resultado final se puede ver en la foto y en este video:

Referencias: