Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
El Control PID estudiando un sistema Barra y Bola y utilizando como Controlador un Arduino. El
objetivo es situar la bola en el centro de la barra inclinándola de forma conveniente mediante un
lazo cerrado de control.
La realizamos mediante el sensor de distancia mediante luz infrarroja y detector PSD: SHARP
GP2Y0A21.
Sensor SHARP
Para filtrar (paso bajo) la señal del sensor y tener una señal más precisa y repetitiva,
conectaremos un condensador electrolítico de 10μF entre la salida del sensor y tierra.
De esta forma, los 1024 puntos que nos proporciona el Conversor Analógico Digital de 10 bit
del Arduino tendrán un fondo de escala de 3,3V en lugar de los 5V por defecto. Así,
incrementamos la resolución de 5mV/ADC a 3mV/ADC.
Serial.print(dist);
Con 9 puntos a lo largo de la barra es suficiente. Obtenemos la curva de calibración del sensor.
int ADCcal [] = {
y para transformar las lecturas ADC del sensor, almacenadas en la variable measure, en la
posición en mm de la variable dist, aplicamos el siguiente algoritmo:
for(int i =0; i<8; i++){ // Aplicamos curva de Calibracion de ADC a mm
dist = map(measure,ADCcal[i],ADCcal[i+1],dcal[i],dcal[i+1]);
La variable dist tiene valores negativos y positivos: -193mm en el extremo izquierdo de la barra,
120mm en el derecho y 0 en el centro. Como nuestro objetivo es dejar la bola en el punto
central, esta variable dist equivale al errorutilizado en la bibliografía de sistemas de control PID.
Actuador
Inclinaremos la barra con un Servo HEXTRONIK HX5010 de 6,9 kg.cm de par motor y una
biela de fibra de vidrio anclada a un extremo de la barra.
Actuador Servo
Como dice la Wikipedia, controlamos el giro del servo con pulsos de duración variable:
Control de la posición del servo mediante pulsos
En este servo en concreto la posición 0º se obtiene con pulsos de 0,5ms y el giro de 180º con
2,3ms.
Controlador
que son recibidas por un PC ejecutando una aplicación desarrollada en Processingy que nos
permitirá disfrutar de gráficas de la posición y la velocidad de la bola en función del tiempo
como ésta:
Gráfica de Processing
Como bonus, enciende un led conectado a la salida 13 cuando la bola está situada a
menos de 8mm del centro.
Alimentación de 5V
Control PID
Una vez que tenemos implementado el sistema físico, llega el momento de dar al controlador la
inteligencia necesaria para mover la barra de forma tal que consigamos nuestro objetivo: Dejar
la bola quieta en el centro de la barra.
Las secuencias de medida y reacción (los ciclos de programa) no se harán tan rápido como
pueda el microcontrolador, sino cada 50ms (valor almacenado en la variable period). Lo
hacemos así porque los sistemas de control PID funcionan mejor si los ciclos de medida y
reacción siempre tienen la misma duración.
El microcontrolador, con reloj de 16MHz, tiene velocidad suficiente para hacer el ciclo de
programa en menos de 10ms. Pero con ciclos tan rápidos, la medida de la velocidad de la bola
pierde precisión porque las diferencias en la posición de la bola entre ciclos son ínfimas y
nosotros calculamos la velocidad de la bola como la diferencia en su posición en 2 ciclos
consecutivos de programa.
Tras las pruebas realizadas, un período de 50ms aporta mediciones aceptables de la velocidad
de la bola y un funcionamiento del servo fluido.
Cálculo de la velocidad de la bola
v[i] =v[i+1];
vel=0;
vel = vel/nvel;
El valor de la velocidad lo utilizaremos para calcular la componente Derivativa del control PID.
Además lo enviamos a través del puerto serie / cable USB para que lo reciba el software
Processing y nos lo represente gráficamente.
Deteniendo la bola: El término Diferencial
El término Diferencial opera sobre la diferencia de posiciones entre el ciclo actual y el anterior.
Es decir, sobre la velocidad de la bola, ya que el tiempo transcurrido entre una medida y la
siguiente es siempre el mismo. Ese tiempo es el periodo de medida y reacción y en nuestro
sistema son 50ms.
El término diferencial se escribe en el software como:
pos=Kd*vel
En las 3 primeras líneas de programa definimos los valores de las 3 constantes del control PID:
float Kp =0;
float Kd = 100;
float Ki =0;
Lo que estamos haciendo es inclinar la barra oponiéndonos a la velocidad de la bola.
Comenzamos dando un valor cualquiera a Kd.
– ¿puedo darle el valor 10, que es mi número favorito?
– Sí, claro. Adelante.
Hemos puesto Kp=0 para que el término proporcional no participe y Kd=10 como punto de
partida. Se observa que es un valor insuficiente porque no reacciona suficiente como para
detener la bola.
Subimos a Kd=100: Sobre-reacciona y el sistema es inestable.
El valor correcto está entre 10 y 100. Probamos con 50, 25… observando el comportamiento
del sistema bajo control Derivativo. Un valor aceptable es Kd=35.
¡Conseguimos detener la bola con bastante eficacia!
Nuestro objetivo es detener la bola en el punto medio. El control proporcional acerca la bola al
centro inclinando la barra más cuanto más lejos esté. El control derivativo inclina la barra más
cuanto más rápido se mueva la bola y consigue detenerla. Observemos ahora la acción
conjunta de los 2 términos, definiendo la posición del servo como:
pos=Kp*dist+Kd*vel;
conclucion
La acción combinada de los término Proporcional y Derivativo es un control PD, suficiente para
muchas aplicaciones. Una de sus debilidades es que cuando la bola se ha detenido cerca del
punto central ya no reacciona. Como la velocidad es 0, el término diferencial no actúa. Como
está cerca del punto central, la inclinación de la barra por el término proporcional es pequeño y
puede no ser suficiente como para que la bola se mueva.
Código final
float Kp =2; //2
float Kd = 35; //35
float Ki =0.1; //0.1
int Rint = 8;
int Rext = 40;
unsigned long time = 0; // tiempo de ejecucion del ultimo ciclo
int period = 50; // Periodo de muestreo en ms
int sensorPin=0; //Pin Analogico donde esta conectada la señal del Sensor de distancia
int measure; // Lo que mide el sensor. Son ADCs.
int dcal [] = { // Calibracion de ADC a Distancia
-193, -160, -110, -60, 0, 40, 60, 90, 120};
int ADCcal [] = {
177, 189, 231, 273, 372, 483, 558, 742, 970};
int lastDist; // Valor anterior de Distancia para calcular la Velocidad
int dist; // distancia en mm con el 0 en el centro de la barra
int nvel=5; // numero de valores de velocidad sobre los que calculamos la media
int v[5];
int vel; // valor medio de las nvel velocidades ultimas
float I; // Valor Integral
#include <Servo.h>
Servo myservo; // create servo object to control a servo
float pos;
float reposo = 1350; // valor que mantiene la barra horizontal
int ledPin=13; // Pin de led verde.
void setup()
{
analogReference(EXTERNAL); // AREF conectado a 3.3V
myservo.attach(12); // attaches the servo on pin X to the servo object
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
}
void loop()
{
if (millis()>time+period){ // ¿Ha transcurrido el periodo?
time = millis();
// Medimos DISTANCIA
measure = analogRead(sensorPin);
measure = constrain(measure, ADCcal[0], ADCcal[8]);
lastDist = dist; // Guardamos el valor anterior de dist para calcular la velocidad
for(int i =0; i<8; i++){ // Aplicamos curva de Calibracion de ADC a mm
if (measure >= ADCcal[i] && measure< ADCcal[i+1]){
dist = map(measure,ADCcal[i],ADCcal[i+1],dcal[i],dcal[i+1]);
}
}
// Calculo de la media de la VELOCIDAD
for (int i=0; i<nvel-1; i++){ // Movemos todas hacia la izq para dejar libre la ultima.
v[i] =v[i+1];
}
v[nvel-1]= (dist - lastDist); // Ponemos un dato nuevo
vel=0;
for (int i=0; i<nvel; i++){ // Calculamos la media
vel = vel+ v[i];
}
vel = vel/nvel;
// Integral
if(abs(dist)>Rint && abs(dist)<Rext){ // Solo si esta dentro de (-Rext,Rext) y fuera de (-
Rint,Rint)
I=I+dist*Ki;
}
else {
I=0;
}
// Calculamos posicion del servo
pos=Kp*dist+Kd*vel+I;
myservo.writeMicroseconds(reposo+pos);