Hill Climbing

Tanto imaginarnos que el espacio de soluciones es un pasaje con colinas y depresiones que antes o después el nombre alguna metaheurística tenía que hacer referencia a subir colinas. Su desarrollo es sencillo. Situado en un punto, revisa todos sus vecinos o, si son demasiados, una muestra aleatoria de ellos. Tras ello se mueve al vecino cuyo valor es mayor que el actual, allí repite el proceso. El algoritmo continua hasta que no se pueda encontrar un vecino mejor o se haya realizado un número de iteraciones prefijado.

Como optimizador local funciona bastante bien por lo que muchas veces se usa para «mejorar» el resultado de un optimizador global. Existe una versión para optimización global que consiste en lanzarlo varias veces desde puntos de origen distintos, a esta versión se le denomina «con reinicio» ya que una vez que deja de mejorar o se alcanza un número determinado de iteraciones se reinicia el algoritmo desde un nuevo origen.

Su funcionamiento es muy parecido a de random search, con la diferencia de que consulta todos (o al menos una muestra) sus vecinos para seleccionar el mejor. Hay otra forma de implementarlo. En lugar de visitar N vecinos se van comprobando vecinos de uno en uno hasta encontrar uno que mejore el resultado actual en por lo menos un valor umbral fijado, todos cuya mejora sea inferior a ese umbral son descartados. Si ese umbral es muy pequeño el comportamiento es similar al de random search.

Para que el código no sea muy parecido al de random search vamos a incluir una mejora. En lugar de mirar solo el fitness de los vecinos para compararlos vamos a usar la pendiente que sería igual a la variación del fitness dividido por la distancia a entre las coordenadas de la solución actual y el vecino que se compara. Es decir vamos a elegir el camino que más rápido sube. Por lo tanto el nuevo valor que usaremos para comparar vecinos es:

fdist(Ni) = f(Ni) – f(S) / dist(Ni, S)

Siendo Ni el vecino que se evalua y S el punto donde se encuentra ahora la búsqueda. Ojo y no confundir está pendiente calculada grosso modo con la pendiente de una función en un punto.

var newCords = this.newSolution(this.cords.slice(0));
var newFitness = this.model.calculateFitness(newCords);
var newVarition = (auxFitness-this.fitness)/this.distance(newCords, this.cords); //dif_fitness/distance

También se incluye un algoritmo para la variación del tamaño del paso de búsqueda en caso de que no se encuentre una solución mejor.

step = step*increaseStep

Para evitar que el tamaños del paso se dispare a valores absurdos se permite fijar un limite de veces que se multiplicara por el incremento. Cada vez que se encuentre una buena solución se volverá al tamaño de paso original.

if(newFitness > this.fitness){
  this.cords = newCords;
  this.fitness = newFitness;
  this.step = this.parameters.step;
  this.numberIncreaseSteps = 0;
} else {
  this.numberIncreaseSteps++;
  if(this.numberIncreaseSteps < this.parameters.maxTimesIncreaseStep){
    for(var i = 0; i < this.parameters.dimensions; ++i){
      this.step[i] *= this.parameters.increaseStep[i];
    }
  }
}

 

El código se puede encontrar en github.

Resolviendo problemas con heurísticas y metaheurísticas

Seamos sinceros, el discurso de que la inteligencia artificial es para lograr crear una mente artificial que imite la inteligencia humana suena muy bonito e inspirador, pero uno de los principales motivos para los que queremos la inteligencia artificial es que nos solucione problemas. Y a ser posible problemas que nosotros no seamos capaces de resolver fácilmente. Hay problemas que son irresolubles matemáticamente e inabordables por la fuerza bruta con la potencia de cálculo actual. Una de las herramientas que tenemos para resolver estos problemas son las heurísticas y metaheurísticas. Estos algoritmos exploran el espacio de soluciones (el conjunto de todas las posibles soluciones para un problema) usando información sobre la calidad de cada solución para guiarse en la búsqueda de la mejor solución. Estos algoritmos permiten resolver en un tiempo aceptable problemas demasiado costosos como para ser abordados de manera exhaustiva (comprobando todas las posibles soluciones). Pero nada es gratis en esta vida, a cambio
Su principal diferencia es que las heurísticas suelen estar orientadas a un tipo de problema mientras que las metaheurísticas son más genéricas y se pueden aplicar a multitud de problemas.

El origen de estos algoritmos es diverso, desde reglas descubiertas empíricamente, desarrollos matemáticos, prueba y error o claras inspiraciones en fenómenos de la naturaleza. Rara vez estos algoritmos son únicos. Generalmente existen familias basadas en variaciones del mismo algoritmo para mejorar su rendimiento con distintos tipos de problemas.

Pese a una descripción tan genérica estos algoritmos tienen ciertas características en común:

Fitness/distancia:

Todas poseen una función que evalúa una solución y le indica «lo buena que es».  Aunque hablare casi todo el rato de función de fitness realmente esta función puede adoptar dos formas:

Una función de fitness o bondad que indica lo correcta que es una solución. Su valor, aumenta cuanto más cerca se está del valor deseado.

Una función distancia que indica a qué distancia del valor buscado estamos. Esta función decrece según nos acercamos a la solución ideal.

Aunque nos pueda parecer que son muy distintas realmente hay dos maneras muy simples de convertir un tipo de función en la otra.

Dividir un numero K mayor de 0 por el valor de la función.  K/f(x)

Restarle a un valor K mayor que 0 el valor de la función. en este caso hay que asegurarse de el valor de la función nunca es mayor que K.  K-f(x) 

Esta función ha de ser una representación apropiada del problema real ya que es la aporta información sobre la solución al algoritmo y de su corrección dependerá que se puede encontrar una solución adecuada.

Máximo/mínimo:

Intenta maximizar o minimizar el valor la función de fitness. El punto deseado debe de ser siempre un máximo o un mínimo y hay que crear la función de fitnes para que cumpla ese requisito.

Una función no tiene porque tener un único punto máximo/mínimo de hecho puede tener máximos/mínimos locales que son puntos que son máximos/mínimos en su entorno pero no son el punto máximo/mínimo de la función. Hay que tener cuidado de no quedarse atascado en uno de estos puntos.

Exploración:

Como ya se ha comentado estos algoritmos basan su funcionamiento en explorar el espacio de soluciones guiándose por la función de fitness buscando sus valores máximos (o mínimos).

Podemos imaginarnos el espacio de soluciones como una amplia pradera llena de lomas, cada punto de este terreno es una solución y la altura de cada punto viene determinada por la función fitness.

Ejemplo de espacio de busqueda

Ejemplo de espacio de búsqueda – Imagen obtenida Wikipedia

Estos algoritmos al ver un punto alto se lanzan hacia él cual montañista o cabra que siempre tira al monte y en cuanto llegan a ese máximo puede quedarse «atrapados» en él negándose a seguir explorando el terreno. Para evitar esto se tiene que permitir que exploren soluciones que sean peores que las ya encontradas, sin embargo tampoco han de ser tan «exploradores» que ignoren los máximos o se convertirán en un algoritmo que se mueve aleatoriamente por el espacio de soluciones. Gran parte de la dificultad de hacer funcionar estos algoritmos es lograr el equilibrio entre ambas situaciones.

Coste vs mejor solución:

Generalmente estos algoritmos devuelven aproximaciones de cual es la mejor solución. Para mejorar estas aproximaciones es necesario realizar más iteraciones lo que supone mayor consumo de recursos y coste en tiempo. Hay que buscar un equilibrio entre la exactitud del resultado y el coste en tiempo y recursos del mismo.

Solución aproximada:

No hay seguridad de que lo que se vaya a obtener sea la mejor solución, posiblemente solo se obtenga una aproximación. Pero afortunadamente en el mundo real una buena aproximación puede ser tan validad como la solución ideal. Ademas que en muchos casos se necesita solo una buena solución, sin necesidad que sea la solución óptima.

