En teoría , soy consciente de cómo funciona un controlador PID, nunca he implementado uno. Estoy implementando un método de control para conducir una válvula sobre PWM.
Detalles del caso de uso: el sistema tiene dos canales ADC, uno para entrada y otro para retroalimentación. La lectura de los canales ADC es libre y se toman suficientes muestras.
Implementación existente: hay un bucle infinito, que solo realiza dos trabajos: leer valores de ADC y generar PWM. Hay una interrupción del temporizador configurada para invocar a los 20 mseg. Entonces '¿Ha transcurrido el tiempo?' en el diagrama de flujo a continuación se evaluará 'Sí' después de cada 20 mseg. A continuación se muestra el diagrama de flujo de lo que estoy haciendo a partir de ahora.
El siguiente es el programa que estoy investigando:
/*
Some information on variables that are being used:
CURR_OUT_CH is Feedback channel
CMD_INP_CH is the channel where external input is applied.
So, ADC_Val.fADC_Final_mAVal[CURR_OUT_CH] is where I am receiving the value of feedback
And, ADC_Val.fADC_Final_mAVal[CMD_INP_CH ] is where I am receiving the value of external input that I am trying to achieve
MAX_ALLOWABLE_DUTY_CYCLE is a macro set to ((uint16_t)480) which Maps to 60% - This is a requirement.
(Op-Amp output is in mV, I convert it into mA based on resistor values)
(Config[chUser_Config_Mode].uiMaxCMD_I) is 350. (max current allowed through, in mA)
*/
#define RESTRICT(x, low, high) (x = (x)<(low)?(low):((x)>(high)?(x=high):(x)))
typedef struct {
float fFeedback;
float fOutput;
float Kp;
float Ki;
float fIntegralError;
float fSetpoint;
} PIControl_t;
PIControl_t PI;
uint16_t Load_Dutycount;
void PICompute(PIControl_t *pPI)
{
// I know that if PI is already a global, then taking the pointer doesn't make sense here,
// but, I may have to add another PI for a different sensor here, that is why I have used
// it this way!
// Instantaneous error is always local
float fError = 0.0;
// The classic PID error term
fError = pPI->fSetpoint - pPI->fFeedback;
// Compute the integral term
pPI->fIntegralError += (pPI->Ki * fError);
// Run all the terms together to get the overall output
pPI->fOutput = (pPI->Kp * fError) + (pPI->fIntegralError);
}
void Update_PWM_Module(void)
{
// Might want to get rid of this fCount, lets see.
float fCount = 0.0;
// Timer hasn't generated an interrupt yet (Integration time hasn't elapsed)
// ISR sets the bCompute variable - Flags are Not the best way, but does what it should.
// And, Timer doesn't start counting if bCompute is set
if(!bCompute)
{
// No control action needed, return!
return;
}
// Assign the feedback value read for PI output computation
PI.fFeedback = ADC_Val.fADC_Final_mAVal[CURR_OUT_CH];
// Compute the PI Controller output
PICompute(&PI);
// Formulate the value to be used to generate PWM
ADC_Val.fADC_Final_mAVal[CURR_OUT_CH] = ADC_Val.fADC_Final_mAVal[CURR_OUT_CH] + PI.fOutput;
// Map Output to no. of counts
fCount = (float) ((ADC_Val.fADC_Final_mAVal[CURR_OUT_CH] * MAX_ALLOWABLE_DUTY_CYCLE) / (float)(Config[chUser_Config_Mode].uiMaxCMD_I));
// Convert into compatible Duty Count type - uint16_t
Load_Dutycount = (uint16_t) fCount;
// Bound the output count between worst case lower and higher points
RESTRICT(Load_Dutycount, MIN_DUTY_CYCLE_COUNT, MAX_ALLOWABLE_DUTY_CYCLE);
// Generate PWM
Generate_PWM(Load_Dutycount);
// Assign the latest external input value read from ADC as the Setpoint for PI computation
PI.fSetpoint = ADC_Val.fADC_Final_mAVal[CMD_INP_CH] ;
// Not sure about this --- Because I think with a new Setpoint, the integrated error (which was developed based on previous Setpoints) will have no significance.
PI.fIntegralError = 0.0;
// Start integration all over again (Timer doesn't start counting if bCompute is set)
bCompute = false;
}
int main(void)
{
// Some code for Power-ON initialization like,
// ADC
// Timer
// PWM
// PI variables
// Everything else which needs one-time initialization before going into the infinite loop
while(1)
{
Read_ADC();
Update_PWM_Module();
}
}
Una vez que se genera el PWM, funciona libremente. El ciclo de trabajo permanecerá constante a menos que lo cambie, por lo que solo cambia periódicamente según el cálculo de PI.
En aras de la aclaración, cuando digo 'anular el valor del error integrado', quise decir pPI->integralError = 0.0;
en el programa C.
Declaración del problema: el tiempo total necesario para la ejecución del bucle cuando el temporizador no ha transcurrido es de aproximadamente 2 ms. Por supuesto, el tiempo de ejecución aumenta cuando se realiza el cálculo de PI y se invoca la función de generación de PWM.
Estoy probando las dos señales:
- Salida de la realimentación a la salida del amplificador operacional que se utiliza.
- Entrada al sistema.
Mi pregunta es, ¿el flujo operativo es correcto? ¿Estoy en lo correcto acerca de generar PWM solo después de que se realiza el cálculo de PI y restablecer el valor del error integrado a 0.0 cada vez que se asigna un nuevo punto de ajuste? Cuando se prueba con una entrada escalonada de 0-4 V, 0,5 Hz, en el osciloscopio veo que el sistema tarda unos 120 ms en aumentar su salida a entrada. Puedo correlacionar que los valores P e I tendrán que ajustarse para mejorar el tiempo. Esta publicación no se trata mucho de ajustar los valores de los factores P e I.
Lectura relacionada: Preguntas sobre electronics.stackexchange que he leído y están estrechamente relacionadas:
Yo: Pero no has explicado por qué estás reiniciando/anulando el término integral.
Weda: Porque pensé que con un nuevo punto de ajuste, el error integrado (que se desarrolló en base a los puntos de ajuste anteriores) no tendría importancia. Creo que aquí es donde tengo una confusión fundamental/falta de comprensión.
Te daré mi ejemplo de 'PI para principiantes' que parece ayudar a algunos en el trabajo:
Control solo proporcional
Figura 1. Respuesta del control de crucero solo P. Tenga en cuenta que la velocidad de consigna de 80 km/h nunca se alcanza.
Encendemos el control de crucero. Acelera a 70 km/h al 100 % del acelerador. Ya debería quedar claro que nunca alcanzaremos los 80 km/h porque con la resistencia a la rodadura y al viento no podemos mantener los 80 km/h con potencia cero. Digamos que se establece a 77 kph al 30% de potencia. Eso es lo mejor que podemos conseguir con el control P-only.
Control proporcional-integral
Figura 2. La respuesta con la adición del control integral.
Cuando se suma la acción integral, el término integral continúa aumentando a una tasa proporcional al error. Esto se puede ver en la curva integral de la Figura 2 como una alta tasa inicial de aumento debido al gran error inicial que cae a cero (línea de nivel) cuando finalmente se elimina el error.
Figura 3. La función clásica de control PID. Fuente: Wikipedia - Controlador PID .
Una cosa que me di cuenta bastante tarde en la vida fue que a medida que la acción integral corrige la salida, el error cae a cero, por lo que la contribución del control proporcional también cae a cero. La salida cuando el error es cero se mantiene puramente por la acción integral.
Tenga en cuenta que si el punto de referencia cambia o la carga cambia (el automóvil se encuentra con una colina o con viento en contra), el error cambiará a un valor distinto de cero, el control P aumentará inmediatamente desde cero y la acción integral continuará desde su valor actual. - no de cero.
Hay un simulador simple de Excel PI en Engineers Excel y esto puede ser útil. no se si es lo mejor
Spehro Pefhany
wedapashi
Transistor
wedapashi
pPI->integralError
a0.0
harry svensson
Transistor
wedapashi
Transistor
wedapashi
Lundin
Lundin
wedapashi
Lundin
wedapashi
Lundin
wedapashi
Abdel Aleem
Lundin
Abdel Aleem
Abdel Aleem
Lundin
Abdel Aleem
Lundin