PID PARA CONTROL DE VELOCIDAD

Hola. Saludos para tod@s.
Estoy intentando desarrollar un control pid de velocidad utilizando la librería PID de Brett Beauregard, pero no consigo que funcione correctamente.
El motor lleva una reductora que le hace girar a 90rpm max., también lleva acoplado un encoder que me da 550pulsos por vuelta, y para controlarlo utilizo el L298N.
Utilizo la librería timer one para provocar una interrupción cada 200mSegs y leer la nueva velocidad. Nueva velocidad que obtengo a partir del contador de impulsos asociado al pin 2 de Arduino a través de las interrupciones por hardware.
Si alguien estuviese interesado en el tema, le agradecería su colaboración pues llevo ya invertido mucho tiempo en este proyecto y me fastidiaría dejarlo sin funcionar.
Ya he buscado información por google y por youtube, pero no he conseguido nada que resuelva mis dudas o problema.
Para cualquier duda que les pueda surgir, no duden en consultarla...
Saludos

PID_VELOCIDAD.ino (2.35 KB)

Lee las normas del foro, publica tu código con etiquetas como ahi se indica y espera a ser respondido.

Te consulto.. verificaste encoder, L298 todo por separado? Funciona supongo sin problemas?

Hola. Buenos días.
Ya he leído las normas del foro. No sé si me lo comentabas por algo en particular. De ser así, te agradecería que me lo aclarases...
Sí que es cierto que debería haber puesto el título de mi post en minúsculas. Lo siento.
Respecto a lo de publicar mi código con etiquetas, lo haré para la próxima; aunque habiendo adjuntado el skech completo, no le veo ningún inconveniente. Pero acepto la propuesta y así lo haré.
Finalmente, comentarle que sí que comprobé ya el correcto funcionamiento del L298N con otro sketch en el que simplemente mapeo una entrada analógica y la escribo en una salida analógica para controlar la velocidad del motor con un potenciómetro...
No sé si el problema podría venir por la lectura de pulsos del encoder y el posterior cáculo de la velocidad que utilizo como Input del PID. Aunque desde mi punto de vista está bien... Pero el caso es que no me funciona correctamente... :frowning:

Contactame por privado.

Os mando también el código editado, por si a alguien le resulta así más cómodo de analizar.
Saludos.

/********************************************************
 * PID Basic Example
 * Reading analog input 0 to control analog PWM output 3 
 * and conect the motor with the L298N driver.
 ********************************************************/
#include <TimerOne.h>
#include <PID_v1.h>
#define PIN_INPUT 0
#define PULSOS 2
#define PIN_OUTPUT 3

double Setpoint, Input, Output;    //Define Variables
volatile double veloc;
double Kp=8, Ki=0, Kd=0;          //Especifica el valor de los parámetros
int salida;
volatile int pulsos=0;
volatile int N;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

void PPR()                  //Incrementa la cuenta de pulsos cada vez que se aparece uno en el pin2
{
pulsos++;
}  
         
void calcula_veloc()
{noInterrupts();           // Suspende las interrupciones
 N=pulsos; pulsos=0;       // N pulsos cada 200ms o periodo definido en el timer
 interrupts();
 veloc = (N*30/55);      //(N*5*60/550)calculamos la velocidad en rev/min. El encoder da 550 PPR
 Input= veloc;       
}

void setup()
{
  pinMode(PULSOS,INPUT);
  pinMode(PIN_OUTPUT,OUTPUT);
  attachInterrupt(digitalPinToInterrupt(2), PPR, RISING);
  Timer1.initialize(200000);                            // Dispara cada 200mseg. para calcular las RPM
  Timer1.attachInterrupt(calcula_veloc);          // Activa la interrupcion 
  Serial.begin(9600);
  Setpoint = 80.0;
  myPID.SetOutputLimits(0.0,400.0);               //Establece los límites del PID para permitir los sobrepulsos del PID 
  myPID.SetMode(AUTOMATIC);                     //Activa el PID
}

void loop()
{
  Input= veloc;                            
  myPID.Compute();
  
  if ((Setpoint-Input)<0)
  {salida=0;}  
  
  if (Output>=250)
  {salida = 255;}
   
  if ((Output>0)&& (Output<90))         // 90rpm es la velocidad nominal del motor
  {salida= map(Output,0,90,0,255);}              
  analogWrite(PIN_OUTPUT, salida);
  Serial.print("PULSOS: ");     // Se imprimen un montón de variables para ver la evolución de las mismas 
  Serial.print(pulsos);           // cara a estudiar la razón del mal funcionamiento del sistema.
  Serial.print("N: ");
  Serial.print(N);
  Serial.print(";");
  Serial.print("  INPUT: ");
  Serial.print(Input);
  Serial.print("   OUTPUT: ");
  Serial.print(Output);
  Serial.print("   SALIDA: ");
  Serial.println(salida);
}