Dada su variedad también es difícil clarificarlas. Pero vamos a ver algunas características por las que agruparlas:

Una única solución o múltiples soluciones: 

Algunas heurísticas/metaheurísticas usan varias soluciones las cuales van modificando, a veces combinándolas,  para obtener nuevas soluciones. En el caso de una única solución simplemente tiene una solución que van mejorando.

Búsqueda global o búsqueda local:

La búsqueda global busca en todo el espacio de soluciones mientras que la local busca solamente en el espacio cercano a una solución propuesta. Lo mayoría de las heurísticas o metaheurísticas tratan de equilibrar ambos aspectos, sin embargo depende del problema a resolver necesitaremos decantarnos por uno de estos dos lados.

Deterministas o estocásticos:

En el primer caso si ejecutamos la heurística/metaheurística varias veces con los mismos parámetros iniciales e iteramos el mismo número de veces obtendremos siempre el mismo resultado. Mientras que en el segundo caso al entran en juego partes aleatorias la respuesta puede ser diferente, pese a que las condiciones iniciales sean idénticas. Eso significa también que puede ser necesario ejecutar el algoritmo varias veces para asegurarse de que la respuesta obtenida es la más adecuada.

Paralelizables:

Algunas heurística/metaheurística son fácilmente paralelizables. Esta característica puede ser importante si necesitamos resolver un problema con gran consumo de recursos ya que podríamos distribuir su calculo entre varias maquinas.

Inspiradas en la naturaleza:

Muchas metaheurísticas han aparecido de fijarse como en la naturaleza se soluciona el mismo problema. Los insectos, bandadas o los genes han sido inspiradores de varias de ellas. El problema es que este método puede llevar a encontrar metaheurísticas que funcionan sin saber muy bien el porque. sin embargo ha dado muy buenos resultados. Aunque últimamente da la sensación de que el proceso de creación de estas metaheurísticas ha dado la vuelta y parece en algunos casos primero se crea el algoritmo y luego se busca a que se parece ya que este tipo de algoritmos siempre despiertan interés independientemente de que no sean tan buenos o aporten nada a los ya existentes.

Podemos pensar que ya tenemos una solución mágica para todos nuestros problemas. Desgraciadamente no es tan sencillo. Suelen funcionar bien con problemas de optimización matemáticaoptimización combinatoria o satisfación de restricciones. Ejemplos hay muchos, el problema del viajante, el problema de la mochila, el problema de la ocho/n reinas, …

Sin embargo en casos cuando el espacio de soluciones no es fácil de explorar ya sea porque es enorme, hay muchas discontinuidades, muchos máximos locales o una combinación de todas.

Personalmente estos algoritmos me gustan, ya no solo por el interés que tiene de resolver este problemas, si no por fácil que puede resultar resolver alguno problemas irresolubles cambiando la forma de afrontar el problema. La mayoría de estas soluciones adoptan estrategias, que al menos a mi, me resultan curiosas.

 

Convertir de escala de grises a RGB/RGBA

Nos va a pasar que en muchos casos tras aplicar varios algoritmos a una imagen terminamos con una versión en escala de grises con un canal de 8 bits por pixel. Pero para visualizarla hemos de convertirla en RGB o en RGBA. ¿Como hacemos para que se siga viendo el mismo nivel de gris? Afortunadamente la respuesta es muy sencilla, damos a los tres canales RGB el valor del único canal de escala de grises.

//Red
r = gs;
//Green
g = gs;
//Blue
b = gs;
//Alpha
a = 0;

Enjambre/Nube de partículas

Esta metahurística está inspirada en el comportamiento de en como los enjambres de insectos, bandadas de pájaros o de los bancos de peces se comportan buscando alimento.

