lunes, 17 de noviembre de 2014

Simulación de circuitos eléctricos en Simulink

Bloques usados:

Solver Configuration: (Simscape>Utilities)
Voltage Sensor: (Simscape >Foundations Library>Electrical>Electrical Sensors)
Current Sensor: (Simscape >Foundations Library>Electrical>Electrical Sensors)
PS-Simulink Converter: (Simscape >Utilities)
Resistor  (Simscape>Foundations Library>Electrical>Electrical Elements)
Electrical Reference  (Simscape>Foundations Library>Electrical>Electrical Elements)
AC Voltage Source     (Simscape>Foundations Library>Electrical>Electrical Sources)
Mux  (Simulink >Common Used Blocks)
Scope  (Simulink>Sinks)

Para este modelo se dejó la configuración del Solver en default con la fuente de voltaje a 10 V pico-pico a 60 Hz y la resistencia es de 50 ohms. El tiempo de simulación es de 0.06 segundos.
Nota: Cómo activar las leyendas en el Scope.

martes, 19 de agosto de 2014

Buscando a Waldo con Matlab

Una aplicación entretenida de la correlación cruzada normalizada es la detección de una imagen patrón en una imagen de mayor tamaño. Esta correlación está definida matemáticamente como 

dónde f(x,y) representa a la función de imagen y t(x,y) la función de templete o imagen patrón que se desea detectar. En nuestro programa ese templete será este segmento de imagen dónde aparece Waldo:


Nuestra función principal será el siguiente mapa en el que habrá que encontrar a Waldo:


Para encontrarlo utilizaremos la función normxcorr2(). Esta función retornará un arreglo bidimensional resultado de la correlación cruzada del templete y la imagen. Para encontrar la posposición de Waldo en la imagen bastará con encontrar el punto donde la función alcanza su máximo. El arreglo resultante será tan grande como la imagen del mapa por lo que habrá que seleccionar una pequeña región alrededor del pico para poder visualizarlo mejor:

 Para encontrar la posición del pico utilizamos la función max() con la siguiente sintaxis:

[pico,indice] = max(c(:)); % Obtener valor del pico y su índice

El arreglo c representa a la función de de correlación. Hasta aquí la variable pico contiene el valor del máximo y la variable indice contiene la posición del valor máximo como si la matriz c estuviera distribuida en un arreglo vectorial. Para conocer la verdadera poción del pico en la imagen se necesita la función  ind2sub():

[y,x] = ind2sub(size(c),indice); % Obtener posición en la imagen 

Teniendo ahora la posición de Waldo solo queda mostrar la imagen completa con un rectángulo superpuesto para señalarlo:


 El programa completo es quizá más compacto de lo que pudieron haber pensado

clc,clear
mapa = imread('Waldo.jpg'); %Carga imagen del mapa
waldokernel = imread('waldok.jpg'); % carga templete de Waldo
c = normxcorr2(waldokernel(:,:,1),mapa(:,:,1)); % Correlación cruzada normalizada
[pico,indice] = max(c(:)); % Obtener valor del pico y su índice
[y,x] = ind2sub(size(c),indice); % Obtener posición en la imagen
ccut = c(y-45:y+45,x-45:x+45); % Recortar región alrededor del pico
imshow(mapa)
hold on
rectangle('Position',[x-50,y-43,70,70], 'LineWidth',2)
figure(2),surf(ccut), shading flat, title('Pico en Correlación Cruzada')



miércoles, 23 de julio de 2014

Elisyum

En un conversación divagada le comenté a un amigo una idea que he traído en la cabeza desde hace mucho. Se trata de construir una ciudad desde cero con todo lo mejor que México puede ofrecer: las mejores universidades, centros de investigación, hospitales y fabricas de alta tecnología así como las oficinas de las empresas más grandes, es decir, una Hong Kong mexicana en la cosa de Veracruz o Oaxaca. Me hizo ver inmediatamente que esa idea le recordó a la película de Elisyum. Quizá tenga razón. Aumentar aún más la desigualidad en el país dista de ser una solución. Pero me queda la curiosidad. ¿Qué pasaría si existiera una ciudad así?

jueves, 26 de junio de 2014

Problema al implementar un programa asíncrono en VHDL

Supongamos que tenemos un programa muy simple en VHDL en el que prendemos y apagamos dos leds mediante dos interruptores: 

library ieee;
use ieee.std_logic_1164.all;

entity leds is
port( a,b : in std_logic;
     s1,s2: out std_logic);   
end leds;

architecture control of leds is
begin

s1 <= a;
s2 <= b;

end control;


Para la tarjeta de desarrollo Basys2 nuestro archivo .ucf quedaría:

# Interruptores
NET "a" LOC = P11;
NET "b" LOC = L3;

# Leds
NET "s1" LOC = M5;
NET "s2" LOC = M11;


Hasta aquí todo parece en orden e incluso nuestro programa funciona bien en la simulación. Pero un problema muy común entre los estudiantes de los primeros cursos de sistemas digitales es que al momento de cargar el archivo .bit de un programa asíncrono como este a una tarjeta de desarrollo, ésta se queda sin hacer nada. Esto ocurre porque la tarjeta espera arrancar por default con el oscilador interno aunque no lo usemos en el programa (razón por la que este problema muy rara vez se presenta en programas que si lo usan). Este problema se resuelve simplemente haciendo click derecho sobre "Generate Program File", buscar "Process Properties" que nos abrirá la siguiente ventana en la debemos buscar la categoría "Startup Options" y cambiar la opción de FPGA Start-Up Clock de CCLK a JTAG Clock:

domingo, 22 de junio de 2014

Control PID para el modelo simple de una masa móvil


Supongamos que tenemos un pequeño vehículo de masa 'm' que se encuentra en una posición inicial x0 que queremos llevar hasta una posición final xr. Para simplificar las cosas supondremos que no existe fricción entre las ruedas y el suelo ni resistencia al aire. El modelo de nuestra planta queda descrito entonces por la 2da Ley de Newton:


dónde la fuerza F(t) es la entrada y la posición x(t) nuestra salida de la planta. Obteniendo la transformada de Laplace,su función de transferencia nos queda:


Observe que cuando a 'm' se le asigna una masa unitaria G(s) se convierte en un doble integrador. Para este modelo, como debe haberse tratado en cualquier curso de control, un controlador proporcional resulta ineficaz. Un controlador de este tipo únicamente mantendría oscilando el carrito al rededor de la posición deseada sin alcanzar nunca la estabilidad. Incluso considerando la resistencia al aire, este tipo de controlador mostrará una respuesta transitoria indeseable. Para control ideal, donde el carrito acelere y desacelere suavemente hasta detenerse justo (casi en este caso sin fricción ni resistencia al aire) en la posición que queremos requerimos un controlador PID. Las ganancias para controlar un doble integrador ideal son:


Corriendo nuestro modelo en Simulink estos son son los resultados:



viernes, 30 de mayo de 2014

Derivador práctico con amplificador operacional

Pareciera que los libros escriben con letras chiquitas que el derivador ideal es inestable y no funciona correctamente en la práctica. La razón está en el efecto que causa el ruido al aplicar la derivada. Si modelamos el ruido como una función aleatoria que depende del tiempo, se observa que la función tiene variaciones abruptas dentro de un intervalo muy pequeño. Estas variaciones abruptas causan derivadas con valores muy grandes que saturan la salida del derivador. Es por esto que un derivador implementado físicamente debe llevar un filtro pasa bajas en la entrada para atenuar el ruido lo más posible. El ruido también puede modelarse como una señal de alta frecuencia siendo esta la razón de la efectividad del filtrado.

En el circuito que muestro en la figura estoy usando un filtro pasa bajas con una frecuencia de corte de 1.59 KHz (simplemente porque podía implementarlo con los componentes que tenía a la mano, no es que sea la frecuencia óptima). Los valores de C2 y R1 fueron elegidos para una ganancia unitaria.   Por supuesto, este derivador sólo puede usarse para señales de baja frecuencia como en cómputo analógico o controladores PID.

NOTA: Después de algunas pruebas en el laboratorio me di cuenta que hay una falla en el circuito porque hace falta colocar un seguidor de voltaje entre el filtro y el derivador. Esto ocurre porque al no haber un acoplamiento con un seguidor la función de transferencia de todo el circuito cambia volviéndose un derivador con un filtro pasa-bajas con frecuencia de corte de 16 Khz.

domingo, 9 de marzo de 2014

Crear un archivo de texto desde un array en Matlab

Supongamos que se requiere generar una señal sinusoidal en un DSP o un FPGA a partir de un conjunto de 30 puntos de la función sin(t) dentro un solo ciclo. Si se programa el dispositivo en lenguaje C, necesitamos un array que contenga los 30 valores. Para transferir el array generado a un archivo.txt del cual se puedan copiar cómodamente (separados ya por una coma) se utiliza la función dlmwrite(). El código para generar los 30 valores en formato short es:

t = linspace(0,2*pi,30);
y = round(32767*sin(t));
dlmwrite('texto.txt',y);

domingo, 23 de febrero de 2014

Beakman

Fue muy emocionante compartir la explanada del museo Universum con al menos 2 millares de estudiantes y jóvenes profesionales de las más diversas ciencias, todos siendo niños otra vez durante poco más de una hora. Después de mis padres, me aventuraría a decir que el personaje de Paul Zaloom terminó de encaminarme por la senda de la ciencia. Creo que soy una de las personas más indicadas para afirmar que tener carisma y una irreverencia agradable es más difícil que aprender matemáticas avanzadas. Pocos divulgadores de la ciencia han podido marcar una generación entera de un país."They always say "you must be really tired of hearing I'm a scientist because of you". But I've got to tell you friends... I will never ever get tired of hearing you people "I'm a scientist because of you"". Contuve algunas lagrimas al cierre.

domingo, 16 de febrero de 2014

Arduino + DAC LTC1661 (comunicación SPI)

Algunos proyectos electrónicos requieren salidas de señales analógicas "ideales" como una señal sinusoidal o una señal de control para determinado proceso en el cual el PWM no es adecuado. Si se quiere utilizar Arduino, ya sea como tarjeta de adquisición de datos o como el cerebro de todo el proceso, es necesario utilizar un DAC o fastidiarse con un arreglo de amplificadores operacionales. Para este ejemplo estoy usando un convertidor integrado de 10-bits y 2 salidas LTC1661 de Linear Technologies. Tal vez no sea el modelo de DAC más común en muchos países pero utiliza el protocolo de comunicación SPI, por lo que el código mostrado puede ser adaptado sin mayor problema a otros modelos.

Lo primero que se debe hacer a la hora de escribir un código de comunicación SPI es revisar la hoja de especificaciones del dispositivo con el que vamos hacer la comunicación y buscar el diagrama de tiempos y la tabla de direcciones. Para el LTC1661 tenemos este diagrama:


Recordemos que aún con la librería SPI.h de Arduino sólo podemos enviar paquetes de 8 bits a la vez. Cuando visualizamos esta secuencia nos damos una buena idea de lo que tenemos que hacer en el código. En este caso habrá que dividir la "palabra" de 16 bits que deseamos enviar en dos paquetes de 8 bits cada uno. Así, la secuencia binaria de un 512 (en binario 1000000000) que se traducirá como la mitad del voltaje de referencia para la salida A es:

 1001100000000000
(dirección, dato y bits "don't care")

y debe ser divido como:

10011000  y 00000000

 Para esto tomaremos una variable que llamaremos "valor" que representará a un dato de 10 bits como valor de voltaje. Es importante recordar que esta variable se declara como entero sin signo que en Arduino es un dato de 16 bits. Para obtener el primer byte será necesario utilizar una variable de tipo char (que es de 8 bits) que llamaremos "temp" y que igualaremos a la variable "valor" después de hacer un corrimiento de 6 bits a la derecha de manera que el entero sin signo 0000001000000000 se convierta en 0000000000001000 y finalmente en el char 00001000. Para agregar la dirección 1001 habrá que sumar simplemente 1001000 a nuestra variable "temp". Toda esta operación en lenguaje Arduino (y también en C) queda:

temp = (valor >> 6) & 0x0F;  // Corrimiento a la derecha
temp = 0x90 + temp; // dirección y parte del dato

Teniendo este numero cargado en "char" queda listo para ser enviado con la fusión SPI.transfer(). Para el segundo byte lo único que falta hacer es aplicar un corrimiento de dos bits a la izquierda e igualar a "temp" y volver a enviar con SPI.transfer():

