Пт май 13, 2022 11:17:45
float Kp = 71.5;
float Ki = 5.7;
float Kd = 1.368;
volatile float PreError = 0;
volatile float Integral = 0;
volatile float Derivative = 0;
float PID_Regulate(float input, float setpoint, float dt, float min, float max) {
float P = 0;
float I = 0;
float D = 0;
float out = 0;
float error = 0;
error = setpoint - input;
P = Kp*error;
Integral += error * dt;
I = Ki*Integral;
Derivative = (error - PreError)/dt;
D = Kd*Derivative;
out = P + I + D;
// Restrict to max/min
if( out > max )
out = max;
else if( out < min )
out = min;
PreError = error;
return out;
}
Пт май 13, 2022 11:54:07
Пт май 13, 2022 12:02:55
Пт май 13, 2022 12:08:38
Пт май 13, 2022 12:31:37
если понимаешь что делаешь и чем рулишь..
void TIM4_IRQHandler(void) {
uint8_t i;
TIM4->SR &=~ TIM_SR_UIF; // Clear TIM4 interrupt flag.
// Calculating average of 16 meansurements ******************************************
ReadedTemp = 0;
for (i = 0; i < 16; i++) {
ReadedTemp += ((SPI_Get32bit() >> 18)/4);
}
ReadedTemp = ReadedTemp/16;
// **********************************************************************************
Power = (uint32_t)(1000 -(PID_Regulate(ReadedTemp, TIM1->CNT, 0.01, 0, 1000)));
TIM2->CCR3 = Power; // Задержка перед передним фронтом ответного импульса на симистор.
TIM2->ARR = TIM2->CCR3 + 5; // Задний фронт = передний фронт + 5 тактов.
}
Пт май 13, 2022 13:12:19
примерно это я и хотел сказать (по крайней мере про I)Lum1noFor писал(а):ибо же коэффициенты при нагреве и в режиме стабилизации параметра должны быть разные?
Пт май 13, 2022 13:29:20
Пт май 13, 2022 13:48:06
float Kp = 71.5;
float Ki = 5.7;
float Kd = 1.368;
volatile float PreError = 0;
volatile float Integral = 0;
volatile float Derivative = 0;
float PID_Regulate(float input, float setpoint, float dt, float min, float max) {
float P = 0;
float I = 0;
float D = 0;
float out = 0;
float error = 0;
error = setpoint - input;
P = Kp*error;
I += error * dt * Ki;
Derivative = (error - PreError)/dt;
D = Kd*Derivative;
out = P + I + D;
// Restrict to max/min
if( out > max )
{
I+=max-out;
out = max;
}
else if( out < min )
{
I+=min-out;
out = min;
{
PreError = error;
return out;
}
можно, можно просто при большой ошибке принудительно вычищать накопитель... но решение это несколько "угловатое"Lum1noFor писал(а):А возможно ли сделать так - запускать нагрев при Ki = 0, а включать его уже после "грубого" нагрева?
не, надо не в градусах, а в единицах измерения... если у тебя температура измеряется с точностью 0.1 градуса, то градус - это уже 10 единиц... (тут не важно с какой точностью оно на экран выходит, а важно с какой точностью оно измеряется и в ПИД передается)Lum1noFor писал(а):за 10 мсек, действительно, температура будет меняться всего на 1-3 градуса.
Пт май 13, 2022 13:49:57
не, надо не в градусах, а в единицах измерения... если у тебя температура измеряется с точностью 0.1 градуса, то градус - это уже 10 единиц... (тут не важно с какой точностью оно на экран выходит, а важно с какой точностью оно измеряется и в ПИД передается)
Пт май 13, 2022 13:51:57
Пт май 13, 2022 14:04:07
Пт май 13, 2022 14:16:07
Пт май 13, 2022 14:18:01
Пт май 13, 2022 14:20:04
Пт май 13, 2022 14:45:36
typedef unsigned long u32;
typedef signed long s32;
#define shift(a, b) (((b) >= 0) ? (a) << (b): (a) >> -(b))
#define ASIGN32(x, y) (((s32)(x) ^ (s32)(y) >> 31) - ((s32)(y) >> 31)) //при y>=0 result == x; при y<0 result == -x
#define roundU32(x) ((u32)((x) + .5))
//ПИ-регулятор
#define FOC_QPNT 25 //положение дес.точки для вычислений ПИ-регулятора
#define FOC_QPNT_ACT 28 //положение дес.точки для входных значений ПИ-регулятора
#define CONFIG_MAX_PID_kp 16.0 //макс.возможное значение Kp (невкл.)
s32 focd.cfg.pid.id.kp; //коэфф.усиления пропорциональной составляющей; UQ1.31, нормированое к CONFIG_MAX_PID_kp
s32 focd.cfg.pid.id.ki; //коэфф.усиления интегральной составляющей; SQ1.31 * -1 (меньше 1)
s32 ref; //целевое значение
s32 in; //текущее входное значение
s32 err, i, j;
//вычисление ошибки
err = ref - shift(in, FOC_QPNT - 28); //SQ.FOC_QPNT
//вычисление I-составляющей
j = __QDSUB(focd.pid.d.outi, __SMMUL(focd.cfg.pid.id.ki, err)); //IOut(k) = IOut(k-1) + Ki * err; SQ.FOC_QPNT
//ограничение I-составляющей (симметричное ограничение)
i = focd.cfg.pid.id.limI;
if ((u32)(j + i) > (u32)i * 2) j = ASIGN32(i, j);
focd.pid.d.outi = j; //SQ.FOC_QPNT;
//вычисление полного результата
j = __SMMLA(focd.cfg.pid.id.kp, err * (s32)roundU32(CONFIG_MAX_PID_kp), j); //Out(k) = Kp * err + IOut(k); SQ.(FOC_QPNT-1)
//ограничение полного результата (симметричное ограничение)
i = focd.cfg.pid.id.lim;
if ((u32)(j + i) > (u32)i * 2) j = ASIGN32(i, j);
j <<= 1; //SQ.FOC_QPNT
//здесь в j = результат с точкой в позиции FOC_QPNT
Пт май 13, 2022 14:51:41
Пт май 13, 2022 15:00:53
Пт май 13, 2022 15:08:52
Пт май 13, 2022 15:46:46
Во-вторых: Здесь интегральную составляющую лучше считать так:
Integral += error * Ki;
Далее - ограничение. Это и будет I.
Integral += error * dt;
if (Integral > max/Ki) {
Integral = max/Ki;
} else if (Integral < -max/Ki) {
Integral = -max/Ki;
}
I = Ki*Integral;
Пт май 13, 2022 16:09:40