Se basa en «liberar» un grupo de individuos en el espacio de soluciones. Estos se mueven explorándolo en busca de los puntos máximos (óptimos). En cada iteración se mueven condicionados por la dirección que llevan ahora, cuyo símil sería la inercia, la mejor solución que ellos han visitado, que sería su experiencia, y la mejor solución visitada por el grupo, que seria la tendencia social.

Cada individuo tiene memoria y recuerda cual a sido el mejor punto por el que ha pasado y su «bondad» (fitness) para compararlo con la calidad de otros puntos buscando otro mejor. A parte de eso tiene una lista de coordenadas con la posición actual y otra con la velocidad a la que se moverá en cada dimensión en la siguiente interacción. Las velocidades pueden ser positivas o negativas así que además de la cantidad de desplazamiento también indican la velocidad. A diferencia de los enjambres del mundo real que solo se mueven en 3 dimensiones (al menos los que hay aqui en la Tierra) en los virtuales los individuos se mueven en tantas dimensiones como hagan falta para explorar las soluciones del problema. Por ejemplo si queremos encontrar el máximo de una función que tiene 10 parámetros cada parámetro será una dimensión del espacio de soluciones. Enjambres virtuales que vuelan en un espacio de n dimensiones buscando sus puntos máximos. Hay que reconocer que suena mejor que «Optimización basada en enjambres».

El algoritmo funciona actualizando el vector de coordenadas sumándole el vector de velocidades cuyos valores se actualizan en cada interacción y representa la lucha interna entre las creencias propias del individuo y las que comparte la sociedad (no diréis que no lo vendo interesante). Esto matemáticamente se realiza calculando la distancia a la mejor solución propia y a la mejor solución de todo el enjambre y cada una se pondera multiplicándola por un  valor que indica su importancia (Ksocial, Kown) . Ademas se multiplica por un valor aleatorio entre 0 y 1. Para introducir cierta variación aleatoria en cuanta importancia da a cada creencia. Imagínate una mañana antes del café lo que te importa que piense la sociedad.

Jugando con las constantes Ksocial, Kown puedes obtener individuos que se vean influenciados de distinta forma por la experiencia social y personal. Por ejemplo si ambos son cero el individuo no se verá influenciado por ninguna experiencia. Si poniendo cualquiera de ellos a cero nuestro individuo no tendrá en cuenta esa experiencia para guiarse. Por lo general el valor que se da ambas es 2.

El calculo de las velocidades para cada coordenada seria:

v[i] = Kinertia * v[i] + Ksocial * rand(0,1) * (cordsocial[i] – cord[i]) + Kown * rand(0,1) * (cordsown[i] – cord[i])

this.updateVelocities = function(){
  var ownExperienceVelocities = this.ownExperience.velocities(this.cords);
  var socialExperienceVelocities = this.socialExperience.velocities(this.cords);
  for(var i = 0; i < this.cords.length; ++i){
    this.velocities[i] = this.inertia.value * this.velocities[i] + ownExperienceVelocities[i] + socialExperienceVelocities[i];
  }
};

La cordenadas se actualizarian con una simple suma:

Cords[i] = cords[i] + v[i]

//Update cords
this.move = function(){
  for(var i = 0; i < this.cords.length; ++i){
    this.cords[i] += this.velocities[i];
  }
};

Finalmente nos queda la la inercia. La inercia se multiplica por el valor actual de la velocidad en cada dimensión Si el valor es muy grande los individuos se moverán más rápido y menor será el efecto de los otros parámetros basados en la experiencia. La función de la inercia es evitar que los individuos converjan demasiado pronto obligándoles a recorrer el terreno. Un valor de inercia elevado es útil al principio cuando interesa explorar lugares nuevos del espacio de soluciones pero dificulta la aproximación final al resultado óptimo una vez encontrada la región donde está. Es buena idea ir reduciéndola con el tiempo. Para ello se fija una inercia inicial y una final y se reduce cada interacción hasta alcanzar la inercia final. Al ir decreciendo los movimientos son cada vez más pequeños aproximándose a la solución final.