JELEMU:
El motor lleva una reductora que le hace girar a 90rpm max., también lleva acoplado un encoder que me da 550pulsos por vuelta, y para controlarlo utilizo el L298N.

Pero el encoder esta a la salida del reductor o en el eje del motor ? Tendría que estar en el eje.

Yo esto lo pondría así, ahorrando una división, y las interrupciones las activaría al final de la función

void calcula_veloc()
{noInterrupts();           // Suspende las interrupciones
 N=pulsos; pulsos=0;       // N pulsos cada 200ms o periodo definido en el timer

 veloc = (N * 0.5454);      //(N*5*60/550)calculamos la velocidad en rev/min. El encoder da 550 PPR

 Input= veloc;       

 interrupts();
}

Del PID no tengo experiencia, y no puedo comentar nada....
Saludos.

Gracias por intentar ayudarme, jordy.
No sé si el encoder va acoplado al eje primario o al eje de la reductora. Yo lo que sé es que si giro mi eje exterior una vuelta, obtengo 550 pulsos. Por eso digo que obtengo 550ppr.
He probado con tu aportación, pero el resultado es el mismo. El motor sigue girando como a tirones. Si que da la sensación que quiere aproximarse a mi setpoint, pero al ir a tirones, no consigue estabilizarse bien.

He pedido por internet un convertidor de frecuencia a tensión continua. A valores analógicos. El LM2907N. A ver si así, midiendo la V referencia de la velocidad (tipo tacómetro), y midiendo esa tensión en alguna entrada analógica, consigo mi objetivo...
Seguiremos informando...

Y con eso introduces otro problema!!! que es que si el LM2907 no se comporta como esperas tendras otra variable para resolver a tu ya complicado tema.

Has revisado todo por separado antes del PID?

Las RPM funcionan bien y estables?
el control del Motor lo mismo?

Utilizo la librería timer one para provocar una interrupción cada 200mSegs y leer la nueva velocidad. Nueva velocidad que obtengo a partir del contador de impulsos asociado al pin 2 de Arduino a través de las interrupciones por hardware.

Esto entrega valores estables?

Nota: cuando pregunto esto lo hago sin considerar el PID. Con PID neutralizado. Solo fijar una velocidad de motor y ver que dice sus RPM.

Podrías pasar un link de este grupo motor?

JELEMU:
No sé si el encoder va acoplado al eje primario o al eje de la reductora. Yo lo que sé es que si giro mi eje exterior una vuelta, obtengo 550 pulsos. Por eso digo que obtengo 550ppr.

Intuyo que tienes poca resolución, según tus números: haces una lectura cada 200ms
vel.max 90rpm x 550 ppr = 49500 pulsos por minuto / 60 segundos / 5 (200ms) = 164 pulsos en cada lectura.

Y el uso de double para variables que se han de comparar me resulta extraño. Lo has decidido tu? o es un requerimiento de esta implementación del pid ?

Y el uso de double para variables que se han de comparar me resulta extraño. Lo has decidido tu? o es un requerimiento de esta implementación del pid ?

Eso lo usa el PID.
Que amplie la ventana y punto.. a 1 segundo, no necesita compensar tan rapidamente a menos que el sistema sea muy dinámico.

Buenas noches:

Los PID necesitan bastante paciencia para buscar el proporcional, derivada e integral.
Kp=1 aumentarlo poco a poco (2, 3 ...) Ganancia
Ki=1 menor a 1,velocidad con la que se repite la acción proporcional
Kd=1 El valor indicado por la constante de derivación es el lapso durante el cual se manifestará la acción proporcional correspondiente a 2 veces el error y después desaparecerá (0.5, 1, 2...)

Saludos,

Hola surbyte.
Lo del LM2907 pensaba probarlo para ahorrarme el tener que leer pulsos y sus problemas. También podría prescindir de usar las interrupciones con timers para calcular la velocidad cada x ms. El control del motor y sus rpm sin PID, van bien.
Voy a asegurarme del tema de los PPR; porque yo medí con el osciloscopio 550 PPR y el link que os adjunto dice que da 823...

Seguiremos informando. Por cierto, gracias a los todos los interesados.

Por cierto, Jordi. Lo de utilizar variables tipo double, efectivamente, es por requerimiento de la librería PID.

Hola de nuevo. Perdón por la tardanza, pero ando muy liado con otras historias...
Ya he vuelto a cerciorarme de los PPR y efectivamente son 550.
Lo de ampliar la ventana a 1segundo, tampoco funciona. He probado con gran cantidad de valores de Kp, etc.
Con Kp=8, Ki=0.2 y Kd=0.1 oscila alrededor del setpoint pero no consigue estabilizarse. Oscila, pega tironcillos, continuamente. Ya esté el timer a un segundo, a medio segundo, o a un cuarto...
Ya no sé ni qué intentar. Creo que esperaré a ver si me llega el convertidor de frec a continua... :frowning: