Descargar

Proyecto final de control digital equilibrador


  1. Descripción del proyecto
  2. Comunicación entre la computadora y el micro
  3. Algoritmo genético
  4. Convertidor Digital-Analógico
  5. Diagrama

1.- Descripción del proyecto

El proyecto consiste en equilibrar una varilla colocada en una base que se conecta a un potenciómetro. El potenciómetro a su vez está colocado en un carro sobre un riel. El objetivo es equilibrar verticalmente la varilla haciendo que el carro permanezca lo más cerca posible a un punto en el riel. La posición del carro también se mide con un potenciómetro. En la siguiente figura se muestra el diagrama.

edu.red

Los potenciómetros se conectan al microcontrolador M68hc908jk3 (de ahora en adelante llamado micro) a los pines pb0 y pb4 para realizar la conversión analógico-digital. Los datos de la conversión se mandan a una computadora, esta los procesa y regresa el voltaje que debe aplicarse al motor del carro. Entonces el micro manda esta información a un conversor digital-analógico conectado al puerto d. La salida de este conversor se conecta al motor del carro. Este es el diagrama a bloques del proyecto:

edu.red

En las siguientes secciones se describe el funcionamiento de la comunicación entre la computadora y el micro, el funcionamiento del programa del micro, el programa de la computadora, el conversor digital-analógico y por último, el diagrama completo del proyecto. El programa para la computadora fue hecho en C++ Builder de Borland y el del micro en el programador de P&E Micro.

2.- Comunicación entre la computadora y el micro

La comunicación entre la computadora y el micro se realiza a través del puerto paralelo de la computadora y 6 pines del puerto B del micro. La comunicación es bidireccional pero no al mismo tiempo, es decir, mientras la computadora transmite el micro recibe y mientras el micro transmite la computadora recibe pero no pueden transmitir y recibir al mismo tiempo, si esto se intenta ambas partes quedan en un ciclo esperando a que la otra parte responda el pedido de comunicación. El algoritmo de comunicación es igual en las dos partes (solo se cambian las conexiones). En el micro se tienen que configurar lo pines pb1, pb2 y pb3 como entradas y los pines pb5, pb6 y pb7 como salidas. Los pines pb0 y pb4 se configuran como entradas pues en estos se conectan los potenciómetros. Los pines pb3 y pb7 funcionan como bits de estado de recepción y transmisión respectivamente (BR y BT), los pines pb2 y pb1 funcionan como bits de datos de recepción (DR1 y DR2) y los pines pb6 y pb4 como bits de datos de transmisión (DT1 y DT2). En a computadora la configuración que se aplica al puerto paralelo es la que se muestra en el siguiente diagrama:

edu.red

El algoritmo para la transmisión de un byte es el siguiente:

  • 1. Esperar a que empiece la transmisión (que se ponga 1 en BR)

  • 2. Ya que hay un 1 en BR, se lee en DR1 y DR2 los dos bits menos significativos.

  • 3. Poner un 1 en BT para indicar que se leyeron los primeros dos bits.

  • 4. Esperar a que se ponga un 0 en BR.

  • 5. Leer los siguientes dos bits de los pines D1 y D2.

  • 6. Poner 0 en el BT para indicar que se recibió la información

  • 7. Esperar a que se ponga un 1 en BR.

  • 8. Leer los siguientes dos bits.

  • 9. Poner 1 en el BT.

  • 10. Esperar a que BR tenga un 0.

  • 11. Leer los últimos dos bits.

  • 12. Poner 0 en BT.

  • 13. Fin de la recepción.

La contraparte de este algoritmo es la transmisión de un bit:

  • 1. Esperar a que BR esté en 0.

  • 2. Poner en BT un 1 y en los DT1 y DT2 los bits menos significativos.

  • 3. Esperar a que BR esté en 1.

  • 4. Poner en 0 en BT y en DT1 y DT2 los siguientes dos bits.

  • 5. Esperar a que haya un 0 en BR

  • 6. Poner un 1 en BT y los siguientes dos bits en DT1 y DT2

  • 7. Esperar hasta que en BR haya un 1.

  • 8. Poner los dos bits más significativos en DT y colocar un 0 en BT.

  • 9. Esperar hasta que en BR sea puesto un 0.

  • 10. Fin de la recepción.

El esperar a que se pongan unos o ceros en los bits de transmisión y recepción es lo que provoca que, al querer transmitir o recibir las dos partes al mismo tiempo, se trabe la comunicación cayendo en un ciclo. Aparte de las rutinas de transmisión y recepción es necesario tener una de inicialización, esta rutina manda y recibe de manera progresiva los números del 0 al 7 por BT, DT1 y DT2 y los recibe por BR, DR1 y DR2. Al final se vuelva a mandar y esperar un cero. El código para la comunicación en el micro es el siguiente:

;configuración inicial:

lda #$E0 ;poner el puerto b los 5 bits menos significativos como

sta ddrb ;entradas y los 3 mas como salidas

;iniciar la comunicación

iniciacom

lda #$00 ;cargar con 0 el acumulador

ini2 sta salida ;colocar el valor del acumulador a en el PTB

sta PTB

ini1 lda PTB ;leer el puerto B

and #$0E ;hacer una mascara para obtener BR, DR1 y DR2

lsla ;ajustar

lsla

lsla

lsla

cmpa salida ;comparar lo que se leyó con lo transmitido

bne ini1 ;ir a ini1 si no son iguales

add #$20 ;agregar uno a lo que se transmite

cmpa #$00 ;verificar si no se han mandado todos los números del 0 al 7

bne ini2 ;ir a ini2 si aún faltan números

sta PTB ;poner 0 en PTB

ini3 lda PTB ;esperar a que la computadora ponga 0 en el puerto

and #$0E

cmpa #$00

bne ini3

lda #$00

sta PTB ;Poner nuevamente 0 en PTB

rts ;Salir.

;Recepción:

lee brclr 3,PTB,lee ;Esperar a que haya un uno en BR

lda PTB

and #$06 ;leer los dos bits menos significativos

lsra

sta entrada

bset 7,PTB ;poner en uno a BT

buclee brset 3,PTB,buclee;esperar un cero en BR

lda PTB

and #$06 ;Obtener los siguientes dos bits

lsla

add entrada

sta entrada

bclr 7,PTB ;poner 0 en BT

buclee2 brclr 3,PTB,buclee2;Esperar un 1 en BR

lda PTB

and #$06 ;Obtener los siguientes dos bits

lsla

lsla

lsla

add entrada

sta entrada

bset 7,PTB ;Poner un uno en BT

buclee3 brset 3,PTB,buclee3;esperar un cero en BR

lda PTB

and #$06 ;Leer los dos bits menos significativos

lsla

lsla

lsla

lsla

lsla

add entrada

sta entrada

bclr 7,PTB ;poner 0 en BT

sallee rts ;Fin de la transmisión

;Transmisión

escribe brset 3,PTB,escribe;esperar a que haya un 0 en BR

lda salida

and #$03 ;poner uno en BT y los dos bits menos significativos en DT

lsla

lsla

lsla

lsla

lsla

add #$80

sta PTB

bucesc1 brclr 3,PTB,bucesc1;esperar un uno en BR

lda salida

and #$0C ;Poner 0 en BT y los siguientes dos bits en DT

lsla

lsla

lsla

sta PTB

bucesc2 brset 3,PTB,bucesc2;Esperar un cero en BR

lda salida

and #$30 ;Poner un uno en BT y los siguientes dos bits en DT

lsla

add #$80

sta PTB

bucesc3 brclr 3,PTB,bucesc3;Esperar un uno en BR

lda salida

and #$C0 ;Poner un cero en BT y los dos bits mas significativos en DT

lsra

and #$7f

sta PTB

bucesc4 brset 3,PTB,bucesc4;Esperar un cero en BR

salesc rts ;Fin de la recepción

El siguiente es el código de la computadora para las funciones descritas anteriormente.

//–Esta función pone un byte (valor) en el puerto (puerto)——————–

void salidapuerto(short int puerto,unsigned char valor)

{

asm{ mov dx,puerto

mov al,valor

out dx,al

}

}

//–Esta función es el complemento de la anterior, lee un byte del puerto——

unsigned char entradapuerto(short int puerto)

