IMU MPU-9250 - Acelerómetro

Hola a todos de nuevo,

Hoy he estado probando esta plaquita que tiene una IMU (unidad de medición inercial) que lleva un acelerómetro, un giroscopio y un magnetómetro, al total 9 grados de libertad.

Realmente se trata de una placa compleja. Da para mucho más de lo que se va a ver en esta página, pero vamos a ir primero con lo simple. Hay mucha gente que se pregunta: ¿Puedo medir la velocidad con el acelerómetro? Así que me he dispuesto a hacer la prueba.

Usaré éste código modificado de www.luisllamas.es con una placa Arduino UNO, como veréis dejo comentada la parte de giroscopio y magnetómetro, por si queréis echarle un ojo, y he hecho otras modificaciones:

  • Calcular la salida en milésimas de g (aceleración de la gravedad, aprox. 9.8 \displaystyle m/s^{2})
  • Calcular la resultante de los tres ejes, como si de un vector se tratase:

\displaystyle Resultante = \sqrt{ax^{2}+ay^{2}+az^{2}}\ m/s^{2}

Con este cálculo la resultante en reposo debería ser exactamente g, es decir, daría 1000. Lo que pasa es que habrá ruido y offset, por lo que nunca va a estar tampoco justo ahí.

Después de programar esto, conecto todo y tomo los datos con Termite, a un archivo TXT. Como tengo la licencia de MATLAB student, he aprovechado para hacer los cálculos.

He hecho un movimiento hacia adelante, he parado un poco, y luego hacia atrás. Después he repetido otra vez, y después de un reposo, otra vez pero más lento.

Y la salida de la aceleración resultante es la siguiente:

La buena noticia aquí es que el valor en reposo oscila sobre 1g, como habíamos predicho, y eso es bueno. Podemos eliminarlo fácilmente restando 1000 a la señal. Pero el resto es todo ruido. Intentar hacer una integración es un suicidio, porque el ruido significa que hay un "drifting" (deriva) de la velocidad que es constante, y además muy rápido. Estoy hablando de que en reposo antes de empezar el movimiento indica una velocidad de 5 m/s.

Con ayuda de un par de filtros (paso alto y media movil, también conocido como paso bajo) ha mejorado algo:

Ahora hacemos la integración y obtenemos... "La velocidad":

Según esto, mi mano se ha movido a 25 m/s, que son exactamente 90 km/h. Puede ser que me haya emocionado un poco pero es algo excesivo. Lo bueno es que tras el filtro he podido detectar el movimiento en el sentido contrario e incluso parece que está ahí la parada más larga. Ya se observan al inicio los efectos del drifting, aunque bastante atenuados, ese error se acumula ahí para siempre.

Este ha sido el mejor resultado de una tarde entera de análisis de los datos. Creo que lo volveré a intentar poniendo una SD y dando una vuelta con el coche, ya que las aceleraciones en ese caso son menores, y puedo usar únicamente el valor de un eje para quitar ruidos, quitar la gravedad...

La conclusión de esto es, así de primeras, que es una terrible idea usar una IMU para intentar calcular la velocidad. Tanto con esta IMU barata como con una de 1000€, según me han contado.

Lo que sí me he llevado es la programación de un par de filtros muy simples y buenos.

Me he estado documentando un poco y hay un estudio de 2007 que dice que la deriva de una IMU usando únicamente el acelerómetro es de unos 150 metros por cada minuto, y que si se usa el giroscopio y el magnetómetro para tomar todas las referencias se puede reducir a 5 metros por cada minuto, lo cual ya es un logro y tengo que probarlo, pero que es imposible realizar una navegación con este sistema si no se corrige con algún otro.

- "Pero yo he visto por ahí que se usa en navegación, incluso de aviones y satélites"

Sí y no. Como acabo de decir, el sistema acumula errores con el tiempo, que en estos sistemas de navegación son corregidos mediante otros sistemas de posicionamiento, como el GPS, radar, triangulación con radio, los sensores de velocidad del viento y muchos datos externos.

Dejo el código:

//GND - GND
//VCC - VCC
//SDA - Pin A4
//SCL - Pin A5
 
