Arduino convertir PWM a analógico por un módico precio.

Antes de leer este post aviso, esta solución es barata pero muy mala, con una salida plagada de ruido. Para aplicaciones que necesiten mejor conversión es mejor adquirir un DAC que lo haga. Sin embargo para «cacharrear» es una buena solución.

Una de las pegas que encuentro a Arduino UNO es no tener una verdadera salida analógica, que permita devolver un voltaje entre 0v y 5v. Lo más parecido que tenemos es PWM que codifica el valor en forma de la anchura (duración) del pulso. Sin embargo es posible convertir esa señal a voltaje. Vamos a ver cómo conseguir una verdadera salida analógica, variación de voltaje, en Arduino UNO conectando un circuito RC (resistencia condensador) muy sencillo a la salida PWM. La parte teórica del asunto es que el circuito RC actúa como un filtro de paso bajo para filtrar la señal PWM y convertirla en un voltaje constante (más o menos). Usando mi simulador de filtros veamos gráficamente lo que ocurre cuando aplicamos a una señal cuadrada un filtro paso bajo con una frecuencia de corte unas 50 veces menor que la señal cuadrada (En el simulador elegimos como señal una onda cuadrada, filtro paso bajo, una frecuencia para el filtro de 0.0075 Hz y quitamos todas las fuentes de ruido el ruido). El resultado es una señal que tras un periodo tiende a ser estabilizarse alrededor de un valor:

Ese valor será proporcional al tamaño en anchura de la parte alta del pulso cuadrado. Por lo que podemos convertir una pulso PWM, que modula su valor como la anchura de la parte alta de la señal, en un voltaje.

El circuito tiene la siguiente forma: (ahora veremos de donde salen esos valores)

Filtro paso bajo RC

Como ya vimos, para calcular la frecuencia de un filtro paso bajo podemos usar la siguiente formula:

f = 1 / (2* Pi * R * C)

Siendo f la frecuencia, C la capacidad del condensador y R la resistencia.

Para nuestro caso f = 1 / (2 * Pi * 4400 * 10^-7) = 361.71 Hz

Pero hemos dicho que la frecuencia de corte ha de ser unas 50 veces menor que la funcionamiento…pero eso es en la teoría. Haciendo pruebas en la vida real va mejor con alrededor de 100 veces. Por lo que PWM debería funcionar a unos 36 kHz. El problema esta que PWM no trabaja a esa frecuencia….a no ser que la cambiemos como vimos en esta entrada Por lo que fijaremos su frecuencia en unos 32 kHz.

El código será el siguiente:

const byte PRESCALER2 = 0b001;
void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analogWrite(3, 255); //5v
  delay(5000);
  analogWrite(3, 204); //4v
  delay(5000);
  analogWrite(3, 153); //3v
  delay(5000);
  analogWrite(3, 102); //2v
  delay(5000);
  analogWrite(3, 51); //1v
  delay(5000);
  analogWrite(3, 0); //0v  
  delay(3000);
}

El resultado tiene bastante ruido y los valores resultantes no son muy exactos. Por lo que no se puede usar para casos donde se requiera gran precisión en el valor.

Para ajustar más los valores podemos usar una pequeña corrección para subir el valor del la señal PWM en los valores más bajos (son los que más se descuadran) y que se aproxime al valor de voltaje que debería tener:

void analog(byte value){
  value += (255 - value) >> 4; 
  analogWrite(3, value); 
}

El ejemplo anterior con esta pequeña corrección:

const byte PRESCALER2 = 0b001;
void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analog(255); //5v
  delay(5000);
  analog(204); //4v
  delay(5000);
  analog(153); //3v
  delay(5000);
  analog(102); //2v
  delay(5000);
  analog(51); //1v
  delay(5000);
  analog(0); //0v  
  delay(3000);
}

void analog(byte value){
  value += (255 - value) >> 4; 
  analogWrite(3, value); 
}

Modificar la frecuencia del PWM en Arduino.

Vamos a ver cómo modificar la frecuencia a la que trabaja el PWM de Arduino UNO. Para esto es necesario cambiar la frecuencia de uno de los tres timers que posee. Cada uno de ellos controla la frecuencia de dos pines PWM y cumple una función distinta:

TimerPines PWMRegistroFunciones
Timer 0D5, D6TCCR0Bmicros(), milis() y delay()
Timer 1D9, D10TCCR1BServo
Timer 2D3, D11TCCR2Btone()

En la columna «registro» se indica el registro cuyos tres últimos bits controlan el «prescaler» da cada timer. Sin entrar en detalle de como funciona, este prescaler nos permite ajustar la frecuencia de la señal PWM actuando como un divisor de la misma. En las siguientes tablas podemos ver los valores correspondientes. Recordar que los bits CS*2, CS*1, CS*0 corresponden a los tres bits de menor peso del registro de 8 bits.

Timer 0 – TCCR0B

CS02CS01CS00DivisorFrecuencia
000Parado
001162500 Hz
01087812.50 Hz
01164976.56 Hz
100256244.14 Hz
101102461.04 Hz

Timer 1 – TCCR1B

CS12CS11CS10DivisorFrecuencia
000Parado
001131372,55 Hz
01083921,16 Hz
01164490,20 Hz
100256122,55 Hz
101102430,64 Hz

Timer 2 – TCCR2B

CS22CS21CS20DivisorFrecuencia
000Parado
001131372,55 Hz
01083921,16 Hz
01132980,39 Hz
10064490,20 Hz
101128245,10 Hz
110256122,55 Hz
111102430,64 Hz