{

unsigned char r;

asm{ mov dx,puerto

in al,dx

shr al,03

mov r,al

}

if (rChecked=enfitness;

if (enfitness&&trabajando)

{

TrackBar2->Position=400;

k1=poblacion[voy].k1;

k2=poblacion[voy].k2;

k3=poblacion[voy].k3;

k4=poblacion[voy].k4;

Editk1->Text=FloatToStr(k1);

Editk2->Text=FloatToStr(k2);

Editk3->Text=FloatToStr(k3);

Editk4->Text=FloatToStr(k4);

}

variable[0].valor=posl-posdes;

variable[1].valor=pendulo;

if (trabajando)

{

r=f.evalua();

}

else

{

r.valor=0;

}

if (trabajando)

{

ag(r.valor,tiempo);

Caption=IntToStr(generacion)+", "+IntToStr(voy)+", "+FloatToStr(double(tiempoind)/1000.0)+", "+FloatToStr(poblacion[0].fitness)+", "+FloatToStr(poblacion[voy].fitness);

}*/

//calcular y enviar voltaje

if (CheckBoxpendulo->Checked)

{

voltaje=calculavoltajeconpendulo(posdes,posl,velocidad,pendulo,velopen);

}

else

{

voltaje=calculavoltajesinpendulo(posdes,posl,velocidad);

}

Sleep(5);

if (CheckBoxmover->Checked)

{

escribepuerto(0x378,voltaje);

}

else

{

escribepuerto(0x378,0x7f);

}

//actualizar informacion de graficas y etiquetas

Edit4->Text=FloatToStr(pendulo);

pos2=int(posl);

Edit3->Text=FloatToStr(posdes);

Edit1->Text=FloatToStr(posl);

Edit2->Text=Edit2->Text.IntToHex(voltaje,2);

TrackBar1->Position=voltaje;

TrackBar3->Position=posl*10;

posl=posl*2.5;

velocidad=velocidad*2.5+127;

pendulo=pendulo*15+127;

if (posl

{

if (posl

{

tres=posl;

ctres=clRed;

if (pendulo

{

dos=pendulo;

cdos=clGreen;

uno=velocidad;

cuno=clWhite;

}

else

{

dos=velocidad;

cdos=clWhite;

uno=pendulo;

cuno=clGreen;

}

}

else

{

tres=velocidad;

ctres=clWhite;

dos=posl;

cdos=clRed;

uno=pendulo;

cuno=clGreen;

}

}

else

{

if (posl

{

tres=pendulo;

ctres=clGreen;

dos=posl;

cdos=clRed;

uno=velocidad;

cuno=clWhite;

}

else

{

uno=posl;

cuno=clRed;

if (pendulo

{

tres=pendulo;

ctres=clGreen;

dos=velocidad;

cdos=clWhite;

}

else

{

tres=velocidad;

ctres=clWhite;

dos=pendulo;

cdos=clGreen;

}

}

}

Image1->Canvas->MoveTo(voygraf,0);

Image1->Canvas->Pen->Color=clBlack;

Image1->Canvas->LineTo(voygraf,255-uno);

Image1->Canvas->Pen->Color=cuno;

Image1->Canvas->LineTo(voygraf,255-dos);

Image1->Canvas->Pen->Color=cdos;

Image1->Canvas->LineTo(voygraf,255-tres);

Image1->Canvas->Pen->Color=ctres;

Image1->Canvas->LineTo(voygraf,255);

voygraf=(voygraf+1)%Image1->Width;

Image1->Canvas->MoveTo(voygraf,0);

Image1->Canvas->Pen->Color=clWhite;

Image1->Canvas->LineTo(voygraf,256);

if (pos2%2==0)

{

CheckBox16->Checked=false;

}

else

{

CheckBox16->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox15->Checked=false;

}

else

{

CheckBox15->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox14->Checked=false;

}

else

{

CheckBox14->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox13->Checked=false;

}

else

{

CheckBox13->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox12->Checked=false;

}

else

{

CheckBox12->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox11->Checked=false;

}

else

{

CheckBox11->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox10->Checked=false;

}

else

{

CheckBox10->Checked=true;

}

pos2=pos2/2;

if (pos2%2==0)

{

CheckBox9->Checked=false;

}

else

{

CheckBox9->Checked=true;

}

Done=false;

}

//–Agrega el valor de v en las posiciones———————————–

void TForm1::agregaapos(unsigned char v)

{

voypos=(voypos+1)%5;

pos[voypos]=v;

}

//–Regresa el promedio de la posición (quitar un poco de ruido————–

double TForm1::promediopos(void)

{

double suma=0;

int i;

for (i=0;i0)

{

calc=min(calc,maxvol);

}

else

{

calc=max(-maxvol,calc);

}

calc=127+12.7*calc;

r=int(calc);

return r;

}

Algoritmo genético

La manera de calcular el voltaje cuando se considera el péndulo es la siguiente fórmula:

edu.red

Donde x es la posición del carro en centímetros y alfa el ángulo de la varilla en grados. El problema es encontrar los valores del vector K=(k1,k2,k3,k4) que hagan que funcione de manera correcta el sistema. Para obtener estos valores utilice un algoritmo genético, en este, los individuos son vectores de cuatro dimensiones que representan los valores que se le asignan a K; la manera de evaluar cada individuo es dejar que trate de equilibrar la varilla minimizando la integral de alguna función, la función utilizada fue:

edu.red

La primera generación se inicia con valores cercanos a los recomendados en un manual (modificados con ruido uniforme). La cruza se hizo con dos padres, se daba un volado por cada valor del vector, es decir se obtenía un número aleatorio entre 0 y 1, si este era mayor que 0.5 se le asignaba el valor de un padre y en caso contrario el del otro padre. Para la mutación se aplicó una pequeña perturbación a los valores del vector, esta se hizo con ruido uniforme. El siguiente es el código del algoritmo genético:

//–Función para calcular un número con distribución uniforme en el intervalo [0,1]

double uniforme(void)

{

return double(random(123456))/123455.0;

}

//–Esta es la estructura que tiene cada individuo

struct Tindividuo

{

double k1,k2,k3,k4,fitness;

};

//–Este apuntador es el que guardara la población completa

Tindividuo *poblacion;

//Esta función realiza una mutación en un individuo.

void mutaindividuo(Tindividuo &ind)

{

if (uniforme()>=pm)

{

ind.k1=ind.k1+(uniforme()-0.5)/2;

}

if (uniforme()>=pm)

{

ind.k2=ind.k2+(uniforme()-0.5);

}

if (uniforme()>=pm)

{

ind.k3=ind.k3+(uniforme()-0.5)/2;

}

if (uniforme()>=pm)

{

ind.k4=ind.k4+(uniforme()-0.5)/2;

}

}

//–Esta función toma dos individuos p y m y genera uno nuevo a partir de estos

void cruzapareja(Tindividuo &p,Tindividuo &m,Tindividuo &h)

{

h=p;

if (uniforme()>=0.5)

{

h.k1=m.k1;

}

if (uniforme()>=0.5)

{

h.k2=m.k2;

}

if (uniforme()>=0.5)

{

h.k3=m.k3;

}

if (uniforme()>=0.5)

{

h.k4=m.k4;

}

}

//–Esta function es auxiliar, sirve como parámetro de la función qsort

int comparaindividuos(const void *ae, const void *be)

{

Tindividuo *a=(Tindividuo *)ae,

*b=(Tindividuo *)be;

if (a->fitnessfitness)return -1;

if (a->fitness==b->fitness)return 0;

return 1;

}

//–La selección solamente ordena los individuos por el fitness

void seleccion(void)

{

qsort(poblacion,mu+lambda,sizeof(Tindividuo),comparaindividuos);

}

//–Esta función genera lambda individuos nuevos mediante cruza y después los muta

void cruzaymuta(void)

{

for (int i=mu;i

{

cruzapareja(poblacion[random(mu)],poblacion[random(mu)],poblacion[i]);

mutaindividuo(poblacion[i]);

poblacion[i].fitness=0;

}

}

//–Esta es la función principal del algoritmo genético, es una modificación pues esta

//–ontiene el fitness dentro de otra función (el ciclo principal del programa)

void ag(double aumento,int tiempo)

{

if (trabajando)

{

if (enfitness)

{

if (tiempo>(tiempodeprueba-tiempoind))

{

tiempo=(tiempodeprueba-tiempoind);

}

poblacion[voy].fitness+=aumento*double(tiempo)/1000.0;

tiempoind+=tiempo;

if (tiempoind>=tiempodeprueba)

{

voy++;

tiempoind=0;

enfitness=false;

}

if (voy==(mu+lambda))

{

seleccion();

cruzaymuta();

voy=mu;

if (generacion%generacionesentreguardadas==0)

{

guardageneracion();

}

generacion++;

}

}

}

}

//–Esta función genera la población inicial

void generapoblacioninicial(void)

{

poblacion=new Tindividuo[mu+lambda];

for (int i=0;i < mu+lambda;i++)

{

poblacion[i].k1=0.26+uniforme()/2;

poblacion[i].k2=1.85+uniforme();

poblacion[i].k3=0.29+uniforme()/2;

poblacion[i].k4=0.19+uniforme()/2;

poblacion[i].fitness=0;

}

}

Después de 50 generaciones y con una población de 20+10 y evaluar 10 segundos cada caso, se obtuvo un individuo con un fitness de 25.05, este logra equilibrar la varilla colocando al carro no muy lejos del punto en el que se quiere que permanezca. Manualmente se exploraron algunos de los individuos de esta generación, el que mas me gusto fue este:

individuo 5:

k1=1.059460

k2=0.430551

k3=0.330167

k4=0.112087

fitness=28.277874

5.- Convertidor Digital-Analógico

El convertidor digital-analógico está hecho con dos circuitos integrado: el DAC0800 y el . Tiene una respuesta de -10 a +10 voltios. El diagrama del convertidor es el siguiente:

edu.red

6.- Diagrama

Este es el diagrama completo del proyecto:

edu.red

Enviado por: Ing.+Lic. Yunior Andrés Castillo S.

"NO A LA CULTURA DEL SECRETO, SI A LA LIBERTAD DE INFORMACION"®

Santiago de los Caballeros, República Dominicana, 2016.

"DIOS, JUAN PABLO DUARTE, JUAN BOSCH Y ANDRÉS CASTILLO DE LEÓN – POR SIEMPRE"®

 

 

 

Autor:

Sergio Ricardo Murguia Santana.