#include <Wire.h>
 
#define    MPU9250_ADDRESS            0x68
#define    MAG_ADDRESS                0x0C
 
#define    GYRO_FULL_SCALE_250_DPS    0x00  
#define    GYRO_FULL_SCALE_500_DPS    0x08
#define    GYRO_FULL_SCALE_1000_DPS   0x10
#define    GYRO_FULL_SCALE_2000_DPS   0x18
 
#define    ACC_FULL_SCALE_2_G        0x00  
#define    ACC_FULL_SCALE_4_G        0x08
#define    ACC_FULL_SCALE_8_G        0x10
#define    ACC_FULL_SCALE_16_G       0x18
 
 
//Funcion auxiliar lectura
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
   Wire.beginTransmission(Address);
   Wire.write(Register);
   Wire.endTransmission();
 
   Wire.requestFrom(Address, Nbytes);
   uint8_t index = 0;
   while (Wire.available())
      Data[index++] = Wire.read();
}
 
 
// Funcion auxiliar de escritura
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
   Wire.beginTransmission(Address);
   Wire.write(Register);
   Wire.write(Data);
   Wire.endTransmission();
}
 
 
void setup()
{
   Wire.begin();
   Serial.begin(115200);
 
   // Configurar acelerometro
   I2CwriteByte(MPU9250_ADDRESS, 28, ACC_FULL_SCALE_16_G);
   // Configurar giroscopio
   I2CwriteByte(MPU9250_ADDRESS, 27, GYRO_FULL_SCALE_2000_DPS);
   // Configurar magnetometro
   I2CwriteByte(MPU9250_ADDRESS, 0x37, 0x02);
   I2CwriteByte(MAG_ADDRESS, 0x0A, 0x01);
}
 
 
void loop()
{
   // ---  Lectura acelerometro y giroscopio --- 
   uint8_t Buf[14];

   I2Cread(MPU9250_ADDRESS, 0x3B, 14, Buf);
   
   // Convertir registros acelerometro
   int16_t ax = -(Buf[0] & 8 | Buf[1]);
   int16_t ay = -(Buf[2] & 8 | Buf[3]);
   int16_t az = Buf[4] & 8 | Buf[5];
 
   // Convertir registros giroscopio
   int16_t gx = -(Buf[8] & 8 | Buf[9]);
   int16_t gy = -(Buf[10] & 8 | Buf[11]);
   int16_t gz = Buf[12] & 8 | Buf[13];
 
 
 
   // ---  Lectura del magnetometro --- 
   uint8_t ST1;
   I2CwriteByte(MAG_ADDRESS, 0x0A, 0x01);
   do
   {
      I2Cread(MAG_ADDRESS, 0x02, 1, &ST1);
   } while (!(ST1 & 0x01));

   uint8_t Mag[7];
   I2Cread(MAG_ADDRESS, 0x03, 7, Mag);
 
   // Convertir registros magnetometro
   int16_t mx = -(Mag[3] & 8 | Mag[2]);
   int16_t my = -(Mag[1] & 8 | Mag[0]);
   int16_t mz = -(Mag[5] & 8 | Mag[4]);
 
 
   // --- Mostrar valores ---
   float axf = (float)ax*32000/65536.0;
   float ayf = (float)ay*32000/65536.0;
   float azf = (float)az*32000/65536.0;
   float aresf = sqrt(axf*axf+ayf*ayf+azf*azf);
   ax = (int16_t)axf;
   ay = (int16_t)ayf;
   az = (int16_t)azf;
   uint16_t ares = (int16_t)aresf;
   
   // Acelerometro
   Serial.print(ax, DEC);
   Serial.print("\t");
   Serial.print(ay, DEC);
   Serial.print("\t");
   Serial.print(az, DEC);
   Serial.print("\t");
   Serial.print(ares,DEC);
   Serial.println("");
 /*
   // Giroscopio
   Serial.print(gx, DEC);
   Serial.print("\t");
   Serial.print(gy, DEC);
   Serial.print("\t");
   Serial.print(gz, DEC);
   Serial.print("\t");
 
 
   // Magnetometro
   Serial.print(mx + 200, DEC);
   Serial.print("\t");
   Serial.print(my - 70, DEC);
   Serial.print("\t");
   Serial.print(mz - 700, DEC);
   Serial.print("\t");
   
   // Fin medicion
   Serial.println("");
   */
  delay(10);    
}

