autopid/pid_open_loop_models.h

253 lines
14 KiB
C
Raw Normal View History

2019-10-05 07:54:26 -07:00
<EFBFBD><EFBFBD>/*
* @file pid_open_loop_models.h
*
* Analytic plant models for the real measured step-response data of the open loop test.
* Models are used to calculate the estimated PID parameters.
* These parameters come from Taylor-McLaurin expansion of the model
*
* @date Sep 27, 2019
* @author andreika, (c) 2019
*/
#pragma once
#include "global.h"
#include "pid_functions.h"
// should be multiple of 2
#define MAX_DATA_POINTS 700
enum PidTuneMethod {
// 1st order
PID_TUNE_CHR1,
2019-10-11 11:14:56 -07:00
PID_TUNE_AUTO1,
2019-10-05 07:54:26 -07:00
// 2nd order approximated with 1st order
PID_TUNE_IMC2_1,
PID_TUNE_CHR2_1,
// 2nd order
PID_TUNE_CHR2,
PID_TUNE_VDG2,
PID_TUNE_HP2,
2019-10-11 11:14:56 -07:00
PID_TUNE_AUTO2,
2019-10-05 07:54:26 -07:00
};
// Used as an open-loop plant model for the "manual bump test" and as an input of a transfer function
class ModelOpenLoopPlant
{
public:
ModelOpenLoopPlant() {}
ModelOpenLoopPlant(const double *params_) : params((double *)params_) {
}
2019-10-11 11:14:56 -07:00
double *getParams() {
2019-10-05 07:54:26 -07:00
return params;
}
virtual float getKp() const = 0;
virtual float getKi() const = 0;
virtual float getKd() const = 0;
2019-10-05 07:54:26 -07:00
protected:
double *params;
};
// Based on FOPDT model approximated from Overdamped-SOPDT model
class ModelFopdtApproximatedFromSopdt : public ModelOpenLoopPlant {
public:
ModelFopdtApproximatedFromSopdt(double *soParams) : ModelOpenLoopPlant(p) {
double T2divT1 = soParams[PARAM_T2] / soParams[PARAM_T];
double T2mulT1 = soParams[PARAM_T2] * soParams[PARAM_T];
params[PARAM_K] = soParams[PARAM_K];
params[PARAM_T] = 0.828 + 0.812 * T2divT1 + 0.172 * exp(-6.9 * T2divT1) * soParams[PARAM_T];
params[PARAM_L] = 1.116 * T2mulT1 / (soParams[PARAM_T] + 1.208 * soParams[PARAM_T2]) + soParams[PARAM_L];
#ifdef PID_DEBUG
printf("Params-1: K=%g T=%g L=%g\r\n", p[PARAM_K], p[PARAM_T], p[PARAM_L]);
#endif
}
// we don't really need them, because this model is just an intermediate
virtual float getKp() const {
return 0.0f;
}
virtual float getKi() const {
return 0.0f;
}
virtual float getKd() const {
return 0.0f;
}
2019-10-05 07:54:26 -07:00
private:
// A storage for the new 1st order params
double p[3];
};
// Chien-Hrones-Reswick PID implementation for the 1st order model
class ModelChienHronesReswickFirstOrder : public ModelOpenLoopPlant {
public:
ModelChienHronesReswickFirstOrder(const double *params_) : ModelOpenLoopPlant(params_) {
}
virtual float getKp() const {
2019-10-05 07:54:26 -07:00
return (float)(0.6f / params[PARAM_K]);
}
virtual float getKi() const {
2019-10-05 07:54:26 -07:00
return (float)(1.0f / params[PARAM_T]);
}
virtual float getKd() const {
2019-10-05 07:54:26 -07:00
return (float)(1.0f / (0.5f * params[PARAM_L]));
}
};
// "IMC-PID" Rivera-Morari-Zafiriou implementation for the 1st order model
// See "Panda R.C., Yu C.C., Huang H.P. PID tuning rules for SOPDT systems: Review and some new results"
class ModelImcPidFirstOrder : public ModelOpenLoopPlant {
public:
ModelImcPidFirstOrder(const double *params_) : ModelOpenLoopPlant(params_) {
lambda = fmax(0.25 * params[PARAM_L], 0.2 * params[PARAM_T]);
Kc = (2 * params[PARAM_T] + params[PARAM_L]) / (2 * params[PARAM_K] * (lambda + params[PARAM_L]));
Ti = params[PARAM_T] + 0.5 * params[PARAM_L];
Td = params[PARAM_T] * params[PARAM_L] / (2.0 * params[PARAM_T] + params[PARAM_L]);
}
virtual float getKp() const {
2019-10-05 07:54:26 -07:00
return (float)Kc;
}
virtual float getKi() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc / Ti);
}
virtual float getKd() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc * Td);
}
private:
double lambda;
double Kc, Ti, Td;
};
// Based on "IMC-Chien" model: "Chien, I.L., IMC-PID controller design - An extension."
// "Proceedings of the IFAC adaptive control of chemical processes conference, Copenhagen, Denmark, 1988, pp. 147-152."
class ModelChienHronesReswickSecondOrder : public ModelOpenLoopPlant {
public:
ModelChienHronesReswickSecondOrder(const double *params_) : ModelOpenLoopPlant(params_) {
Ti = params[PARAM_T] + params[PARAM_T2];
Td = params[PARAM_T] * params[PARAM_T2] / Ti;
lamda = fmax(0.25 * params[PARAM_L], 0.2 * Ti);
Kc = Ti / (params[PARAM_K] * (lamda + params[PARAM_L]));
}
virtual float getKp() const {
2019-10-05 07:54:26 -07:00
return (float)Kc;
}
virtual float getKi() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc / Ti);
}
virtual float getKd() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc * Td);
}
protected:
// closed-loop speed of response
2019-10-05 07:54:26 -07:00
double lamda;
// "Standard" PID coefs
2019-10-05 07:54:26 -07:00
double Kc, Ti, Td;
};
// Basen on Van der Grinten Model (1963)
// "Step disturbance".
class ModelVanDerGrintenSecondOrder : public ModelOpenLoopPlant {
public:
ModelVanDerGrintenSecondOrder(const double *params_) : ModelOpenLoopPlant(params_) {
double T12 = params[PARAM_T] + params[PARAM_T2];
Ti = T12 + 0.5 * params[PARAM_L];
Td = (T12 * params[PARAM_L] + 2.0 * params[PARAM_T] * params[PARAM_T2]) / (params[PARAM_L] + 2.0 * T12);
Kc = (0.5 + T12 / params[PARAM_L]) / params[PARAM_K];
}
virtual float getKp() const {
2019-10-05 07:54:26 -07:00
return (float)Kc;
}
virtual float getKi() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc / Ti);
}
virtual float getKd() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc * Td);
}
protected:
double lamda;
double Kc, Ti, Td;
};
// Based on Haalman-Pemberton model: "Haalman, A.: Adjusting controllers for a deadtime process. Control Eng. 65, 71 73 (1965)"
// Suited for overdamped response and significant delay.
class ModelHaalmanPembertonSecondOrder : public ModelChienHronesReswickSecondOrder {
public:
ModelHaalmanPembertonSecondOrder(const double *params_) : ModelChienHronesReswickSecondOrder(params_) {
double T1divT2 = params[PARAM_T] / params[PARAM_T2];
double LdivT2 = params[PARAM_L] / params[PARAM_T2];
Ti = params[PARAM_T] + params[PARAM_T2];
Kc = 2.0 * Ti / (3 * params[PARAM_K] * params[PARAM_L]);
if (T1divT2 >= 0.1 && T1divT2 <= 1.0 && LdivT2 >= 0.2 && LdivT2 <= 1.0) {
Td = Ti / 4.0;
} else {
Td = params[PARAM_T] * params[PARAM_T2] / Ti;
}
}
};
#if 0
// Based on "IMC-Rivera/Smith" model:
// "Proceedings of the IFAC adaptive control of chemical processes conference, Copenhagen, Denmark, 1988, pp. 147-152."
class ModelRiveraSmithSecondOrder : public ModelOpenLoopPlant {
public:
ModelRiveraSmithSecondOrder(const double *params_) : ModelOpenLoopPlant(params_) {
Ti = params[PARAM_T] + params[PARAM_T2];
Td = params[PARAM_T] * params[PARAM_T2] / Ti;
lamda = fmax(0.25 * params[PARAM_L], 0.2 * Ti);
Kc = (params[PARAM_T] + params[PARAM_T2]) / (params[PARAM_K] * (lamda + params[PARAM_L]));
}
virtual float getKp() const {
2019-10-05 07:54:26 -07:00
//return (float)(Kc * (1.0 + Td / Ti));
return (float)Kc;
}
virtual float getKi() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc / Ti);
}
virtual float getKd() const {
2019-10-05 07:54:26 -07:00
return (float)(Kc * Td);
}
private:
double lamda;
double Kc, Ti, Td;
};
#endif
2019-10-11 11:14:56 -07:00
class ModelAutoSolver : public ModelOpenLoopPlant {
public:
ModelAutoSolver(const ModelOpenLoopPlant *initial) : ModelOpenLoopPlant(pidParams) {
pidParams[PARAM_Kp] = initial->getKp();
pidParams[PARAM_Ki] = initial->getKi();
pidParams[PARAM_Kd] = initial->getKd();
}
virtual float getKp() const {
return (float)pidParams[PARAM_Kp];
}
virtual float getKi() const {
return (float)pidParams[PARAM_Ki];
}
virtual float getKd() const {
return (float)pidParams[PARAM_Kd];
}
protected:
double pidParams[numParamsForPid];
};
2019-10-05 07:54:26 -07:00