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!
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.
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!
Transistor
Tim Wescott
Tim Wescott
Drew Fowler