var Inertia = function(inertiaMin, inertiaMax, inertiaChange){
this.inertiaMin = inertiaMin || 0;
this.inertiaMax =  inertiaMax || 1;
this.inertiaChange = inertiaChange || 0.01;
this.value = 1;

this.update = function(){
  if(this.value >= this.inertiaMin){}
    this.value -= this.inertiaChange;
  }
  if(this.value < this.inertiaMin){
    this.value = this.inertiaMin;
  }
}
}

Cada interacción del algoritmo consta de los siguientes pasos:

  1.  Calculamos las velocidades en cada dimensión.
  2. Calculamos las nuevas coordenadas.
  3. Verificamos y corregimos las coordenadas.
  4. Calculamos la nueva inercia.
  5. Calculamos el fitness de las nuevas coordenadas.
  6. Si es necesario actualizamos la mejor solución propia.
  7. Si es necesario actualizamos la mejor solución social.
this.fly = function(){
  this.updateVelocities();
  this.move();
  this.cords = this.modelFitness.validateCords(this.cords);
  this.inertia.update();
  this.fitness = this.modelFitness.calculateFitness(this.cords);
  this.ownExperience.updateFitness(this.fitness, this.cords);
  this.socialExperience.updateFitness(this.fitness, this.cords);
}

Con el paso de las iteraciones los individuos iran convergiendo sobre el punto óptimo que hayan encontrado.

Vamos a modelar este sistema. Lo primero es ver que parámetros nos hacen falta:

La inercia simplemente almacena el valor inicial, el final, la cantidad que cambia y el valor actual. Cada vez que se indica que se actualice se resta al valor actual el valor de cambio hasta alcanzar el mínimo.

Almacenaremos la experiencia como un objeto que almacena las mejores coordenadas, su valor de fitness, la importancia (peso) que se da a la misma en la formula del cálculo de velocidades y una función para actualizarla y otra para calcular el valor correspondiente a la formula de actualización de velocidades.

var Experience = function(K){
  this.fitness = 0;
  this.cords = [];
  this.K = K || 2;

  this.updateFitness = function(fitness, cords){
    if(fitness < this.fitness){
      this.fitness = fitness;
      this.cords = cords.slice();
    }
  }

  this.velocities = function(birdCords){
    var velocities = [];
    for(var i = 0; i < this.cords.length; ++i){
      velocities[i] = (this.cords[i]-birdCords[i]) * this.K * Math.random();
    }
    return velocities;
  }
};

Cada individuo tendrá su propia experiencia y compartira la experiencia social con el resto de individuos. De esta forma tan sencilla se comunican entre ellos.

Los individuos son sencillos, simplemente se componen de la experiencia propia y social, el fitness actual, las coordenadas actuales, el vector de velocidades y la inercia.

var Bird = function(inertia, socialExperience, ownExperience, modelFitness, parameters){
  this.socialExperience = socialExperience;
  this.ownExperience = ownExperience;
  this.parameters = parameters;
  this.modelFitness = modelFitness;
  this.fitness = 0;
  this.cords = [];
  this.velocities = [];
  this.inertia = inertia;

  this.fly = function(){
  ...
  }

  this.move = function(){
  ...
  };

  this.updateVelocities = function(){
  ...
  };

  this.initialize = function(){
  ...
  };
};

La bandada/enjambre/nube es simplemente un array de individuos que cada iteracción se les pide que actualicen sus posiciones y fitness.

Las iteracciones se repiten hasta alcanzar la condición de finalización deseada. La mejor solución estará almacenada en la experiencia social.

Vamos a ver un ejemplo calculando el valor mínimo de la función similar a la que define una esfera.

f(x, y) = x²+y²

Pero para n dimensiones, el valor de la misma en cada punto es la suma de todas las coordenadas del punto elevada al cuadrado. Siento desvelaros el asesino pero el resultado se encuentra en punto que tenga todas sus coordenadas a 0. Realizaremos la búsqueda en el espacio comprendido entre 10 y -10 para cada una de sus coordenadas