BMP280 BME280

¡Hola!

Hoy quería contaros un poco sobre este sensor. Se trata de un circuito integrado que vamos a encontrar en modulitos, como se hace todo ahora, para tener el acceso fácil. Vamos a dejar primero una cosa clara para que no os equivoquéis como yo:

- BMP280: sensor de temperatura y presión
- BME280: sensor de temperatura, humedad y presión

Los más baratos los encontraréis a 3.3V, y la diferencia con los más caros es, no nos engañemos, que estos últimos llevan un pequeño regulador de tensión y puedes conectarlo a 5V. Y luego están los muy caros que llevan adaptadores de nivel de tensión para que el módulo no sufra, lo cual es lo más indicado.

Aunque sea de 3.3V, se puede conectar con un puerto digital con un máximo de 5V, mientras alimentas la patita Vcc con 3.3V. Pero no es lo más recomendable. Se puede usar un Arduino Pro Mini o Due, o algún otro modelo que funcione a 3.3V.

Aquí uno caro con reguladores de Adafruit

¿Qué puedes hacer con estos sensores?
Lo que he hecho yo ha sido poner un módulo BMP280 conectado a la Raspberry Pi 3. Cada cierto tiempo, 5 minutos, hago que se lean los valores del sensor, se guarden los valores en un archivo de datos (dos archivos de datos distintos, de hecho, uno del día y otro de las últimas 24 horas) y mediante un extenso código creo una gráfica, que luego puedo visualizar en el navegador mediante un poco de código php y html. Aquí un resultado:

A ver, la temperatura está demasiado elevada, esto es porque está en un lugar bastante cerrado junto a la raspberry que suelta algo de calor (el procesador se pasa el día sobre los 50ºC). Hay picos de temperatura cuando encendemos el ventilador, lo cual es muy curioso. La presión tampoco es correcta y ahí ya no sé qué decir, pero la evolución, comparada con un sensor calibrado no muy lejos de aquí, es la misma.

Este es el comienzo de un proyecto mayor que tengo entre manos y aunque el resultado no parezca muy correcto, ya he conseguido tener algunas pinceladas del software y de paso ver cómo se comporta la temperatura de la habitación ante determinados eventos.

Otras cosas que se pueden hacer: antes de la raspberry probé el módulo con Arduino y daba valores bastante realistas tanto de temperatura como de presión. Conociendo la presión atmosférica a nivel del mar y la que da el sensor, puedes aproximar la altitud a la que te encuentras. Esto viene en el código de ejemplo de Arduino IDE con la librería Adafruit_BMP280.

Y si te compras el BME280, puedes medir la humedad y ya tienes un sensor ambiental completo.

Estos módulos se comunican mediante I2C, lo cual requiere dos cables de comunicación. Por ejemplo en el Arduino Uno R3 se conecta:

  • Pin SDI del módulo al pin A4 (analógico 4) del Arduino
  • Pin SCK del módulo al pin A5 (analógico 5) del Arduino

Debido a que el módulo ya contiene unas resistencias de pull-up, no es necesario preocuparse de nada más, sólo requiere la alimentación (recordad, Vcc a 3.3V) y GND.

Para el resto de placas y microcontroladores (Raspberry, STM32, ...) hay librerías, así que su uso sigue siendo igual de sencillo.

He generalizado mucho para el BMP280, porque el BME no lo he probado, pero las librerías se encuentran fácilmente poniendo en Google por ejemplo: Arduino bme280 library.

Mucho ánimo y espero que se os ocurran cosas geniales con este módulo.

Programar como un electrónico

Programando hoy en día las llamadas librerías nos quitan un montón de trabajo, el mejor ejemplo de esto es programar en el Arduino IDE. En él ni siquiera te tienes que preocupar de importar las librerías más usadas, y puedes cambiar de placa sin tener que cambiar ni un poquito del código que has escrito, es casi magia.