Ejemplo

En nuestro ejemplo nos vamos a centrar en los pines D3 y D11. ¿Por qué estos dos? Para evitar problemas innecesarios, ambos usan el timer 2 que va asociado únicamente a la función tone(). Además es el timer que más nos permite jugar con sus frecuencias.

El prescaler lo ajustaremos con la siguiente instrucción:

TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;

Donde PRESCALER2 indicara la configuración de los bits CS22, CS21 y CS20. Veamos el código del ejemplo:

const byte PRESCALER2 = 0b001;

void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | PRESCALER2;
  pinMode(3, OUTPUT);
}

void loop() {
  analogWrite(3, 128);
  delay(1000);
}

Si quieres profundizar más en este tema y saber más sobre Arduino puedes echar un vistazo a mi libro.

Si quiere ver el ejemplo funcionando puedes mirar este vídeo de mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

¿Puede DALL-E borrar las marcas de agua de las fotos?

Tras leer el titulo poca introducción necesita esta entrada. Tan solo necesito una foto con marca de agua, para evitarme problemas voy a usar una foto mía (tomada por mi, no de mi). Y añadirle una marca de agua poco sutil.

Foto de un burro con marca de agua

Ahora la subo a DALL-E y con mucho cuidado borro la marca de agua

Ahora pidamos a DALL-E la foto de un burro «photo of a donkey» y elijamos el mejor resultado (según mi punto de vista):

¿Y si le pedimos un caballo? «photo of a horse«

Pero un caballo y un burro están «próximos» y si le pido algo muy distinto como un coche «photo of a car«

Parece ser que el contexto de la foto pesa mucho y se impone a nuestros deseos. ¿Y si no nos complicamos la vida y pedimos simplemente una foto? «photo»

¡Correcto! DALL-E tiene suficiente información en la imagen como para completarla.

Aunque todas las imágenes parezcan iguales hay diferencias entre ellas.

Quizás este sistema es muy descarado, me gustaría quitar la marca de agua y evitarme una denuncia del autor. ¿Y si genero una variación a partir de la imagen con marca de agua?. Como siempre me quedo con el mejor resultado de los cuatro:

Es difícil que el autor original defienda que esta es su misma foto (el burro no esta ni en la misma pose). Ademas DALL-Eme ha movido la marca de agua a un sitio donde molesta menos, no pasa nada por que ya sabemos como eliminarla.

Definitivamente no es que DALL-E permita eliminar marcas de agua, es que permite generar imágenes nuevas a partir de fotos con marca de agua y que ni el propio autor seria capaz de reconocer.

Autómatas celulares y evolución

Vamos a usar los autómatas celulares para modelar una versión muy simple de evolución y luego jugar con ello.

Para simular la evolución necesitamos «algo» en cada individuo (celda) que haga la función de código genético que será sometido a un proceso de selección competitiva según lo adaptado que este al entorno. Los más adaptados tendran más oportunidades de replicarse, pero durante la misma se verán sometidos al cruce con el código genético de otro individuo y a mutaciones.

Veamos en detalle cada una de estas parte.

Genes, genotipo y fenotipo

Vamos a empezar a describir los genes de nuestro individuo. En nuestro caso cada gen es un único bit, por lo que puede tener 2 valores 0 y 1. Cada individuo tiene 8 genes por lo que entre todos serán un único byte. Hay 256 posibles combinaciones de genes (genotipos). Para representar el aspecto (fenotipo) de cada individuo usaremos el nivel de gris representado por el valor del byte que representa sus genes. Este color ira desde negro para el valor mínimo (0) a blanco para el valor máximo (255), pasando por distintos tonos de grises.

Función fitness

Es la función que devuelve un valor que puntúa lo bien adaptado que está un individuo al entorno. Tenemos todo un artículo dedicado a ella.

En este caso tendremos dos funciones fitness para poder jugar con ellas. Para ambas tomaremos el valor del bit representado por los genes. Por ejemplo, el genotipo 00000010 valdrá 2 y el genotipo 00000110 valdrá 6. Esto hace que no todos los genes tengan la misma importancia. También jugaremos con eso.

Para lo que no tengan claro como funcionan los numeros binarios pueden mirar la siguiente tabla para saber cuanto vale cada gen (el valor total del genotipo seria la suma de los valores de todos los bits que estén a 1)

Nº de bit76543210
Valor1286432168421

Hemos dicho que habrá dos funciones fitness una considera como más aptos a los individuos con mayor valor en sus genes. La otra a los individuos con menor valor. Ya veremos cómo jugamos con esto.

En la interfaz está la opción de cambiar la función fitness. Hay tres posibilidades: higth, que da preferencia a los bits de mayor valor; los, que da preferencia a los de menor valor y two, que usa ambas funciones fitness

Selección

Para que haya evolución necesitamos un proceso de selección, en cada iteración cada uno de nuestros individuos elegirá a uno de sus vecinos para cruzarse con él. La selección se hará con un mecanismo de ruleta, es decir, al azar, pero cada vecino tiene diferente probabilidad de salir elegido según el resultado de la función fitness. Los más adaptados son más probables.

Cruce

Una vez seleccionado el otro progenitor, se realiza el cruce de sus genes. El proceso consiste en tomar los 4 bits superiores del individuo, los 4 bits inferiores del vecino y con unirlos para formar el genotipo de descendiente. Esto creará un individuo con un código genético nuevo, mezcla de ambos individuos, que ocupará el lugar de su progenitor.

Mutación

