Control de motor de escape PID

Tengo un PID que controla un motor de CC. Estoy tratando de controlar la velocidad del motor con mucha precisión. Mi controlador me permite cambiar la dirección del motor y darle un pwm de velocidad. Por lo tanto, tengo un PID que tiene un máximo y un mínimo de más y menos. Para acelerar el dispositivo y ralentizarlo lo suficientemente rápido. La salida del PID es para el pwm y, por lo tanto, es un valor absoluto del PID, simplemente cambiando un pin de dirección cuando PID < 0. Estoy usando la dirección opuesta del motor solo como sistema de frenado. Por lo tanto, el motor siempre debe ir en una dirección, pero debe reducir su velocidad más rápido aplicando un par inverso.

Estoy escribiendo firmware C en MCUXpresso. Los gráficos provienen del envío de datos a través de UART a un Arduino para graficar datos fácilmente.

Mi problema es que, a veces, cuando la variable de proceso llega a 0 o cerca de él, el PID se invierte y debe volverse negativo y, por lo tanto, hace girar el motor a toda velocidad en la dirección opuesta. Las dos imágenes a continuación muestran ciertos casos de cuándo sucedió. La línea roja es el punto de ajuste y la línea azul es la variable de proceso.

El código que controla el dispositivo y el PID se encuentra a continuación.

Me cuesta entender por qué el PID se escaparía de esta manera. Cualquier ayuda sería increíble. ¡Gracias!

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

control principal

int dir = FORWARD; //Controls direction of motor

motorPID.setpoint = vehicleSpeed;

motorPID.input = SM_GetRPM();

motorPID.input = motorPID.input * speedConversion;

UART_SendPID((uint8_t)motorPID.input, (uint8_t)motorPID.setpoint);

PID_Compute(&motorPID);

if(motorPID.output < 0){
    dir = BACKWARD;
}

if(motorPID.setpoint == 0){
    motorPID.output = 0;
}

if(motorPID.input > 60){
    MC_SetMotorSpeed(0, dir);
    int test = 0;
}

MC_SetMotorSpeed(abs(motorPID.output),dir);

Código PID

//Find all error variables
self->lastError = self->error;
double input = self->input;                         //Only so input can't change during compute
self->error = self->setpoint - input;
self->integral += self->error;
double derivative = self->error - self->lastError;

//Anti-integral Windup
if(self->integral > self->Outmax)
    self->integral = self->Outmax;
else if(self->integral < self->Outmin)
    self->integral = self->Outmin;

//Calculate PID
self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);

//Set limits
if(self->output > self->Outmax)
    self->output = self->Outmax;
else if(self->output < self->Outmin)
    self->output = self->Outmin;

EDITAR: Resulta que esto fue un error combinado del problema descrito y un problema de hardware.

Publique el código como texto en lugar de una captura de pantalla sin recortar del código. De esa manera será legible y podremos copiarlo y pegarlo para editarlo en las respuestas. Asegúrese de usar el botón de etiqueta de código y formatéelo/formatéelo correctamente. También puede mencionar cuál es el entorno de desarrollo y el lenguaje de programación.
Los grandes saltos repentinos en una señal como esta a menudo se deben a problemas con el desbordamiento de enteros. Si hay números enteros en la ruta de la señal (¿MC_SetMotorSpeed, tal vez?), Inspecciónelos o muéstrelos.
Su código PID se ve bien, excepto que debería estar incrementando self->integral con error * self->Ki (y no hacer esa multiplicación en la línea self->output), o debería estar comparando self->integral * self- >Ki con self->Outmin y self->Outmax.
@TimWescott Limitar la integral después de multiplicarla por Ki parece que este puede haber sido el problema. Agradezco su ayuda y debería haberlo captado antes. Gracias. Si pudiera poner su comentario en una respuesta, sería la solución por ahora.

Respuestas (2)

Intenta cambiar la línea que dice

self->integral += self->error;

a

self->integral += self-> Ki * self->error;

y haz coincidir eso cambiando la línea que dice

self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);

a

self->output = (self->Kp*self->error) + self->integral + (self->Kd * derivative);

Eso escalará el término integral correctamente para el paso limitante de su integrador.

a juzgar por sus gráficos, no creo que esté muestreando a una velocidad lo suficientemente alta como para lograr la estabilidad... Sin embargo, esta puede ser la limitación de arduino, ya que no estoy familiarizado con MCUExpresso.

Idealmente, debe tener una descripción matemática de su sistema, de modo que tenga una función de transferencia, luego puede leer su ancho de banda de la función de transferencia. Este ancho de banda también es la frecuencia de Nyquist, que es la frecuencia mínima "aceptable" (usando esa palabra vagamente) la muestra de una señal. Normalmente, desea muestrear alrededor de 30-40 veces la frecuencia de Nyquist. Si esto no es posible, sugiero cambiar los sensores.

Encontré problemas similares el semestre pasado en la universidad en mi proyecto, ¡espero que esto ayude!

Los gráficos solo muestran la velocidad a la que se envían los datos al arduino. No me preocupa el ancho de banda en esta solución, ya que se puede ajustar el tiempo de muestra en el PID.
Entonces, si entendí el gráfico y a usted mismo correctamente, ¿el número de muestra está en el eje x y el valor PWM está en el eje y?
Mis disculpas. Debería haberlo dejado claro. El eje x es el tiempo y el eje y es la velocidad del motor.