Si no sabes de qué te estoy hablando, no pasa nada, se explicará según avance este hilo.

Pero hubo un tiempo, no muy lejano, en el que las cosas eran más complejas y se programaban con ensamblador. Este es el lenguaje de programación más puro (por describirlo de alguna forma) al que te vas a enfrentar cuando programes algo, aunque estoy seguro de que no lo tendrás que usar porque, como digo, es cosa del pasado. Sin embargo está bien, estuvo bien por mi parte aprender eso, porque da una visión mucho más cercana de lo que es en realidad un sistema de este tipo, un microcontrolador, un procesador, su memoria, por dónde van los datos... Que parece innecesario, pero luego hasta los ordenadores se manejan con esto en el fondo. Y eso es lo que quiero transmitir.

Como decía, el ensamblador es el lenguaje de programación más básico, lo que le transmites al programador con lo que escribes no es más que mover unos bits aquí y allá, hacer operaciones lógicas básicas, todo funciona leyendo y escribiendo registros.

¿Qué es un registro?

Bueno, no me voy a poner ahora a explicar qué tiene un ordenador por dentro (RAM, CPU, almacenamiento, buses de datos), pero sí extender esto a los microcontroladores.

En primer lugar, el programa se escribe en la memoria permanente del micro, ahora por lo general son memorias tipo FLASH, ahí se almacena en unos y ceros todo el programa y es el único lugar donde se mantienen los datos inalterados aunque se apague y se encienda. A no ser que el micro tenga otra memoria para datos del usuario, por ejemplo una EEPROM, que ahí también son permanentes, hasta que los borres.

En los micros la CPU es un concepto un poco abstracto, ya que está desperdigada por el resto de componentes. Se encarga de leer la memoria de programa, mover los datos y llevarlos a la unidad aritmético-lógica (ALU), el lugar donde se realizan operaciones matemáticas o lógicas muy simples, como sumar, multiplicar o hacer AND u OR.

Hay memoria RAM, está por ahí para guardar las variables mientras corre el programa, esas variables que se borran tras el apagado.

Hay vida más allá de estos tres componentes, hay lo que se llama registros. Un registro es como otra memoria que se borra al apagar el micro, y está hecha de la misma forma, de una serie de bits. Además tiene una dirección, a la que la CPU puede ir para leer o escribir los datos. Es igual que una memoria simple, pero los registros tienen funciones especiales. Vamos a ver un ejemplo.

El micro está leyendo el programa en la FLASH, y el programa le dice que escriba dos variables en la RAM, que son dos números en binario 110 (6) y 010 (2), y quiere que esas dos variables se sumen. El proceso no es nada simple.

En primer lugar va a coger uno de esos números y lo va a poner en un registro auxiliar (el registro W por ejemplo en el PIC), para ello tiene que pasar por la ALU sin realizar ninguna operación.

En el siguiente paso va a coger el otro número y lo va a poner en la ALU a la vez que el primer número, que estaba en el registro auxiliar, también entra en la ALU por el otro lado. La ALU tiene la instrucción de sumar y obtiene 1000 (8) que se guarda en, ojo cuidado, el registro auxiliar. Una vez terminada la operación hay un registro de estado (STATUS en el PIC), que tiene un bit que se pone a 1 cuando la ALU ha terminado. Se mira este bit y como ya lo hemos visto, hay que decirle que se ponga a 0 para que avise la próxima vez.

Ya sabemos que el resultado está preparado, ahora se puede mover a la RAM para guardar el resultado o incluso enviarlo a un registro de los periféricos, por ejemplo un pin de salida digital, si se envía al registro del puerto digital (p.ej. PORTB), esto haría que (con otras configuraciones previas) el pin correspondiente al bit número 4 de ese registro se pusiese en 1, y encendería... No sé, digamos que se enciende el LED, que siempre queda bonito.

Espero que con esto más o menos ya tengas una idea de qué es un registro. Es eso, un lugar en las direcciones de memoria del micro donde cambiamos bits para que ocurran ciertas cosas. Ya estamos un poquito más cerca de entender qué hace un código en C.

Direcciones

Ahora hablemos de las direcciones, hemos dicho que los registros son lugares en la memoria y que la CPU puede dirigirse a ellos mediante su dirección. Esta viene dada en las hojas de características del micro, es decir, te lo chiva el fabricante, y en numeración hexadecimal, lo que no lo hace muy amigable pero sí algo más compacto. Y tu se lo transmites al programa previamente, aunque aún no lo sepas.

Continuando con el ejemplo, en ensamblador tu no le dices al programa "manda lo que hay en el registro auxiliar al puerto b", lo que dices realmente es "mueve lo que hay en el registro auxiliar al registro con dirección 0x06". En este caso no tienes que decir la dirección del registro auxiliar porque ya está implícita en la función de ensamblador, MOVWF. Al fin y al cabo casi todo lo que se mueve pasa por ese registro, así se escribe menos. Vamos a escribir esta línea en ensamblador:

MOVWF 0x06

Nota: no todo lo que se mueve en todos los micros pasa por ese registro, pero sí la absoluta mayoría. Los más actuales tienen un módulo llamado DMA (direct memory access) que evita precisamente eso, pasando directamente de un registro a otro o a la memoria, con lo que la CPU puede usar el registro auxiliar para otra cosa mientras tanto.

Librerías

Pero menudo lío, ¿no? Pues llegan al rescate las librerías. ¿Qué hacen las librerías? Predefinir las cosas para que no lo tengas que hacer tu, y con esto me refiero a que, con la tecnología tan avanzada que tenemos, podemos hacer lo siguiente:

MOVWF portb

¿Qué acaba de hacer esta librería? Asignar un nombre bonito a una dirección de memoria, para que no tengamos que recordar los numeritos, ha hecho algo tan simple como esto:

portb equ 0x06

Hagamos otro ejemplo, esta vez vamos a poner a 1 un pin del puerto b directamente, pero sin alterar el estado del resto de los pines como hemos hecho (ups) accidentalmente antes. Para ello el ensamblador tiene que hacer:

  • Leer el puerto b y llevarlo al registro auxiliar W
  • Operar con un número binario cuyo resultado cambie el bit exacto que queremos sin alterar el resto
  • Limpiar el bit de STATUS que indica que ha terminado la operación
  • Llevar el resultado del registro W al registro del puerto b

¿Qué has hecho tu en un código C con librerías? Si estamos en Arduino IDE por ejemplo:

digitalWrite(pin4, HIGH)

Pero la ejecución es exactamente la misma, y esto ya viene hecho de antes por otra persona entonces no tienes que preocuparte.

"Bueno no es para tanto"

Pues no, no es para tanto si hablamos de un micro de 8 bits como es el PIC o los más comunes Atmel de Arduino, en estos la dirección de un registro se puede describir mediante un número hexadecimal de 2 dígitos, por ejemplo el 0x06 que hemos visto antes.

PERO. Luego vienen los micros de 32 bits. Los ordenadores son de entre 32 y 64 bits, recordemos. En estos casos la dirección del registro se tiene que escribir con 8 dígitos. 0x00000000. Ya va empeorando el asunto, pero esto no es nada.

En un micro de arquitectura ARM, la configuración de cada uno de los pines alcanza tal grado de complejidad (resistencias pullup/down, multiplexores, funciones analógicas, pwm, comunicación, velocidad, etc etc etc) que los registros empiezan a tener registros dentro. Y sin embargo, gracias a las librerías, logramos compactarlo todo a una serie de instrucciones muy simples y fáciles de recordar, por ejemplo lo que expliqué aquí.

¿Pero entonces se usa para algo el ensamblador? No, el ensamblador se ha sustituído completamente por las librerías, pero sí que usamos de vez en cuando un sucedáneo. Ya no nos gusta escribir la función movwf, sino que podemos hacer algo como:

portb = 0x08

Y en ocasiones, se da que no existen librerías para lo que quieres o simplemente lo vas a hacer más rápido tu directamente que ponerte a buscarlo, por lo que sí que puede pasar que escribas direcciones a mano...

0x06 = 0x08

Como esto cambiaba todo el puerto voy a hacer algo que sólo cambia el pin 4:

0x06.4 = 1