Añadiremos mutaciones, cada cruce tiene cierta probabilidad de sufrir una mutación en uno de sus genes. Al producirse una mutación se cambia el valor de ese gen. Si es 0 pasa a ser 1 y viceversa.

La interfaz permite elegir la probabilidad de que está mutación ocurra en cada cruce. Se puede ajustar su valor entre 0 y 5% aunque lo recomendable es en 0,01% y 0,05%.

Creando el mundo

Cada uno de los individuos del mundo se crean con un valor entre 0 y 127. Esto se hace así para que haya un gen, el de mayor peso (gen 7), que este a 0 en todos ellos. La idea es que ese gen solo se puede poner a 1 a través de una mutación. Visualmente significa que solo puede haber cuadrados entre negro y gris claro. Para que haya cuadrados blancos tiene que producirse una mutación.

Jugando con todo esto

Empecemos por un punto importante, hemos dicho que, al principio, ningún individuo tendrá el gen 7 a 1. De tal manera que si tenemos individuos blancos será gracias a una mutación. Así que si iniciamos el autómata como mutaciones a 0 y fitness a «Hight» veremos que no es capaz de alcanzarlo, si añadimos unas pocas mutaciones lo alcanza sin problemas.

Gen de mayor peso a 0: Con mutaciones – Sin mutaciones

Ahora teniendo todo en blanco y con mutaciones vamos a cambiar la función fitness a «Low» vemos que cambia a negro, los individuos han sido capaces de adaptarse al entorno. Probemos los mismo, partiendo del estado con la mayoría de los individuos blancos, pero con las mutaciones a 0, el resultado es que nada cambia. Sin mutaciones y una población altamente especializada no hay variedad genética como par adaptarse al entorno.

De Hight a Low: Sin mutaciones – Con mutaciones

Veamos otro caso distinto probemos fitness a «Two» que indica que hay una función fitness distinta para cada zona del tablero. Al ejecutarlo veremos un fenómeno que se llama «especiación» en el cual a partir de una especie se crean dos separadas genéticamente.

Especiación

Ya hemos dicho antes que la incapacidad de adaptarse a un entorno es por la falta de variedad genética, al estar en un entorno dominado por muy poca variedad de genes y suprimir la mutaciones como fuente de variación la especie queda atrapada y no puede adaptarse. Pero en el caso anterior tenemos el tablero dividido en dos, hay variedad, aunque suprimamos las mutaciones debería de poder adaptarse y así es. Es capaz de adaptarse a cualquier cambio en el entorno, pero solo una vez.

¿Qué pasa si nos vamos al lado contrario aumentamos mucho las mutaciones?. Que vemos que los ganes varían tanto y tan rápido que es imposible mantener la población estable. Pensad que el simulador solo deja poner una tasa de mutación del 5% (1 de cada 20 individuos tiene una mutación) que parece una tasa baja y aun así se puede ver visualmente como afecta e impide que la población se adapte completamente al entorno.

Podéis probar vosotros mismos aquí y si queréis echar un vistazo al código fuente podéis hacerlo aqui.

Puedes ver un vídeo con más explicaciones en mi canal de Youtube:

Haz click para ver mi vídeo en Youtube

¿Puede GPT-3 dibujar?

Vamos a usar GPT-3 para dibujar, o intentarlo. Y no hablo de usar DALL-E, si no de usar el propio GPT-3 para que genere el dibujo, para que esto sea posible el dibujo debería de cumplir las siguientes condiciones:

  • Tiene que estar realizado con texto. GPT-3 solo genera texto.
  • Tiene que poderse incluir una descripción del mismo. Para poder pasarla a GPT-3 como texto a completar

SVG

SVG es un lenguaje de descripción de gráficos vectoriales. Permite describir dibujos usando un lenguaje basado en XML. Es decir, un lenguaje de etiquetas. Como SVG puede ir directamente en el código HTML vamos a pasar a DALL-E el principio de una web con una etiqueta abierta de SVG (a la que podemos añadir el ancho y el alto fijando las dimensiones del dibujo). Para describir el dibujo que queremos, justo antes de esa etiqueta pondrrmos un comentario HTML (<!– –>) describiendo el dibujo.

Por ejemplo le pedimos una estrella

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a star -->
<svg height="210" width="500"

Le damos a autocompletar

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a star -->
<svg height="210" width="500"
 xmlns="http://www.w3.org/2000/svg">
<!-- Define five points. -->
<polygon points="100,10 40,198 190,78 10,78 160,198"
style="fill:lime;stroke:purple;stroke-width:5;fill-rule:nonzero;" />
<!-- Define the top right point. -->
<circle cx="100" cy="10" r="4" fill="red" />
<!-- Define the left middle point. -->
<circle cx="40" cy="198" r="4" fill="red" />
<!-- Define the bottom point. -->
<circle cx="190" cy="78" r="4" fill="red" />
<!-- Define the top left point. -->
<circle cx="10" cy="78" r="4" fill="red" />
<!-- Define the right middle point. -->
<circle cx="160" cy="198" r="4" fill="red" />
</svg>
</body>
</html>

¡Correcto!

Ahora probaremos a pedir un coche

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a car-->
<svg height="210" width="500" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  
  <rect x="1" y="1" width="498" height="208" fill="url(#grad1)" />
  <text x="280" y="150" font-family="Verdana" font-size="35">My car</text>
</svg>
</body>
</html>

No ha salido bien

Demosle otra oportunidad