Como buscamos el mínimo de la función pero este algoritmo funciona buscando máximos hemos de buscar una forma de solucionarlo. La búsqueda es en el espacio comprendido entre 10 y -10 asi que el valor máximo que puede tomar la función, que recordemos es la suma del cuadrado del valor de cada coordenada, es 100 por el numero de coordenadas. Solo hemos de restar a ese valor el valor del punto actual y tenemos una función de fitness que devuelve el valor máximo cuando nuestro punto corresponde con el mínimo.

La función de validar coordenadas es tan secilla como asegurarse de que nunca son menores de -10 ni mayores de 10.

Debajo esta el ejemplo para 5 dimensiones:

var test = require('../test/sphere.js');
var mrWolf = require('./flock.js');

var sphere = new test.TestSphere();
var model = {
  calculateFitness: sphere.calculateFitnessMaximize.bind(sphere),
  validateCords: sphere.validateCords.bind(sphere)
}
var parameters = new mrWolf.FlockParameters();
parameters.dimensions = 5;
parameters.cordsMin = [-10, -10, -10, -10, -10];
parameters.cordsMax = [10, 10, 10, 10, 10];
parameters.velocityMin = [-1, -1, -1, -1, -1];
parameters.velocityMax = [1, 1, 1, 1, 1];
parameters.inertiaChange = 0.05;

var flock = new mrWolf.Flock(parameters, model)
for(var i = 0; i < 1000; ++i){
  flock.search();
  console.log("Iteration "+i+" : "+flock.birds[0].socialExperience.fitness)
}
console.log("Best solution:")
console.log(flock.birds[0].socialExperience.fitness);
for(var i = 0; i < flock.birds[0].socialExperience.cords.length; ++i){
  console.log(flock.birds[0].socialExperience.cords[i]);
}

Aquí puedes encontrar el código en GitHub.

Una variante interesante de esta metaheurística es modificar el alcance de la experiencia social de cada individuo. En lugar de compartir una experiencia común para todos se comparte solo entre un grupo de individuos. Este grupo de individuos se puede fijar en la creación del mismo o por proximidad, calculando la distancia entre ellos y seleccionando la mejor experiencia solo entre aquellos cuya distancia sea menor de un valor fijado. Es importante que entre los grupos haya intercambio de información o no aportará nada tener varios grupos.

Convertir RGB a escala de grises

Una de las operaciones más habituales que se hace en visión por computador es convertir una imagen a color a una en escala de grises. Es una operación bastante simple pero hay múltiples opciones para real izarlo. Todas se basan en lo mismo, combinar los valores de los tres canales RGB para obtener un solo canal. Aunque hay distintos formatos, para este post voy a poner el caso de que los pixeles en color están en codificados como RGB con un byte por cada canal y que la información en escala de grises emplead un soo canal de un byte. Como vemos hemos de reducir tres bytes a solo uno.

Vamos usar una función que nos servirá como para la mayoría de los casos, en ella se le pasan los tres canales y los pesos asignados a cada uno, se multiplica cada canal por su correspondiente peso y se suman. Al ser un ejemplo no se realiza ningún tipo de verificación de los valores que se pasan ni del resultado.

function RGBtoGS(r,g,b,kr,kg,kb){
 return kr*r + kg*g + kb*b;
}

El caso más sencillo y rápido es tomar el valor de uno solo de los canales. Esto se puede usar cuando uno de los canales tiene más información o sufre menos el ruido. Hay que tener en cuenta que se pierde la información de los otros dos canales.

RGBtoGS(r,g,b,1,0,0); //devolver el canal rojo como gris
RGBtoGS(r,g,b,0,1,0); //devolver el canal verde como gris
RGBtoGS(r,g,b,0,0,1); //devolver el canal azul como gris