¿Entonces qué es eso de programar como un electrónico?

La programación que se hace en los ordenadores es muchísimas veces independiente del hardware, es decir, vas a hacerlo funcionar en cualquier ordenador, y no importan sus registros, direcciones, ni su configuración porque eso ya lo han implementado previamente los fabricantes de cada uno de los componentes (drivers) y se ha agrupado todo bajo el sistema operativo para hacerlo funcionar sin que te enteres.

En electrónica programamos microcontroladores muy dependientes del hardware y sus periféricos. Cuando hacemos una placa con un micro, le añadimos una señal de reloj, esta señal tiene que ser tratada previamente para que llegue a todos los periféricos correctamente, y esto se hace a nivel de registros (pero con librerías). Nosotros somos el fabricante. Cuando añadimos las entradas y salidas, configuramos cada uno de los pines para que haga exactamente su tarea. Se trata de saber dónde están y a dónde van los bits que quieres manejar, más que poner una función y dejar que el sistema operativo se preocupe por tí.

Estos métodos tienen ciertas ventajas, un mayor control sobre lo que estás haciendo y muchas veces, en vez de usar una librería que lo hace todo, puedes encender únicamente los periféricos que necesitas, con lo que ahorras energía. Otra ventaja es que durante el diseño tienes libertad de colocar las cosas como mejor te convenga, sin tener que seguir los esquemas del fabricante.

Espero que os haya parecido interesante

Neurociencia

El 4 y 5 de mayo (2017) acudí al II Congreso de Pensamiento Crítico y Divulgación Científica (Universidad de Valencia), donde el tema central este año era la neurociencia. Quería escribir "unas palabras" para dejar constancia de lo que he podido aprender, y seguro que podría dar que hablar.

En primer lugar, destacar la gran labor divulgativa y crítica (ese era el título...) de los/las profesionales que acudieron a exponer sus trabajos, y la perspectiva fuera de la estricta biología que se ha tomado para este. Como estudiante de ingeniería al principio me chocó un poco que filósofos hablasen de neurociencia. Después, pude ampliar mi mente al ver que un pequeño avance en el conocimiento del cerebro, su funcionamiento y conexiones, daba mucho que pensar en psicología, medicina, educación, y a los filósofos mucho juego porque les gusta hablar de los entresijos de la mente, comportamiento, pensamientos y cómo se une a la parte física (estructura cerebral y función).

En segundo lugar hay que hablar de la tontería y el mito que se ha formado alrededor de las neurocosas. Al igual que en otros campos en su momento destacaron las inmunocosas, ahora parece que el prefijo de moda es neuro. Y es que suena muy atractivo, para la gente que no se da cuenta, sobre todo cuando se une a cosas como neuroeconomía o neuromarketing. No sé qué tendrá que ver, pero ya se ofrecen máster de estas cosas. Cosas que hacen que me duela la cabeza. Neurodolor por neurocalentamiento. Pues entre lo poco que sabemos, y la cantidad de gente que quiere aprovecharse para sacar dinero, y la cantidad de gente que se lo cree, la tontería está servida. Son montones de mitos y falacias las que se han impuesto y ahora puedes escuchar en la barra de cualquier bar, o peor, aplicarse en educación o tratamientos psicológicos no avalados científicamente. Y de las pseudociencias ni hablemos.

En mi campo, el cerebro no tiene nada de místico ni extraño. Es una conexión de células que se excitan entre sí, muchas de ellas, y realizan una función. El trabajo actual más importante es desarrollar la tecnología y estudiar su estructura para poder modelizar matemáticamente un cerebro completo, y así poder simular respuestas al igual que hacemos ya mismo con el corazón. Aún no sé si, por ejemplo, el cerebro de un caracol (sólo 10k neuronas) está modelizado, es probable que sí, y sus resultados, pero seguro que el cerebro humano (unas 10^11 neuronas) no, porque no tenemos ordenadores capaces de calcular eso.

