Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
Hola, ando programando el velocista para probar la placa de sensores con un 18f452 en C y el compilador de microchip, y como mi C está un poco olvidado me salen unas cuantas dudas sobre el tipo de variables que puedo usar.
Lo primero es que estoy usando variables de tipo float, da algún problema usar este tipo de variable en un 18f?
Luego estoy haciendo lo siguiente servo= 44 - errort*3;
Donde servo es un char, errort es un float, y en Delay100TCYx(servo); el argumento que va ahí es un unsigned char me parece, servo en la asignación va a salir siempre positivo.
Lo he puesto así directamente y parece ser que funciona... debo hacer algún tipo de conversión por el medio?
Adjunto todo el código, el regulador P os parece correcto?
Luego otra duda, si declaro las variables errors, errort, y contador dentro del main me da un error, lo soluciono poniendolas fuera, pero no sé el motivo.
Gracias. Saludos.
Video del funcionamiento:
http://www.youtube.com/watch?v=2lDU1N9XuXU /***************************************************************************************
****************************************************************************************
** Programa para probar la placa de sensores, regulador proporcional.
** Pic18f452
** Cristal 16 MHz
** 18/12/2008 www.jmnlab.com
****************************************************************************************
***************************************************************************************/
#include <p18f452>
#include <delays>
#include <timers>
#pragma config WDT=OFF, LVP=OFF, OSC=HS, OSCS=OFF, PWRT=ON, BOR=OFF, STVR=ON
void inicializar (void);
void reseteo (void);
void timer2_isr (void);
#pragma code high_vector=0x08 // high interrupt vector en 0008h
void interrupt (void)
{
_asm GOTO timer2_isr _endasm // Salta a la ISR
}
#pragma code // default code section
#define LED1 PORTEbits.RE0 // Ambar
#define LED2 PORTEbits.RE1 // Rojo
#define SENSORES PORTEbits.RE2 // Placa de sensores
#define MINIZ PORTBbits.RB4 // Control miniz
#define R7 PORTCbits.RC0 //Extremo derecho
#define R6 PORTCbits.RC1
#define R5 PORTCbits.RC2
#define R4 PORTCbits.RC3
#define R3 PORTDbits.RD0
#define R2 PORTDbits.RD1
#define R1 PORTDbits.RD2
#define R0 PORTDbits.RD3 // Centrales
#define L0 PORTCbits.RC4 //
#define L1 PORTCbits.RC5
#define L2 PORTCbits.RC6
#define L3 PORTCbits.RC7
#define L4 PORTDbits.RD4
#define L5 PORTDbits.RD5
#define L6 PORTDbits.RD6
#define L7 PORTDbits.RD7 // Extremo izquierdo
//Variables
unsigned char DelayCounter1;
char servo = 44, velocidad = 44;
int contador=0;
int errors=0;
float errort=0;
//Programa principal
void main (void)
{
inicializar();
reseteo();
LED1 = 1;
SENSORES=1;
while(1)
{
errors=0;
contador=0;
errort=0;
if(L7==1) // Asignación de error
{
errors+=7;
contador++;
}
if(L6==1)
{
errors+=6;
contador++;
}
if(L5==1)
{
errors+=5;
contador++;
}
if(L4==1)
{
errors+=4;
contador++;
}
if(L3==1)
{
errors+=3;
contador++;
}
if(L2==1)
{
errors+=2;
contador++;
}
if(L1==1)
{
errors+=1;
contador++;
}
if(L0==1)
{
errors+=0;
contador++;
}
if(R0==1)
{
errors+=0;
contador++;
}
if(R1==1)
{
errors+=(-1);
contador++;
}
if(R2==1)
{
errors+=(-2);
contador++;
}
if(R3==1)
{
errors+=(-3);
contador++;
}
if(R4==1)
{
errors+=(-4);
contador++;
}
if(R5==1)
{
errors+=(-5);
contador++;
}
if(R6==1)
{
errors+=(-6);
contador++;
}
if(R7==1)
{
errors+=(-7);
contador++;
}
if (contador!=0)
{
errort=errors/contador;
servo= 44 - errort*3; //servo=centro-error*constante proporcional
}
else
servo=44;
}
}
//***************************FUNCIONES****************************************
void inicializar (void)
{
TRISA = 0b11111111; // Configuración de los puertos
TRISB = 0b11101111; //
TRISC = 0b11111111; //
TRISD = 0b11111111; //
TRISE = 0b00000000; // Puerto E como salidas.
ADCON1 = 0b00001111; // Todo puerto A como digitales.
PORTB = 0;
PORTE = 0;
OpenTimer2 (TIMER_INT_ON & T2_PS_1_16 & T2_POST_1_16); //TMR2IF=0 TMR2IE=1 TMR2ON=1 TMR2=0
RCONbits.IPEN = 0;
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
PR2 = 250;
}
void reseteo (void)
{
INTCONbits.GIE = 0;
LED2 = 1; //3 parpadeos para detectar los posibles resets del pic
LED1 = 0;
Delay10KTCYx(200);
LED2 = 0;
Delay10KTCYx(200);
LED2 = 1;
Delay10KTCYx(200);
LED2 = 0;
Delay10KTCYx(200);
LED2 = 1;
Delay10KTCYx(200);
LED2 = 0;
INTCONbits.GIE = 1;
}
#pragma interrupt timer2_isr save=DelayCounter1 // interrupción, salvamos la variable de los delays
void timer2_isr ( void)
{
PIR1bits.TMR2IF = 0; //Limpia el flag de interrupción del timer2
MINIZ = 1;
Delay100TCYx(16); // 400 uS
MINIZ = 0;
Delay100TCYx(servo);
MINIZ = 1;
Delay100TCYx(16); //400 uS
MINIZ = 0;
Delay100TCYx(velocidad);
MINIZ = 1;
Delay100TCYx(16);
MINIZ = 0;
}
Si en esto tienes razón, de la forma que lo he hecho hubiese sido mejor un número impar de sensores. me evitaría tener que hacer la suma o resta de 15, 30, 45 según el caso para ser 100% exacto si no quiero usar float.
Pero aquí ya estamos contando pocos mm, y tb he tenido otra cosa en cuenta, que nunca va a haber un sólo sensor activo. Puede haberlo en otro diseño de placa, pero a cuenta de perder resolución si leermos en digital, según lo tengo voy con una resolución de 8mm aproximadamente.
De todas formas si tomo los dos sensores centrales como cero y uso float no hay ese problema, mi salida deseada es toda esa zona comprendida por los sensores centrales, no pasa nada porque intervengan los dos sensores centrales, yo puedo darme la libertad de definir donde está mi centro (puede ser lo ancho que yo quiera), no sé si me éxplico.
No sé hay que pensar en mil cosas, y todo esto para que luego me mate un rádio mínimo de curvatura, que ya lo estoy viendo XD. Pero hay un plan B en marcha...
Edito:
Y si no, cambia los valores para tener saltos iguales, por ejemplo:
-75 -45 -15 +15 +45 +75
pero de manera que entre sensor y sensor el salto de error sea el mismo.
No se, yo prefiero la solución de sensor central, pero cada uno...
Pues no sé que había hecho antes que con estos números me había salido una combinación con decimales, pero está bien y lo soluciona.
No sé lo mejor que puedo hacer es montar una pista en el salón cuando me abandonen por navidad y empezar a probar.
JM, me parece que tu lógica matemática es bastante buena. No he buscado maneras más sencillas de hacerlo, pero esta me parece buena, y sobre todo, entendible. Además del tema comentado por Dragonet, el resto me parece totalmente OK.
Sólo un pequeño detalle, y es que los pocos PID's que he visto, se ejecutan en una rutina de interrupción de un timer. Así se consigue la precisión temporal necesaria, pero es un motivo más para evitar la coma flotante y optimizar al máximo las rutinas matemáticas.
Al fin y al cabo, sumar, restar, y multiplicar son pocos ciclos máquina (si tienes multiplicador hard, claro), así que dividiendo por una potencia de dos, te deja la rutina muy compacta y rápida.
Hola:
Me acabo de acordar, que hace mucho mucho tiempo (en una galaxia muy, muy lejana ;)) se me ocurrió hacer la lectura de los sensores en serie. De esta manera podía reducir el consumo, ya que o bien tenía sólo uno encendido o sólo los encendía un pequeño tiempo mientras el latch paralelo-serie cogía el dato.
Entonces, en lugar de leer el byte/UInt16 entero, lo que hacía era contar cuantos ceros seguidos tenía, y luego seguir contando hasta que volvía a tener ceros.
Bueno a ver si me explico, que soy de ciencias: mientras no detecta nada, supongo que leo 0 en el bit de entrada. Supongamos que tengo 6 ceros seguidos. Guardo en una variable 6. Luego me vienen tres unos (es decir, que la línea me activa 3 sensores). Sigo contando hasta que en el puerto dejo de leer unos. Sale 9. Guardo el 9 y le sumo el 6. Y entonces tengo un valor que es directamente proporcional a la distancia (en sensores) desde el extremo hasta el centro de la línea. Y da igual que haya 3 como que haya 8 sensores activos a la vez.
Con 16 sensores, el valor nunca subirá de 31. Si multiplicamos por 8 (shift left tres bits), tenemos un valor de 0 a 255. 'Et voilà'. Un control tipo P inmune a los cambios de 'grosor' de la línea.
Como esto es bastante rápido, lo meto en una ISR que se ejecuta al reiniciarse el temporizador del PWM de los motores. Y lo bueno es que usando el segundo puente de un L293S/N754410 para accionar directamente el motor del servo (pasando de cualquier otra electrónica), podemos cerrar el lazo cada milisegundo, con lo que la respuesta es más rápida.
Convertir el control en PID es un juego de niños. Sintonizarlo no.
Yo lo pensaba hacer con el timer de 16 bits del ATmega16, donde una de las salidas del PWM sería para el motor de CC, y la otra para el servo. Modo CTC con el ICR del timer, reloj de 8MHz dividido por 8 a la entrada del timer, ICR a 1000, y el valor de salida del PWM puede variar de 0 a 999. Por supuesto, también se podría evitar dividir por 8, y tendríamos un rango de 0 a 7999 con una frecuencia de PWM de 1KHz.
Si alguien lo prueba, que me diga algo. Yo no podré hasta el verano, si todo sale bien.
Feliz Navidad.
La cosa de encender y apagar sensores en serie sólo es una ventaja si lees en analógico para quitar la luz ambiente, y entonces tienes que leer los valores de los adc para sacar la posición de la línea, son necesarios muchos menos sensores.
Yo llevo un mosfet que me enciende y apaga la placa de sensores, por lo que enciendo los sensores, espero un tiempo para que alcancen su valor, capturo la salida y apago los sensores, de esta forma reduzco el consumo.
16* I de tiempo mínimo es mi intensidad de encendido total, que es el mismo que si lo enciendo uno detrás de otro 16 veces, y de esta forma gano en tiempos.
Yo llevo un timer que salta cada 62.5 Hz, sólo puedo mandar información a la electrónica del coche cada 62.5 Hz, por lo que lo primero que hará la isr de esta interrupción será leer y calcular el error, espero que los tiempos me valgan para la D, pero bueno aún queda mucho programa para llegar aquí.
Lo que estoy viendo es que puede que al final leer en analógico si sea muy necesario.