Una variación de esta técnica consiste en no coger siempre el mismo canal si no en coger el más o el menos brillante. Tienen la ventaja de ser rápido y de aportar más información que el caso anterior. Si la imagen es muy oscura (subexpuesta) elegir el canal más brillante puede ayudar a sacar detalles que de otra forma quedan ocultos en las zonas oscuras, si es demasiado luminosa (sobreexpuesta) el menos brillante puede aportar información que de otra manera quedaría quemada. Y ya que estamos podemos optar por el punto medio y calcular la media de la suma entre el canal más y el menos luminoso de cada píxel.

function greaterRGBtoGS(r,g,b){
  if(r &gt; g &amp;&amp; r &gt; b){
    return r;
  } else if(g &gt; r &amp;&amp; g &gt; b){
    return g;
  } else{
    return b;
  }
}
function lesserRGBtoGS(r,g,b){
if(r &lt; g &amp;&amp; r <b> r &amp;&amp; g &gt; b){
    return g;
  }else{
    return b;
  }
}

function averageRGBtoGS(r,g,b){
  return (greaterRGBtoGS(r,g,b)+lesserRGBtoGS(r,g,b))/2
}

Sin embargo la manera más habitual de hacerlo es ponderar los tres valores con tres constantes. La forma más fácil que se nos ocurre es simplemente calcular la media de los tres valores (o multiplicar cada uno por 0.33). Pero esta solución no es acorde con la realidad en la que por diversos motivos (desde físicos a biológicos como que nuestros ojos no son igual de sensibles a cada componente) cada canal no aporta lo mismo. Para ello existen distintas recomendaciones de que valores usar para ponderar cada canal. Incluyo algunos ejemplos.

RGBtoGS(r,g,b,0.33,0.33,0.33); //media
RGBtoGS(r,g,b,0.2126,0.7152,0.0722); //CIE 1931
RGBtoGS(r,g,b,0.299,0.587,0.114); // rec601 luma
RGBtoGS(r,g,b,0.2627,0.6780,0.593); // ITU-R BT.2100

Aunque lo recomendado es ponderar los tres canales, desde mi limitada experiencia el de máximo brillo me ha dado buenos resultados sobre todo con imágenes obtenidas con cámaras de móviles y portátiles. Además tiene la ventaja de ser muy rápido.

Chispas – Introducción

Antes de explicar qué es chispas voy a tratar de presentar que problema trata de solucionar chispas. Los sistemas «inteligentes» actuales tienen necesidad de elementos externos a tu propia infraestructura. Si compras una bombilla inteligente necesita estar conectada a Internet. Nuestra bombilla inteligente realmente se vuelve bastante tonta si no tiene un «cerebro» que le diga que hacer. ¿Por qué tiene que estar ese cerebro en la nube?. ¿Por qué tengo que depender de una empresa externa para que funcionen las luces de mi casa?. Puede parecer una tontería pero si la compañía cierra o es comprada por otra, como paso hace poco con Revolv, comprada por Nest (la propia Nest había sido comprada por Google) y que dejo tirados a un montón de usuarios de sus productos. ¿Como puede ser que cuando compro algo ese algo no sea mio?. Bueno, es mio como pisapapeles porque gran parte de sus funcionalidades se pierden sin los servicios que le dota una empresa que es la primera interesada en que les compre un pisapapeles nuevo. No soy de naturaleza desconfiada pero me suena sospechoso. Actualmente cualquier electrodoméstico puede durar fácilmente una década. ¿Va a mantener una empresa tanto tiempo los servicios que dotan de inteligencia a tu electrodoméstico?. Ojo, que mantener no es solo tener los servicios funcionando es también ir actualizando y corrigiendo los fallos y errores de seguridad que vayan apareciendo. Todo eso tiene un coste. ¿Van a seguir gastando dinero en un producto que hace tiempo que ya no venden y no les da actualmente ningún beneficio?. ¿Estamos seguro de que van a darnos la mejor solución o solo la más barata?. De hecho ya hay casos de productos con brechas grandes en su seguridad que al no poder, o no querer,  actualizar su firmware no queda más remedio que tirarlos a la basura.