Y cuando consigamos destapar los entresijos del cerebro, biológicamente hablando, las implicaciones éticas no son menores. Supongamos el caso en el que un sujeto comete un crimen, es escoria de la humanidad y debe ser juzgado y encarcelado. Pero, en un estudio mediante resonancia magnética funcional, se encuentra en su cerebro una porción asociada a la moral que no funciona bien, es decir, estrictamente hablando este sujeto no sería capaz de tomar decisiones correctas. ¿Qué pena se le debe imponer? En mi opinión está claro que no se puede, bajo ningún concepto, justificar sus acciones. Sin embargo, teniendo en cuenta los antecedentes y los hechos probados, estaría bien que se le impusiese una pena justa y que se le ayudase en la reinserción. Una vez hechos estos avances, deberán existir tratamientos que ayuden. Puede que haya quien se pregunte: ¿Por qué no estudiamos a todo el mundo para ver aquellos que tienen predisposición a cometer un crimen? Pues porque esto no es Minority Report ni tenemos tiempo ni dinero para realizar estos estudios a toda la población.

Está claro que la neurociencia ayudará en el futuro, hay cientos de síndromes y problemas psicológicos que dependen de sus avances. Hay que dejar bien claro que igual de importante que investigar en neurociencia, es saber utilizarla.

Pulsioxímetro casero V1

Bueno, ya ha llegado el momento de ir desarrollando el pulsioxímetro casero que tenía prometido. Para conocer la teoría sobre éste, me remito a la entrada pulsioximetría.

Dicho esto ya sabréis más o menos de dónde proviene la señal, cómo se mide y cómo se procesa. No se menciona en ningún punto las características de la señal porque:

  • En amplitud no va a estar claro por el hecho de que dependerá de la intensidad de los LED y de la sensibilidad del fotodiodo receptor. Lo ideal es que se tenga una ganancia conocida para poder conocer el valor de la señal a la entrada, ya sea fija o variable (potenciómetros digitales), pero eso lo dejaré para la siguiente versión. En esta pondremos un potenciómetro de precisión y ajustaremos según se visualice la señal.
  • En frecuencia dicen algunas fuentes (no todas se ponen de acuerdo) que de 0.5 a 5 Hz está el fotopletismograma. Yo soy más fan de poner el corte superior a 15 Hz. En el inferior, la idea es no filtrar y dejar los offset "naturales", que aunque son bastante grandes nos interesan para tomar la señal con un ADC típico como los de un Arduino, PIC, ARM, ... Claro que hay que poner un ajuste de offset. Cualquier otra cosa ya lo dejaría a los filtros digitales, por comodidad. Así los componentes son asequibles.

Veréis, la intención de esta primera versión no es conseguir medir algo similar al SpO2, sino hacer una aproximación al circuito analógico y conseguir medir el fotopletismograma con cierta calidad. Después, el circuito analógico se puede reutilizar y mejorar. Si todo saliese muy bien haría un encargo de PCBs a los chinos para tenerlo bonito.

Compré unos LED SMD para esto. Yo que puedo voy a usar el color verde. Mi intención es soldar el LED y el sensor cerquita (pero no mucho) en una placa y colocar el dedo encima de ambos. Por eso es SMD, porque es plano y será más cómodo poner el dedo. La historia aquí va a ser la dificultad de soldar el LED, pero como ya tengo algo de experiencia me apañaré en el asunto, si no os veis bien con esto, probáis con un LED normal y corriente o de 3 mm de diámetro, que son más pequeños, y levantar un poquito el sensor para no clavaros la punta del LED y que encima el dedo se quede mal colocado. En la V2 es casi seguro que acabaré con algo así, si no encuentro un SMD infrarrojo a un precio asequible.

Entre otros asuntos también quiero probar a hacer una fuente de corriente constante para el LED, a ver si así emite más o menos siempre a la misma intensidad.

En cuanto a la alimentación, ahí viene la mayor de las risas en cuanto a diseño. Me gustaría que la entrada ADC fuese a 3.3 V máximo, para poder aprovechar el mayor rango posible de microcontroladores del mercado, y ya de paso que funcione a pilas. Tengo unos portapilas de 2xAAA, convertidores boost de 1-4 a 5 voltios e inversores DC MAX1044. Ya veremos si lo uso todo, pero por ejemplo si queréis un máximo de 5 V aconsejaría usar el convertidor boost, que en principio yo lo quería para estabilizar la tensión de las pilas cuando empezase a caer por desgaste.