temp = valor << 2;

Como había escrito al principio, es posible que los dispositivos que estén utilizando trabajen con datos de 8 o 12 bits y no 10 como en este ejemplo. Pero en esos casos lo único que habrá que hacer es ajustar el valor de los corrimientos. Si es la primera vez que conectas un dispositivo SPI a un Arduino es importante que sepas cuales son los pines para este tipo de comunicación.

Código completo:

#include <SPI.h>;
#include < math.h>;
int ss = 10;
float t;
unsigned valor = 0;

void setup(){
  
pinMode(ss, OUTPUT);
SPI.begin(); 

}

void enviar( unsigned int valor_dac){
char temp;

digitalWrite(ss, LOW); // CS o SS en bajo

temp = (valor_dac << 6) & 0x0F;  // Corrimiento a la derecha
temp = 0x90 + temp; // dirección y parte del dato
SPI.transfer(temp); // envio del primer byte
temp = valor_dac << 2; // Corriemiento a la izquierda 
SPI.transfer(temp); // envío del segundo byte

digitalWrite(ss,HIGH); // CS o SS en alto
}

void loop(){
  
for (int i; i < 80; i++){ // función de prueba
  t = t + 0.078;
  valor = (unsigned int) 511*sin(t)+511;
  enviar(valor);
  delay(1);
  if (t > 2*3.14) t = 0;
}

}

lunes, 3 de febrero de 2014

Función promedio para lectura analógica desde PIC (MikroC)

Cómo muchos hemos notado, la conversión A/D en PIC es un tanto ruidosa. El método de promediación no es la solución definitiva al ruido del ADC, pero mejora considerablemente los resultados:

unsigned int promediolectura(void){
int i =0;
unsigned int a = 0;

for (i=0; i < 17 ; i++){

  
   invec[i] = ADC_Read(0);

}

for (i=0; i <17;i++){
   a = a + invec[i];
}

  a = a >>4;
 
  return a;
}

miércoles, 4 de diciembre de 2013

Velada astronómica en Atlihuetzía

El viernes pasado estuvimos en el jardín de niños Guadalupe Bernal de Cuellar en Atlihuetzía, Tlaxcala llevando el planetario móvil y algunos telescopios. Por primera vez en los años que llevo haciendo divulgación científica con el INAOE trabajé con niños de preescolar; algo que había estado esperando porque fue durante esta época cuando creció mi gusto por la ciencia. Fue muy gratificante escuchar los comentarios de los niños después de darles una pequeña charla dentro del planetario inflable proyectando Stellarium con un espejo esférico. Me agradó mucho ver también el concurso de trajes espaciales organizado por la escuela. Felicito enormemente la labor de los profesores de este kinder, me encantaría ver a todo el país haciendo lo mismo. La etapa preescolar es muy importante para el nacimiento de una vocación científica y es la más descuidada por los divulgadores en Puebla.

martes, 26 de noviembre de 2013

Adquisición de Señales Mioeléctricas (EMG)


La electrónica médica es un área a la que personalmente siempre he rehuido. Sin embargo esta práctica para la materia Procesamiento Digital de Señales me ha reconciliado bastante con esta ciencia. No pretendo hacer un reporte completo en esta entrada, pero si describir todo lo relevante al circuito y a la adquisición por la PC para que este interesante experimento pueda realizarse por otros estudiantes. Este es el circuito utilizado:


El amplificador de instrumentación es algo difícil de encontrar en México pero puede comprarse en linea en Newark, el envío es gratis y demora unos 4-6 días. Para el filtrado posterior se considera únicamente el rango de frecuencias de 50 - 500 Hz, eliminando la componente de ruido de 60 Hz del suministro eléctrico. Para implementar los filtros se puede usar un LM334 o un LM224 aunque lo ideal es pedir dos opamp's de baja distorsión OPA2604 para reducir el ruido en la señal. V+ y V- van conectadas a 5V y -5V respectivamente. Si quieres hacer pruebas en tu casa y no cuentas con fuentes de laboratorio puedes hacer un arreglo de dos baterías de 9V reguladas con un 7805 y un 7905 (-5V).

Hay dos maneras de adquirir los datos a la PC: vía serial (Con Arduino o un PIC) o usando SoundcardScope a través de la tarjeta de audio. Si vas a utilizar esta última opción, es necesario mapear la señal de +/-5V a +/- 1V. Para esto debes usar el siguiente circuito divisor de voltaje en la salida del filtro de Notch (antes del rectificador de precisión):


Nota: la resistencia de 13K en el filtro de Notch es obligatoria, quiero decir que no puede ser sustituida por un valor comercial cercano. Será necesario utilizar un trim-pot.

Nota 2: El electródo de referencia va en en la muñeca o el codo.

martes, 29 de octubre de 2013


A veces, emborrachados por los descubrimientos y logros incesantes de la ciencia moderna, de forma inconsciente, guiados por el hecho de que el conocimiento es generado cada día, nos vemos secretamente guiados a “sentir” que la realidad se fabrica en ese momento. Como si el ‘top quark’ no hubiera existido hasta su descubrimiento. Como si la curvatura del espacio-tiempo no hubiera existido en la época de Euclides. Como si la naturaleza cuántica de la fotosíntesis no hubiera sido. Un algo simplemente inexistente.

Lo maravilloso, lo sorprendente es que siempre han sido. Pero no solo ellos, sino todo conocimiento futuro. Toda base del conocimiento futuro que pueda existir alguna vez. Todo cuanto es posible expresar como tal. Todo ello, lo inimaginable, lo lejano y lo cercano, el borde mismo del Universo, existe.


viernes, 27 de septiembre de 2013

Revolución

Hace poco edité un cartel facebuquero con el título "Libros recomendados para entender cómo hacer una revolución". La corta lista de 3 libros la tomé del video  Revolution: An Instruction Manual: "From Dictatoship to Democracy" de Gene Sharp, una guía corta sobre procedimientos de resistencia civil  que puede leerse en una sola tarde , "The Crawl" y "The Psychology of Revolution" de Gustave Le Bon. Publiqué el cartel en una página de divulgación científica que mantengo junto con los enlaces las ediciones digitales en español de los 3 libros. Más por curiosidad de experimento social que de "sed revolucionaria" envíe un enlace al cartel con un breve texto a todas las páginas de "revos" que puede encontrar. Sólo una se interesó por la publicación y dio un share que se perdió entre memes de EPN. Más de uno me ha mirado con desaprobación al oírme decir que los adolescentes y adultos jóvenes de este país no quieren realmente un país mejor sino que disfrutan la catarsis del desprecio al símbolo opresor.  

miércoles, 18 de septiembre de 2013

Interrupciones externas con PIC18f4550 en MikroC

La mayoría de los tutoriales que hay en internet sobre interrupciones en microcontroladores parecen decirnos todo lo que ya sabemos excepto lo que realmente necesitamos. Por esta razón no vamos a tratar aquí las generalidades del proceso. El mejor consejo que puede darse para ser un buen programador de microcontroladores es confiar en la hoja de especificaciones del dispositivo más que en cualquier tutorial. Muchos de los proyectos disponibles en la web son un horrible copy & paste de segmentos de códigos que le funcionaron al programador en otros casos y le fue más sencillo adaptarlos a nuevos propósitos. Muchos de estos programas están repletos de lineas innecesarias y algoritmos innecesariamente complejos que sólo asustan a los que apenas se aventuran en el mundo de los microcontroladores. Si realmente se entiende el funcionamiento, limitaciones y ventajas del PIC en cuestión, la programación simplemente "fluirá" de las manos. Si uno se acostumbra a usar la hoja de especificaciones se podrá adaptar cualquier buen tutorial a un PIC diferente al utilizado en el tutorial. Comencemos...

Registros

Un microcontrolador es, en un sentido estricto, una computadora y hará lo que le ordenes. Los registros nos sirven para configurar la funcionalidad del dispositivo; lo que queremos que se habilite o deshabilite y como queremos que se comporte. Así mismo nos permite saber las cosas se ejecutaron como se ordenaron por medio de banderas (flags). Para el 18F4550 tenemos tres registros de control de interrupciones: INTCON, INTCON2 e INTCON3. Para más detalles pueden revisar la data. Aquí entraremos directo a un ejemplo sencillo:

Ejemplo Encender un LED con un flanco de bajada y apagarlo con un flanco de subida en INT0

Esto, por supuesto, podemos hacerlo sin utilizar interrupciones, pero el punto aquí es visualizar el funcionamiento de estas. Bien entendido, puede utilizarse este ejemplo para hacer cosas más interesantes como un frecuenciometro, medidor de rpm's o cualquier cosa por la que hayan llegado aquí.

Primero, definamos que es lo queremos hacer.

1. Queremos que haya una bandera que se active cuando INT0 se interrupa en flanco de subida.
2. Queremos que la misma bandera se limpie y se vuelva a activar pero con un flanco de bajada.
3. Finalmente queremos utilizar este proceso para hacer algo útil.

Bien, primero entonces debemos modificar los registros correspondientes. Necesitamos una línea que habilite la interrupcion externa por INT0

INTCON.INT0IE = 1; // Interrupción externa INT0 habilitada

...otra línea que habilite todas la interrupciones de manera que nuestro vector de interrupción se ejecute continuamente

INTCON.GIE = 1;   // Habilita todas las interrupciones

...y otra que habilite (inicialmente) el disparo de la bandera por flanco de subida

INTCON2.INTEDG0 = 1; // INT0 en flanco de subida

Esta última linea modifica un bit del registro INTCON2. Como las 2 primeras lineas modifican bits del mismo registro, podríamos reducirlas a una sola linea igualando todo el registro al valor del byte deseado. Pero para fines didácticos lo dejaremos así. Bien, lo que sucederá hasta este punto en el programa será que cuando haya un cambio de bajo a alto en RB0 (INT0) la bandera INT0IF, que está en el registro INCON, cambiará a uno y se quedará en ese estado hasta que resetee vía software.

Algo importante que suelen olvidar los tutoriales es que es necesario activar el TMR0 incluso si no va utilizarse directamente en el programa. Si no lo activan, el programa funcionará sin problemas en algunos simuladores pero no en el PIC físico. Para activarlo se usa la linea:

T0CON = 0b10000000;

Aquí solo estoy activando el timer y al dejar en '0' los bits menos significativos estamos configurando el prescaler a 1:2. Si necesitan un prescaler diferente consulten el apartado del registro T0VCON en la data.  
 
Rutina de interrupción

void interrupt(){

  -Instrucciones-

}


Al leer esta rutina, el compilador sabrá que deberá escribir esas lineas de código en el espacio de memoria llamado vector de interrupción. Puesto que se habilitó el bit GIE del registro INCON, no será necesario llamar a "interrupt" desde "main" como normalmente se haría en lenguaje C. Para este ejemplo requerimos que nuestra rutina de interrupción envíe un '1' a RD0 después de detectar un flanco de subida y un '0' al detectar un flanco de bajada. Para esto necesitamos una variable que llamaremos flagFlanco y otra LED que contendrá el estado del puerto. El código completo nos queda:

int LED = 0;
bit flagFlanco; // Las variables de este tipo no pueden 
                // inicializarse (0 por default)    

void interrupt(){


  if(INTCON.INT0IF == 1){

   flagFlanco = ~flagFlanco;
   INTCON.INT0IF = 0; // Limpiar bandera de INT0

   }

   if( flagFlanco == 1 ){

   LED = 1;
   INTCON2.INTEDG0 = 0; //INT0 en flanco de bajada
    }

  else {

   LED = 0;
   INTCON2.INTEDG0 = 1; // INT0 en flanco de subida
   }

}

void main() {

ADCON1 |= 0x0F;
CMCON |= 7;

INTCON.INT0IE = 1; // Interrupción externa INT0 habilitada
INTCON.GIE = 1; // Habilita todas las interrupciones
INTCON2.INTEDG0 = 1; // INT0 en flanco de subida
TRISD = 0x00; // Puerto D como salidas


//TMR0 activado, prescaler 1:2
T0CON = 0b10000000;


  while(1) {

   PORTD.F0 = LED;

  }

}

domingo, 8 de septiembre de 2013

Mapa celeste del cometa ISON desde México

Animación del 10 al 25 de diciembre del 2013 a las 6:00 AM