Salida de PID (Derivado Integral Proporcional) de escalamiento

8

He implementado una función PID usando la fórmula,

correction = Kp * error + Kd * (error - prevError) + kI * (sum of errors)

¿Qué debo hacer para mantener mi salida entre un cierto rango? diga 0-255 Si ignoro algún valor que no esté entre 0 y 255, ¿se produce un comportamiento irregular?

    
pregunta Hamza Yerlikaya

4 respuestas

6

Por lo general, limito el término integral (suma de errores) y, si no puede manejar el timbre, debe eliminar la ganancia para que el sistema quede saturado. También asegúrese de que sus variables de error, prevError y (suma de error) sean variables más grandes que no se recorten ni desborden.

Cuando simplemente recorta la corrección y luego la reincorpora al siguiente término de error, se producirá una no linealidad y el bucle de control obtendrá una respuesta de escalón cada vez que haga un recorte, lo que provocará su comportamiento irregular.

    
respondido por el Rex Logan
6

Debes manejar dos problemas:

  1. desbordamiento aritmético
  2. liquidación del integrador

El desbordamiento aritmético es bastante sencillo: siempre que estés haciendo cálculos enteros, asegúrate de usar valores intermedios de mayor ancho: por ejemplo, si a y b son de 16 bits, y agregas / restarlos, usar un valor intermedio de 32 bits y limitarlo al rango de un valor de 16 bits (0 a 65535 para los no firmados, -32768 a 32767 para los firmados) antes de volver a convertir a 16 bits. Si está absolutamente seguro de que nunca podrá desbordarse, porque está absolutamente seguro de los rangos de las variables de entrada, puede omitir este paso, pero tenga cuidado.

El problema de la liquidación del integrador es más sutil. Si tiene un gran error durante un período de tiempo prolongado, de modo que alcance el límite de saturación de la salida de su controlador, pero el error sigue siendo distinto de cero, entonces el integrador continuará acumulando errores, posiblemente aumentando su tamaño. estado estable. Una vez que el controlador sale de la saturación, el integrador tiene que volver a caer, causando un retraso innecesario y posiblemente una inestabilidad en la respuesta de su controlador.

En otra nota:

Lo recomendaría encarecidamente (sí, sé que esta pregunta tiene 18 meses de edad, por lo que probablemente haya terminado con su tarea, pero para beneficio de los lectores, supongamos que no lo es) que calcule el término integral de manera diferente: En lugar de Ki * (error integrado), calcule la integral de (Ki * error).

Hay varias razones para hacerlo; puede leerlos en una publicación de blog que escribí sobre cómo implementar los controladores PI correctamente .

    
respondido por el Jason S
4

Un par de mejoras que tal vez quieras considerar:

  • genere los términos I y D adecuados utilizando los filtros adecuados en lugar de usar sumas y diferencias (de lo contrario, será muy propenso al ruido, los problemas de precisión y otros errores). NB: asegúrese de que su término tenga suficiente resolución.

  • defina una banda de propiedades fuera de la cual los términos D e I están desactivados (es decir, control proporcional solo fuera de la banda de propiedades, control PID dentro de la banda de propiedades)

respondido por el Paul R
2

Bueno, como dijo Jason S, esta pregunta es antigua :). Pero a continuación es mi enfoque. He implementado esto en un PIC16F616 que se ejecuta en un oscilador interno de 8MHz, usando el compilador XC8. El código debería explicarse en los comentarios, si no, pregúntame. Además, puedo compartir todo el proyecto, como lo haré en mi sitio web más adelante.

/*
 * applyEncoder Task:
 * -----------------
 * Calculates the PID (proportional-integral-derivative) to set the motor
 * speed.
 *
 * PID_error = setMotorSpeed - currentMotorSpeed
 * PID_sum = PID_Kp * (PID_error) + PID_Ki * ∫(PID_error) + PID_Kd * (ΔPID_error)
 *
 * or if the motor is speedier than it is set;
 *
 * PID_error = currentMotorSpeed - setMotorSpeed
 * PID_sum = - PID_Kp * (PID_error) - PID_Ki * ∫(PID_error) - PID_Kd * (ΔPID_error)
 *
 * Maximum value of PID_sum will be about:
 * 127*255 + 63*Iul + 63*255 = 65500
 *
 * Where Iul is Integral upper limit and is about 250.
 * 
 * If we divide by 256, we scale that down to about 0 to 255, that is the scale
 * of the PWM value.
 *
 * This task takes about 750us. Real figure is at the debug pin.
 * 
 * This task will fire when the startPID bit is set. This happens when a
 * sample is taken, about every 50 ms. When the startPID bit is not set,
 * the task yields the control of the CPU for other tasks' use.
 */
void applyPID(void)
{
    static unsigned int PID_sum = 0; // Sum of all PID terms.
    static unsigned int PID_integral = 0; // Integral for the integral term.
    static unsigned char PID_derivative = 0; // PID derivative term.
    static unsigned char PID_error; // Error term.
    static unsigned char PID_lastError = 0; // Record of the previous error term.
    static unsigned int tmp1; // Temporary register for holding miscellaneous stuff.
    static unsigned int tmp2; // Temporary register for holding miscellaneous stuff.
    OS_initializeTask(); // Initialize the task. Needed by RTOS. See RTOS header file for the details.
    while (1)
    {
        while (!startPID) // Wait for startPID bit to be 1.
        {
            OS_yield(); // If startPID is not 1, yield the CPU to other tasks in the mean-time.
        }
        DebugPin = 1; // We will measure how much time it takes to implement a PID controller.


        if (currentMotorSpeed > setMotorSpeed) // If the motor is speedier than it is set,
        {
            // PID error is the difference between set value and current value.
            PID_error = (unsigned char) (currentMotorSpeed - setMotorSpeed);

            // Integrate errors by subtracting them from the PID_integral variable.
            if (PID_error < PID_integral) // If the subtraction will not underflow,
                PID_integral -= PID_error; // Subtract the error from the current error integration.
            else
                PID_integral = 0; // If the subtraction will underflow, then set it to zero.
            // Integral term is: Ki * ∫error
            tmp1 = PID_Ki * PID_integral;
            // Check if PID_sum will overflow in the addition of integral term.
            tmp2 = 0xFFFF - tmp1;
            if (PID_sum < tmp2)
                PID_sum += tmp1; // If it will not overflow, then add it.
            else
                PID_sum = 0xFFFF; // If it will, then saturate it.

            if (PID_error >= PID_lastError) // If current error is bigger than last error,
                PID_derivative = (unsigned char) (PID_error - PID_lastError);
                // then calculate the derivative by subtracting them.
            else
                PID_derivative = (unsigned char) (PID_lastError - PID_error);
            // Derivative term is : Kd * d(Δerror)
            tmp1 = PID_Kd * PID_derivative;
            // Check if PID_sum will overflow in the addition of derivative term.
            if (tmp1 < PID_sum) // Check if subtraction will underflow PID_sum
                PID_sum -= tmp1;
            else PID_sum = 0; // If the subtraction will underflow, then set it to zero.

            // Proportional term is: Kp * error
            tmp1 = PID_Kp * PID_error; // Calculate the proportional term.
            if (tmp1 < PID_sum) // Check if subtraction will underflow PID_sum
                PID_sum -= tmp1;
            else PID_sum = 0; // If the subtraction will underflow, then set it to zero.
        }
        else // If the motor is slower than it is set,
        {
            PID_error = (unsigned char) (setMotorSpeed - currentMotorSpeed);
            // Proportional term is: Kp * error
            PID_sum = PID_Kp * PID_error;

            PID_integral += PID_error; // Add the error to the integral term.
            if (PID_integral > PID_integralUpperLimit) // If we have reached the upper limit of the integral,
                PID_integral = PID_integralUpperLimit; // then limit it there.
            // Integral term is: Ki * ∫error
            tmp1 = PID_Ki * PID_integral;
            // Check if PID_sum will overflow in the addition of integral term.
            tmp2 = 0xFFFF - tmp1;
            if (PID_sum < tmp2)
                PID_sum += tmp1; // If it will not overflow, then add it.
            else
                PID_sum = 0xFFFF; // If it will, then saturate it.

            if (PID_error >= PID_lastError) // If current error is bigger than last error,
                PID_derivative = (unsigned char) (PID_error - PID_lastError);
                // then calculate the derivative by subtracting them.
            else
                PID_derivative = (unsigned char) (PID_lastError - PID_error);
            // Derivative term is : Kd * d(Δerror)
            tmp1 = PID_Kd * PID_derivative;
            // Check if PID_sum will overflow in the addition of derivative term.
            tmp2 = 0xFFFF - tmp1;
            if (PID_sum < tmp2)
                PID_sum += tmp1; // If it will not overflow, then add it.
            else
                PID_sum = 0xFFFF; // If it will, then saturate it.
        }

        // Scale the sum to 0 - 255 from 0 - 65535 , dividing by 256, or right shifting 8.
        PID_sum >>= 8;

        // Set the duty cycle to the calculated and scaled PID_sum.
        PWM_dutyCycle = (unsigned char) PID_sum;
        PID_lastError = PID_error; // Make the current error the last error, since it is old now.

        startPID = 0; // Clear the flag. That will let this task wait for the flag.
        DebugPin = 0; // We are finished with the PID control block.
    }
}
    
respondido por el abdullah kahraman

Lea otras preguntas en las etiquetas