<!DOCTYPE html>
<html>
<body>
<!-- svg code to draw a car-->
<svg height="210" width="500" xmlns="http://www.w3.org/2000/svg">
  <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="200" y="10" width="100" height="30" style="fill:rgb(0,255,0);stroke-width:3;stroke:rgb(0,0,0)" />
   <circle cx="100" cy="100" r="80" fill="green" />
  <rect x="20" y="20" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="20" y="80" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="40" y="20" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  <rect x="40" y="80" width="10" height="10" style="fill:rgb(255,0,0);stroke-width:3;stroke:rgb(0,0,0)" />
  Sorry, your browser does not support inline SVG.
</svg>

</body>
</html>

Al menos parece que intenta dibujar un coche.

Ascii art

Lo primero que se me ocurrió es Ascii Art, un tipo de dibujo realizado con caracteres de texto, la idea era buena pero el resultado fue….curioso, a diversa peticiones:

A dog in ascii art
A bunny in ascii art

Respondió con el siguiente dibujo

                 ,#####,
                 #_   _#
                 |a` `a|
                 |  u  |
                 \  =  /
                 |\___/|
        ___ ____/:     :\____ ___
      .'   `.-===-\   /-===-.`   '.
     /      .-"""""-.-"""""-.      \
    /'             =:=             '\
  .'  ' .:    o   -=:=-   o    :. '  `.
  (.'   /'. '-.....-'-.....-' .'\   '.)
  /' ._/   ".     --:--     ."   \_. '\
 |  .'|      ".  ---:---  ."      |'.  |
 |  : |       |  ---:---  |       | :  |
  \ : |       |_____._____|       | : /
  /   (       |----|------|       )   \
 /... .|      |    |      |      |. ...\
|::::/'' jgs /     |       \     ''\::::|
'""""       /'    .L_      `\       """"'
           /'-.,__/` `\__..-'\
          ;      /     \      ;
          :     /       \     |
          |    /         \.   |
          |`../           |  ,/
          ( _ )           |  _)
          |   |           |   |
          |___|           \___|
          :===|            |==|
           \  /            |__|
           /\/\           /"""`8.__
           |oo|           \__.//___)
           |==|
           \__/

Como poco inquitante.

A otras muchas peticiones respondió con cosas sin sentido o espacios en blanco

Emojis

Este era fácil y supuse que lo haría bien, usar emoticonos para representar ideas.

JS Canvas

Otra forma de dibujar es usando un canvas en una web y usar código JS para dibujar en el lo que quieras. la idea es parecida a SVG, pero con código JS.

Primero preparamos la web;

<!DOCTYPE html>
<html>
<body>
  <canvas id="star" width="600" height="600"></canvas>
  </body>
<script>
 //Aquí ira el código generado por JS
</script>

La completaremos con el código que genere GPT-3

En lugar de pasarle la cabecera de la web le pasamos solo un comentario:

/* js code to draw a star in a canvas */

Le pedimos una estrella:

<!DOCTYPE html>
<html>
<body>
  <canvas id="star" width="600" height="600"></canvas>
  </body>
<script>
  /* js code to draw a star in a canvas */
// Define the points of the star.
  var points = 5;
  var innerRadius = 50;
  var outerRadius = 100;
  var angle = Math.PI / points;
  var x, y;
 
// Get the context of the canvas.
  var canvas = document.getElementById('star');
  var ctx = canvas.getContext('2d');
 
// Begin the path.
  ctx.beginPath();
 
// Move to the first point.
  x = canvas.width / 2 + Math.cos(0) * outerRadius;
  y = canvas.height / 2 + Math.sin(0) * outerRadius;
  ctx.moveTo(x, y);
 
// For each point draw a line to it.
  for (i = 1; i <= points * 2; i++) {
    x = canvas.width / 2 + Math.cos(angle * i) * (i % 2 === 0 ? innerRadius : outerRadius);
    y = canvas.height / 2 + Math.sin(angle * i) * (i % 2 === 0 ? innerRadius : outerRadius);
    ctx.lineTo(x, y);
  }
 
// Close the path.
  ctx.closePath();
 
// Fill the path.
  ctx.fillStyle = '#000';
  ctx.fill();
</script>

¡Casi! Y es fácil corregir el problema (basta con comentar el calculo del primer punto). Ahora, y esto le da nota extra, el método sirve para estrellas de cualquier numero de puntas, basta con cambiar la variable «points».

Ahora le pedimos un coche.

<!DOCTYPE html>
<html>
<body>
  <canvas id="canvas" width="600" height="600"></canvas>
  </body>
<script>
  /* js code to draw a car in a canvas */
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// draw car body
context.beginPath();
context.moveTo(75, 25);
context.quadraticCurveTo(25, 25, 25, 62.5);
context.quadraticCurveTo(25, 100, 50, 100);
context.quadraticCurveTo(50, 120, 30, 125);
context.quadraticCurveTo(60, 120, 65, 100);
context.quadraticCurveTo(125, 100, 125, 62.5);
context.quadraticCurveTo(125, 25, 75, 25);
context.stroke();
// draw car wheels
context.beginPath();
context.arc(75, 100, 10, 0, 2 * Math.PI);
context.fillStyle = "black";
context.fill();
context.lineWidth = 1;
context.strokeStyle = "black";
context.stroke();
context.closePath();
context.beginPath();
context.arc(125, 100, 10, 0, 2 * Math.PI);
context.fill();
context.lineWidth = 1;
context.stroke();
context.closePath();
</script>

La idea está ahí pero no es lo que queríamos

Vamos a darle más detalles a ver… «Un coche deportivo rojo»

<!DOCTYPE html>
<html>
<body>
  <canvas id="canvas" width="1600" height="1600"></canvas>
  </body>
<script>
/* js code to draw a red car in a canvas with id "canvas" */
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// draw a rectangle for the car body
ctx.fillStyle = "red";
ctx.fillRect(30, 30, 150, 50);
// draw a triangle for the car roof
ctx.beginPath();
ctx.moveTo(40, 30);
ctx.lineTo(140, 30);
ctx.lineTo(90, 10);
ctx.fill();
// draw two circles for the car wheels
ctx.beginPath();
var centerX = 50;
var centerY = 75;
var radius = 20;
// draw left wheel
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// move center coordinates to right wheel
centerX += 100;
// draw right wheel
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// color the wheels
ctx.fillStyle = "black";
ctx.fill();
// reset the current path
ctx.beginPath();
// draw a line in the middle of the car to separate the two colors
ctx.moveTo(50, 30);
ctx.lineTo(50, 80);
ctx.stroke();
// stroke the current path with a thick line
ctx.lineWidth = 3;
ctx.stroke();
</script>

No es lo que tenía en mente, pero bueno un coche es, no sé si deportivo, y rojo también.

Conclusiones

Con dibujos muy sencillos e insistiendo a base de prueba y error se puede conseguir algo. Menos en Ascii Art que es una causa pérdida. No es un sistema cómodo, ni práctico. Eso no quita lo sorprendente que es que entienda lo que le pides y trate de dibujarlo, aunque sea con resultados irregulares.

Es posible que con un entrenamiento adecuado se puedan mejorar estos resultados. Hay que tener en cuenta que esta forma de dibuja obliga a entender como se relacionan los distintos elementos del dibujo entre ellos. no solo es dibuja la ruedas con dos círculos es colocarlas correctamente respecto al resto del dibujo.

Puedes ver un vídeo sobre el mismo tema en mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Stable Diffusion se enfrenta a un examen de dibujo.

Ya hemos visto a DALL-E enfrentarse a varios retos. Hoy enfrentamos a otro modelo, está vez código libre, Stable Diffusion. Le he pedido a un profesor de dibujo que le ponga un par de ejercicios y valoremos el resultado.

Ejercicio 1: Mándala

El enunciado del ejercicio es:

Crea un zendala. Elige una gama cromática de colores fríos o cálidos y crea una armonía cromática con colores afines. Usa un mínimo de dos colores y un máximo de tres.

Cambiamos «crear» por «dibuja» y «zendala» por «mandala», palabra mucho más conocida. Lo traducimos al inglés:

Draw a mandala. Choose a chromatic range of cold or warm colors and create a chromatic harmony with similar colors. Use a minimum of two colors and a maximum of three.

Y el resultado es:

Varios puntos a tener en cuenta:

  • Simula ser un dibujo a mano, coloreado y es un mandala. Lo damos por válido.
  • Escala cromática. Falla, no es lo que le hemos pedido, lo intenta pero no sabe que hacer. Incorrecto.

Ejercicio 2: Camaleón puntillista

El siguiente ejercicio tiene dos partes  (iba a ser solo una pero el profesor sugirió un cambio)

Dibuja un camaleón con puntillismo en una jungla

Nos encontramos con la duda de ¿Cuál es la traducción de puntillismo? Google sugirió «pointillism» no estábamos seguros pero continuamos. El texto quedó:

draw a chameleon with pointillism in a jungle
  • Lo esperábamos en blanco y negro, pero no sé lo especificamos. El profesor da por bueno el resultado.
  • Solo es puntillista el camaleón, no la jungla. Puede ser culpa nuestra por no indicarlo ya que puntillismo hace referencia al camaleón
  • Falta un «trozo» de camaleón. Algo habitual en este tipo de IA, no se les da muy bien encuadrar la imagen

La pega es que el profesor esperaba un dibujo en blanco y negro, por lo que había que especificarlo en el prompt. Además pidió otro cambio. No se fiaba mucho de que el algoritmo no estuviera «copiando» imágenes. Por lo que en lugar de en la jungla, algo típico para un camaleón, pidió algo diferente, una escalera.

Cómo cuando usamos «stairs» dibujo un camaleón con un lápiz en la mano ¿? Usamos la palabra «ladder».

Dibuja un camaleón con puntillismo bajando una escalera. Blanco y negro
draw a chameleon with pointillism going down a ladder. Black and white
  • El profesor le da el visto bueno y lo considera como superado
  • Yo señalo que no esta «bajando la escalera». Pero no parece tener importancia

Resultado:

No es fácil dar una nota numérica ya que por un lado los dibujos son espectaculares, por otro comete errores muy simples como el usar más de tres colores en el mandala o cortar al figura del camaleón. El resultado seria claramente más que aprobado aunque no llegaría a sobresaliente.

Puedes ver la explicación en vídeo en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

DALL-E vs Diseñador Gráfico, crear un logo

Vamos a enfrentar a DALL-E contra un diseñador profesional a ver quién diseña mejor logo para mi blog. Haremos dos enfrentamientos:

Primero pasaremos a DALL-E una descripción de la temática de la web y un poco de información sobre la misma. La idea es que DALL-E tenga, más o menos, la misma información que el diseñador. No se le va pasar ninguna descripción del logo deseado. Queremos ver la «creatividad» de DALL-E. Sobre los logos que más me gusten le pediré que genere variaciones.

En el segundo le pasaremos el logo generado por el diseñador y le pediremos que genere variaciones, a ver si puede mejorarlo.

Antes de empezar la confrontación hay que dejar claro que a DALL-E no se le van a valorar los textos, ya que tiene problemas para generar textos con sentido. Si que valoraremos el diseño de los mismos.

El logo con el que competir es el de este blog:

Generando un logo desde cero

Vamos a pasar a DALL-E una descripción del blog y su temática. También quién es Chispas, información que le sirvió al diseñador para crear el logo.

Un logo para una web sobre inteligencia artificial, electrónica y programación. El nombre de la web es "Chispas". "Chispas" es una inteligencia artificial.

En inglés:

A logo for a website about artificial intelligence, electronics and programming. The name of the website is "Chispas". "Chispas" is an artificial intelligence

Dejo Chispas sin traducir para evitar que influya añadiendo chispas electricas al logo.

No es el resultado esperado, quizás sirvan para portada de un disco indy, pero no valen como logotipo. Voy a probar el «menos es más» y a simplificar la prompt.

A logo for a website about artificial intelligence, electronics and programming.

Veamos los resultados:

Como me parecían muy «típicos» decidí darle una segunda oportunidad:

Los dos primeros casos de esta segunda prueba se parece más a lo que me paso el diseñador gráfico para comprobar como queda el logo con distintos fondos:

Aunque lo lógico sería elegir la imagen 1 y 2 para generar variaciones, no me convence mucho la imagen 1 con ese cuadrado queriendo ser la letra a, así que voy a elegir las imágenes 2 y 4 y pedirle a DALL-E que me genere variaciones a ver cuál es el mejor resultado de cada. De la 2 me gusta que parece una mezcla entre el logo de ahora (coincidencia) y algo orgánico. De la 4 la idea de mezclar medio cerebro «orgánico» con elementos claramente artificiales.

De la variaciones de la imagen 2 me quedo con esta:

Me gusta el estilo orgánico de estos diseños, me gusta tanto que me los guardo para usarlos. Un punto en contra es que en esta variación no ha entendido que la imagen debería ser la misma en cada cuadrante. Debo decir que son demasiado pequeños para usarlos como logo del blog.

De la variaciones de la imagen 4 he elegido esta:

En este caso me gustaría una mezcla del logo original (imagen 4) con este. Me gusta que este cerebro luzca más orgánico pese a mantener las lineas rectas. Prefiero los hexágonos a los círculos. Si tuviera que quedarme con una de las dos me quedaría con la original.

Humano: 1 DALL-E: 0.5 (no se merece un 0)

Variaciones del logo de un diseñador

En este caso partimos del logo ya creado por un humano y le pedimos modificaciones para ver qué ideas sugiere.

Debo reconocer que algunas de estas variaciones me gustan. No las veo como para sustituir la imagen original. Pero si como para tener variaciones para algunas secciones.

Humano: 1 DALL-E: 0.5 (no se merece un 0)

Conclusiones

DALL-E es capaz de crear logos, puede que no sean los mejores logos, pero para un apaño sirven. El mayor problema es que solo genera imágenes de 1024 x 1024, lo cual puede ser pequeño para usos profesionales. Los resultados no son llegar y usar, es necesaria la intervención humana para «retocar» el resultado. Creo que DALL-E puede ser más útil como fuente de inspiración, tanto para el cliente como para el diseñador, para crear una idea base de la partir que como herramienta de diseño completa.

Es importante señalar que diseñar un logo de forma profesional es más complicado que lo que hemos visto aquí. No hay tanta libertad ya que el cliente puede imponer restricciones, hay códigos de estilo que seguir, por ejemplo colores que definen la marca o la web. Ademas de la incapacidad de generar imágenes vectoriales que limita mucho su uso. Es cierto que hay herramientas que pueden convertir una imagen en vectorial y resolver este punto y el del tamaño.

Puedes ver un vídeo sobre este articulo en mi canal de Youtube:

Haz click para ver el vídeo en Youtube

¿Puede DALL-E dibujar un cómic?

Tras un intento fallido de generar sprites de un videojuego (aunque realmente lo interesante era hacer uncrop de la imagen). Vamos a intentar generar un cómic para ver si con DALL-E se puede narrar una historia en varias imágenes de forma secuencial. Me preocupa especialmente la capacidad de DALL-E para mantener la consistencia entre las distintas viñetas. Y ya se que no le puedo pedir que me ponga el texto. Por lo tanto el objetivo se reduce a una historia muda o a una que le añadiremos el texto después.

Primer intento

El más sencillo para mi. Escribo el guión, se lo paso. Me genera el comic y a partir de ahí solo es cuestión de tiempo hacerme una artista famoso y millonario.

El guion es el siguiente:

Una tira cómica de 4 paneles estilo Calvin Y Hobbes. La tira cómica es protagonizada por Kelvin, un niño, y el gato Fred. 
Panel 1: Kelvin baja del autobús escolar y se dirige a la casa de su madre. Es invierno, el suelo está nevado, Kelvin lleva un abrigo y un gorro en la cabeza.
Panel 2: Fred le espera detrás tumbado en la entrada detrás de la puerta de la casa. El suelo es de láminas de madera, las paredes blancas y vemos un marco de fotos en una de las paredes.
Panel 3: Kelvin abre la puerta de la casa.
Panel 4: Fred salta sobre Kelvin y lo tira al suelo.

Ahora solo queda traducirlo, introducir algunos cambios pensando en que DALL-E lo entienda mejor y ver el resultado.

Primer problema, el prompt de DALL-E solo permite 400 caracteres. Así que hay que hacer una «poda» del texto original, al reducir el número de caracteres reducimos la descripción de las viñetas que pasamos a DALL-E. El resultado es el siguiente:

A 4 panels page with a comic strip in the style of Calvin and Hobbes. The characters are Kelvin, a kid, and Fred, a cat.
Panel 1: Kelvin gets off the school bus. It is winter, the ground is snowy, Kelvin is wearing a coat and a wool cap.
Panel 2: Fred is lying in the entrance behind the door of the house.
Panel 3: Kelvin, from outside, opens the door of the house.
Panel 4: Fred jumps on Kelvin.

Tras realizar varios intentos me encuentro con los mismos problemas, vamos a ver unos pocos ejemplos:

El resultado se «parece» a lo que quiero conseguir….pero no es exactamente lo que quiero conseguir. Los problemas:

  • No tiene claro cual es el estilo que quiero.
  • No siempre me genera 4 viñetas y cuando lo hace las dibuja cortadas
  • No hace cuatro viñetas distintas, mas bien mezcla una o dos de las descripciones y las representa en las viñetas.
  • Pone cosas que parecen diálogos, no es un gran problema porque suele ponerlos donde es fácil borrarlos.
  • ¿Donde esta el gato? Apenas aparece.

Explincadole a DALL-E lo que son las viñetas

Primero vamos a por el tema de las viñetas. Vamos a indicarle a DALL-E que no queremos que se salga de una estructura de pagina ¿Cómo? Con una plantilla como esta:

Subiremos esta plantilla, marcaremos como «borrar» las zonas grises y DALL-E solo podrá dibujar dentro de ellas.

Como no reconoce el estilo de dibujo que le pido le pongo uno que si que conoce: render 3D

También le facilito la vida eliminado los nombres de Kelvin y Fred, poniendo directamente niño (kid) y gato (cat)

A comic strip with 4 panels. Render 3D 
Panel 1: A kid wearing a coat and a wool cap gets off the school bus. It is winter, the ground is snowy.
Panel 2: A cat is lying in the entrance behind the door of the house.
Panel 3: kid, from outside, opens the door of the house.
Panel 4: cat jumps on kid

Veamos algunos de los resultados:

Tiene mejor pinta pero las viñetas siguen siendo variaciones de una misma sentencia. Otro problema que se ha resuelto al decirle que trate de hacer algo con estilo render 3D , en lugar de un comic es que no pone «pseudopalabras» sueltas por el texto.

¿Entiende DALL-E que cada sentencia es una viñeta?

Vamos a probar las capacidades de compresión de DALL-E. Para ello le voy a pedir que me ponga una figura geométrica sencilla en cada viñeta. Se lo voy a pedir de dos formas, con números (1,2,3,4) y con ordinales (primero, segundo, tercero, cuatro).

A comic strip with 4 panels. Render 3D. 
Panel 1: a circle.
Panel 2: a triangle.
Panel 3: a square.
Panel 4: a hexagon.
A comic strip with 4 panels. Render 3D. 
First Panel: a circle.
Second Panel: a triangle.
Third Panel: a square.
Fourth Panel: a hexagon.

Selecciono un par de casos como ejemplo:

Esta claro que DALL-E no tiene una idea clara de lo que le pido.

Dibujo viñeta a viñeta

Siguiente prueba, si es incapaz de entender las cuatro viñetas a la vez, que pasa si se la explico de una en una. Empiezo por la plantilla que he creado antes, marco la primera viñeta como «borrar» y le paso la descripción de esa viñeta. Elijo el mejor resultado y lo uso para crear la segunda viñeta, elijo el mejor resultado y paso a la tercera y luego a la cuarta.

Me preocupa si mantendrá la coherencia entre viñetas o cada una será con un estilo.

Resumo el proceso poniendo cada una de las cuatro sentencias usadas:

[1] A kid gets off the school bus. It is winter, the ground is snowy, Kelvin is wearing a coat and a wool cap. 3D render
[2] A cat is lying in the entrance behind the door of the house. 3D render
[3] kid, from outside, opens the door of the house.
[4] when the kid open the door a cat jumps on kid

El resultado de este proceso:

No creo que le den un premio Eisner. Pero funciona y mantiene cierta coherencia entre viñetas.

Hay varias cosas que podría mejorar:

  • Dedicar más tiempo a los prompts que en este ejemplo son muy poco detallados
  • Elegir un estilo mejor que render 3D
  • Se pueden introducir correcciones a mano en cada viñeta antes de generar las siguiente
  • Generar mayor número de pruebas, yo solo he generado 3 casos y elegido el mejor.
  • Marcar mejor los límites de la viñeta para evitar esa niebla gris que rodea cada viñeta (y que viene de los restos de color gris que no he marcado completamente para borrar)

Conclusiones

Por lo tanto ¿Es capaz de dibujar un comic?. Si, es capaz, pero no será un gran comic. Hay que indicarle la estructura de las viñetas a mano y cuidar mucho los prompts. Por otro lado se puede llegar a narrar una historia lo suficientemente bien como para entenderla.

Otro punto son los diálogos, a DALL-E no se le dan bien las palabras y hay que añadirlos a parte.

Puedes ver un vídeo sobre este tema en mi canal de Youtube:

Haz click para ver el vídeo en mi canal de Youtube

Cómo hacer «uncrop» de una imagen con DALL-E

Uncrop, palabra que tiene un difícil traducción al español, seria algo así como «desrecortar», lo que hace es, a partir de una imagen, extender esta hacia donde le indiquemos completando la «parte que falta» de la misma.

Veamos como hacerlo usando DALL-E

Para empezar a probarlo necesitaremos una imagen o dibujo para extenderlo. En este caso vamos a usar una nave espacial estilo pixel art. Se la pediremos a DALL-E:

pixel art spaceship for videogame

Nave generada por DALL-E

El resultado es una imagen de 1024×1024, ahora queremos que DALL-E use esta imagen como base para crear sprites para videojuegos. Para ellos, partiendo de nuestra nave, la reducimos a un 25% de su tamaño y la ponemos sobre un fondo negro de 1024×1024, realmente el color da igual, por comodidad es mejor que sea un color oscuro ya que la herramienta que tiene DALL-E para editar imágenes pinta de blanco las zonas a modificar.

La zona negra se marcará para borrar

Como puede verse hemos colocado la nave en la esquina superior izquierda, podemos colocarla donde queramos, sabiendo que la zona negra es la que se va a completar.

Subimos la imagen a DALL-E, marcarmos la zona negra como «para borrar» e introducimos la descripción de la nueva imagen que queremos obtener, en nuestro caso:

sprites of a spaceship for a video game: with engines on, with engines off, firing, with force field, damaged

El resultado:

No era lo deseado pero aun así se aproxima a la idea

Por una vez voy a poner un resultado que deja a DALL-E mal. Ha hecho algo que se parece a lo que le pedimos, pero no lo que le pedimos exactamente y es que el abuso de artículos, vídeos y demás donde se ven resultados increíbles (seleccionados, obviamente) no transmiten la realidad que es un punto intermedio entre lo sublime y lo fallido. Hace cosas increíbles, pero también a veces falla en otras mucho más simples y eso no quiere decir que no haya manera de usarlo para lo que le he pedido. Seguramente exista, quizás usando esta técnica para generar cada uno de los sprites uno a uno. O adaptando el texto introducido, usando prueba y error. Pero ya no es tan intuitivo como «pedirle lo que quieres».

Puedes ver otro uncrop en el siguiente vídeo de mi canal de Youtube:

Haz click para ver el vídeo en Youtube

Usar Serial Plotter con Arduino

Todos los que usamos el editor de Arduino estamos acostumbrados al Monitor Serie o Serial Monitor para ver los datos que se envían por el puerto serie del Arduino. Pero junto a él, en el menú «Tools», hay otra herramientas que permite ver estos datos forma visual el Serial Plotter que dibuja una gráfica (o varias) con los datos que le devuelva el puerto serie.

Su uso es muy sencillo los datos tiene que seguir ir codificados de la siguiente manera:

  • En cada linea van los datos separados por coma y espacio «, «
  • Cada nueva linea va separada de la anterior por un salto de linea

La forma mas sencilla de entenderlo es con este código:

Serial.print(var1);
Serial.print(", ");
Serial.print(var2);
Serial.println();

Si bien no es una herramienta muy avanzada y tiene bastantes limitaciones sirve para visualizar datos visuales de una forma sencilla. Solo puede representar un tipo de gráfica, el tiempo en el eje horizontal y el valor de la variable en el eje vertical. Con este tipo de gráficas tienes el problema de que algunos datos son muy rápidos y serial plotter no permite almacenar la gráfica para revisarla.

Veamos un ejemplo donde se muestran varias gráficas simultáneamente:

float t = 0;
float oldDataT = 0;
float f = 1; //frecuencia
unsigned long oldMillis = 0;
unsigned long nowMillis = 0;

void setup() {
  Serial.begin(9600); //iniciamos el Serial para mostrar la gráfica
  while (!Serial) {}
}

void loop() {
  nowMillis = millis();
  double dt = double(nowMillis - oldMillis)/1000;
  
  t += dt;
  double tf = t*f;
  double sawtoothW = tf;
  double sinW = sin(tf*2*PI);
  double posSinW = sin(tf*PI);
  double squareW = (sin(tf*2*PI) > 0) ? 1: 0;
  double triangleW = (sin(tf*2*PI) > 0) ? 2*tf: 2-(2*tf);

  if(t - oldDataT > 0.05){ //si no lo datos van demasiado rápidos 
    oldDataT = t;
    Serial.print(sawtoothW);
    Serial.print(", ");
    Serial.print(sinW);
    Serial.print(", ");
    Serial.print(posSinW);
    Serial.print(", ");
    Serial.print(squareW);
    Serial.print(", ");
    Serial.print(triangleW);
    Serial.println();
  }

  oldMillis = nowMillis;    

  if(t >= 1/f){ //cuando alcanza un periodo se reinicia
    t = 0;
    oldDataT = 0;
  }
}

El resultado es el siguiente:

Gráficas en serial plotter

En la imagen se pueden ver gráficas de varias formas generadas desde Arduino.

Un ventaja que tiene este sistema es que los datos son compatibles con el formato CVS, habitual cuando se trabaja con datos.

Añadir guías a las gráficas

Hay algún truco que podemos usar para mejorar la visualización de las gráficas. Establecer guías, una guía no es nada más que una línea recta paralela al movimiento del gráfico y que tiene siempre un valor fijo. Se puede establecer una guía poniendo un valor fijo en los datos que se devuelven. Se usan como referencia visual. Si conoces los máximos y mínimos del valor puedes establecer otras dos guías una en cada valor y así evitar que la gráfica vaya dando «saltos» como a veces ocurre cuando varia mucho el valor.

Puedes ver el vídeo sobre este tema en mi canal de Youtube:

Haz click para ver el vídeo en Youtube