Al problema de depender de empresas externas se suma que a su vez estas contratan los servicios de otras empresas y al final no sabes quién tiene acceso a tus datos. Y no es que estas empresas tengan que tener oscuros intereses en tus datos, pero un fallo de seguridad en cualquiera de ellas puede poner en peligro tu privacidad.

A todo esto hay que añadirle que muchos productos tienen incompatibilidades entre ellos. A veces por motivos técnicos, otras por motivos económicos para encerrarte en su «ecosistema». Y ya no hablo de integrar tus propios desarrollos que muchas veces todo son problemas.

amablemente le dices a tu teléfono «teléfono enciende la luz» y ocurre lo siguiente:

  • Le dices a tu móvil «teléfono enciende la luz»
  • Tu móvil graba tu voz y lo envía a unos servidores en algun otro lugar del mundo
  • Allí procesan tu voz, extraen el texto y lo envían de vuelta a tu móvil
  • Tu móvil procesa el texto y le pasa a la aplicación correspondiente
  • La aplicación le envía la petición al servidor de la empresa que te vendió la bombilla
  • El servidor procesa la petición y le envía a tu bombilla la orden de encenderse
  • Tu bombilla , conectada por WiFi a la red de tu casa, recibe la orden de encenderse
  • Tu bombilla se enciende

Todo ello para sustituir a un trozo de plástico y metal que cierra o abre un circuito según en que posición lo pongas y que a una mala puedes montar con un alambre y un clavo.

¿Chispas va ser la solución de todo este gran problema?. La verdad  es que no. Chispas pretende ser el intento de crear un sistema que facilite la creación de tus propios agentes de software inteligentes. La idea es que funcione en sistemas muy sencillos (y baratos) de tal forma que sea fácil montarte tu . De hecho mi plan es que funcione sobre una raspberry pi, arduino y el navegador de cualquier dispositivo (tablet, movil, PC) que soporten HTML5

Hasta ahora he dicho tres palabras mágicas que suena mucho pero no he explicado en que consisten. Como una imagen vale más que mil palabras la idea es esta:

Esquema0

 

Bueno, quizás lo que valga más que mil palabras sea una buena imagen y no esta. Pero como mis habilidades no dan para más tendremos que apañarnos con lo que hay. El dibujo explica que chispas se compone de:

  • Un servidor web, en este caso una Raspberry Pi, aunque puede ser cualquier ordenador. El único requisito es que pueda servir páginas sobre HTTPS. A su vez puede tener otras funciones como servidor de archivos o base de conocimiento (entendiendo como tal cualquier servicio que aporte información a nuestros agentes)
  • Uno o varios dispositivo con navegador web. El agente web de chispas actúa como intermediario entre el usuario y las funciones de chispas. Al ser una aplicación web no requiere instalación, solo conectarse con el navegador.
  • Chispas firmware funciona sobre uno o varios arduinos. Cada arduino se comporta a su vez como un agente independiente y tiene asociado un servidor web muy sencillo por lo que desde un navegador podrias conectarte directamente al arduino y controlarlo desde la interface que sirva el propio arduino. Para que esto funcione el arduino debe de ser capaz de conectarse a la red ya sea usando una shield, un desarrollo propio, o alguna placa que incorpore la conexión a ethernet

Cada uno de estas partes es independiente de las demás y trabaja de forma colaborativa entre ellas. si por ejemplo fallara la raspberry los arduinos seguirían funcionando y el agente web seguiría pudiendo interactuar con ellos.

Todo funciona sobre la red local con intercambio de peticiones HTTP. Es una arquitectura simple y que hace muy sencillo poder introducir componentes propios a cualquier nivel. Nada es propietario y cualquier parte es prescindible o modificable. No se depende de servicios externos pero eso no te impide conectar a Chispas a los mismos. Por ejemplo si queremos que reaccione a nuestro email nada nos impide poner en la raspberry código que lo haga y enviar esa información a los agentes.

En resumen, sencillo, barato, abierto y extensible.