Vamos entonces por etapas: corriente constante en los LED, fotodiodo, convertidor corriente-tensión, ajuste de offset y ganancia, filtro paso bajo y ya el ADC.

Corriente constante

Para conseguir esto estaría bien usar el "ring of two", cuyo circuito es:

En la izquierda se usan dos diodos para referenciar, y en la derecha un transistor. La corriente Ic se calcula fácilmente Ic = 0.7/Re, con lo que para conseguir 20 mA se puede poner una resistencia de 35 Ω. Rb depende de la tensión de alimentación y podría valer entre 1 y 10 kΩ. Tampoco está muy claro eso de que la tensión en Re sea de 0.7V, depende de la tensión base-emisor del transistor. Supuestamente no debería ser demasiado dependiente de la tensión de alimentación.

Fotodiodo

Tengo unos bpw34 cuyo espectro de sensibilidad es el siguiente:

Parecen ser muy bien para el infrarrojo de 940 nm y relativamente bien para el rojo de 660 nm, mientras que presenta una baja sensibilidad (aprox 0.25) para el verde (aprox 510 nm). Bueno, el caso es que van polarizados en inversa.

Convertidor corriente-tensión

Aquí espero usar un convertidor de transimpedancia, simple como:

 Simplified transimpedance amplifier

Como la corriente inversa máxima por el fotodiodo va a ser de 50 μA, voy a probar primero con una resistencia Rf de 100 kΩ, si está muy bajito se puede subir sin muchos problemas, en el prototipo. Dicen que puede presentar oscilaciones, si eso ocurre espero poder verlo sólo por curiosidad, y se soluciona con algún condensador en la realimentación de valor previo cálculo.

Ganancia y offset

Se supone que en este punto me sobran 3 amplificadores operacionales, así que echando balones fuera voy a hacer el circuito más estúpidamente simple que hay, eso sí, es muy seguro.

 

 

Aquí la salida es la señal multiplicada por la ganancia, MENOS el offset dividido por el potenciómetro multiplicado por la ganancia.

Out = G(S-a*Vp)

Filtro paso bajo

Pues un filtro paso bajo normal y corriente. Quisiera probar el orden 1 y el 2, a ver las diferencias, que no deberían ser muchas. Para ajustar a unos 15 Hz, vale con unas resistencias de 100 kΩ y unos condensadores de 100 nF.

ADC

Hoy en día podemos usar tranquilamente cualquier ADC integrado en el microcontrolador, entre 8 y 12 bits irá perfectamente. Habrá que cuidar el rango de tensión.

Al final el circuito queda así:

El diodo D1 está ahí por la intención de proteger el ADC de posibles tensiones negativas al desajustar el offset/ganancia.

Primeros resultados

El circuito no está funcionando como debería. He tenido que hacer alguna pequeña modificación sobre la marcha, que editaré en su momento cuando esté plenamente funcional.

Descubrí que darle al LED 20 mA es realmente excesivo, el brillo es cegador y refleja demasiado sobre la piel, con su correspondiente reflejo en la respuesta del fotodiodo. Con unos 7-8 mA basta.

El problema principal lo tengo con los potenciómetros de ganancia y offset, que se comportan de una forma que no logro explicar, y quiero averiguar el por qué antes de desmontarlo y volverlo a montar.

Aún así he conseguido algunos resultados.

Segundos resultados

He modificado la resistencia del primer amplificador a 1 MΩ. También he modificado el circuito de ganancia y offset para que "deje de hacer cosas raras". Tal como esperaba, el offset y la ganancia se conjugan de tal forma que la señal satura por cualquier lado enseguida, resulta muy inestable. Otro problema que he visto, tal como está en la imagen de encima, es que el offset tiende a permanecer subiendo. Para solucionar las dos cosas molaría usar potenciómetros digitales y probar con un circuito adaptativo, pero eso no ocurrirá todavía. El resultado es el mismo que antes, pero la señal AC tiene más amplitud, he conseguido llevarla estable hasta 1 V pico a pico, aunque con cualquier movimiento se pierde.