ПИД-регулятор DS18B20

ПИД регулятором называется уст-во с управляющей обратной связью. ПИД регулятор позволяет формировать управляющий сигнал для получения необходимой точности и качества переходного процесса. В частности температурный ПИД регулятор позволяет более оптимально поддерживать установленную температуру, устранить температурные колебания вызванные включением и выключением нагревательного элемента.

ПИД регулятор формирует управляющий сигнал состоящий из трех слагаемых:

  • Р — пропорциональная разница входного сигнала и сигнала обратной связи (сигнал рассогласования)
  • I — интегральная составляющая
  • D — дифференциальная составляющая

Если рассматривать все эти составляющие на примере температурного регулятора, то пропорциональная составляющая P состоит из разности текущей температуры и температуры регулирования.

P = (setpoint — currentpoint)

где: setpoint — температура регулирования, currentpoint — текущая температура

Чем больше разница между текущей температуры и температуры регулирования, тем больше сигнал рассогласования. По факту это значение основной мощности нагревательного элемента. При включении например печи, P составляющая имеет максимальное значение, при этом мощность нагревателя так же максимальна. Эта составляющая позволяет разогреть печь до температуры близкую к температуре регулирования, но при этом ни когда не сможет ее достичь, так как мощность нагревательного элемента пропорционально падает по мере приближения к заданной температуре регулирования. Далее нагревательная система стабилизируется при мощности, равной тепловым потерям.

Интегральная составляющая I — это основной элемент регулирования и поддерживания заданной температуры. Мощность нагревательного элемента необходимая для достижения заданной температуры после предварительного нагрева печи при помощи сигнала рассогласования (Р) берется именно из этой составляющей для ПИД сигнала.

I = (I + (setpoint — currentpoint) * dt)

где: setpoint — температура регулирования, currentpoint — текущая температура, dt — время опроса 

Интегральная составляющая пропорциональна интегралу по времени от отклонения регулируемой величины. Её используют для устранения статической ошибки. Она позволяет регулятору со временем учесть статическую ошибку.

По простому — интегральная составляющая просто копит сумму сигнала рассогласования умноженную на время опроса. По факту интегральная составляющая оценивает скорость нагрева или охлаждения печи, выдавая при это сигнал регулирования, при котором будут минимально возможные отклонения от заданной температуры.

Интегральная составляющая обладает небольшой инерцией, при быстром и не большом изменении температуры I составляющая не всегда успевает правильно среагировать на изменение температуры, эта составляющая может выдать слишком малый сигнал регулирования, что приведет к снижению температуры ниже допустимого значения или наоборот может выдать слишком большой сигнал регулирования, что приведет к превышению заданной температуры. Для устранения всех этих нюансов служит третья составляющая ПИД сигнала, это дифференциальная составляющая D.

Дифференцирующая составляющая пропорциональна темпу изменения отклонения регулируемой величины и предназначена для противодействия отклонениям от целевого значения, которые прогнозируются в будущем. Отклонения могут быть вызваны внешними возмущениями или запаздыванием воздействия регулятора на систему.

D = (((setpoint — currentpoint) — last_error) / dt)

где: setpoint — температура регулирования, currentpoint — текущая температура, last_error — величина сигнала рассогласования в предыдущем цикле измерения, dt — время опроса 

По простому — дифференциальная составляющая оценивает сигнал рассогласования и вычитает из него величину сигнала рассогласования в предыдущем цикле измерения, далее полученное значение делится на время опроса.

Например, если температура в печи в одном цикле измерения повысится на 0,05 °С, то составляющая D просто сравнит это сравнит текущую температуру цикла измерения с температурой цикла предыдущего измерения и получит значение ошибки в 0,05 °С. Эта ошибка повлечет за собой небольшую поправку ПИД сигнала, которая вызовет небольшой уменьшение мощности нагревательного элемента, что приведет к уменьшению (в идеале) температуры печи на 0,05°С. Тем самым составляющая D позволяет устранить незначительные колебания температуры.

Все три составляющие сигнала P I и D имеют свои коэффициенты — kp, ki и kd. От этих коэффициентов зависит режим работы ПИД регулятора. Эти коэффициенты могут иметь абсолютно разные значения для разных систем.

PID = (kp * P) + (ki * I) + (kd * D)

Как правильно подобрать эти коэффициенты будет написано ниже, а прежде для понимания работы ПИД регулятора необходимо собрать простой ПИД регулятор на базе Arduino.

В качестве датчика температуры используется цифровой датчик DS18B20. ПИД регулятор будет иметь диапазон регулирования от 0 до 100 °С. ПИД сигнал (D9) подается на светодиод (для зрительного наблюдения) и одновременно на управляющий элемент нагревателя.

ПИД сигнал это ШИМ сигнал низкой частоты (порядка 3 Гц), в процессе изменения ПИД сигнала меняется скважность импульса ШИМ сигнала.

Так же ПИД регулятор имеет три кнопки управления, нажимая кнопки UP и DW можно увеличить или уменьшить температуру регулирования. Кнопка SET позволяет изменить коэффициенты kp, ki и kd. Информация о работе ПИД регулятора выводится дисплей LCD1602 с модулем I2C.

Основной режим работы

Регулировка kp

Регулировка ki

Регулировка kd

Методик какие значения коэффициентов использовать достаточно много, немного разобравшись в теме я понял, что не все они подходят в каких то случаях и нуждаются в уточнении. Поэтому ниже я опишу свою методику, непосредственно используемую к терморегурятору описанному в этой статье.

После сборки ПИД регулятора установите коэффициенты kp равной 1, а ki и kd равными 0.

Коэффициент kd задает первоначальную мощность печи, при коэффициенте 1, фактически мощность (отображается на дисплее в %), будет близка к 0. Плавно увеличиваете значение этого коэффициент до момента когда в печи начнет расти температура. Полученное значение умножьте на 2, это и будет первоначальное значение коэффициента kp.

Далее самый важный коэффициент — ki. В основном от этого коэффициента зависит стабильность поддержания заданной температуры печи.

После того как коэффициент kp вывел печь в режим разогрева и выхода на стабильную температуру, медленно увеличьте коэффициент ki, до момента как начнет по понемногу расти мощность нагревателя. Если температура слишком долго растет до температуры регулирования, то увеличьте коэффициент ki, если наоборот нагрев печи слишком быстрый и перепады температуры нагрева и охлаждения печи выходят за допустимые границы температуры регулирования, то уменьшите коэффициент ki, а так же можно в этом случае немного увеличить kp.

Нужно найти компромисс, при котором печь будет выходить на заданную температуру достаточно быстро и при этом колебания температуры печи должны быть минимальными.

Как только удалось подобрать коэффициент ki и колебания температуры минимальны, можно приступить к коэффициенту kd.

С kd все просто, плавно увеличиваете величину kd, до момента когда колебания температуры станут очень маленьким или исчезнуть вовсе. При этом при каждом цикле измерения может наблюдаться цикличное изменение мощность на несколько процентов. Это говорит от том, что дифференциальная составляющая пытается скомпенсировать незначительные изменения температуры. Если изменение мощности (в %) при каждом цикле очень большое, от 10 и более %, это значит что kd очень большой.

Так же если при изменении температуры регулировки, текущая температура устанавливается на новое значение с большим перепадом (сильное превышение или занижение установленной температуры, а далее установка на нужное значение), то имеется смысл увеличить kp и уменьшить ki. Из-за того что kp мал, интегральная составляющая (ki * I) успевает накопить слишком большое значение пока идет выход на заданную температуру и не успевает его уменьшить до приемлемой величины, что вызывает большой перепад температуры.

Так как печь как правило имеет инертность, то при изменении коэффициентов нужно дать время, чтобы печь смогла среагировать на изменение параметров ПИД регулятора.

Форум — http://forum.rcl-radio.ru/viewtopic.php?id=636

Так же следует отметить, что в примере используется микроконтроллер LGT8F328 на тактовой частоте 16 МГц который имеет полную совместимость в данном примере с микроконтроллером Atmega328.

Скетч

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <OneWire.h>        // http://rcl-radio.ru/wp-content/uploads/2018/07/OneWire.zip
LiquidCrystal_I2C lcd(0x27,16,2);  // Устанавливаем дисплей
OneWire  ds(8); // Вход датчика 18b20

int nagrev,set,up,dw;
float temper,t,kpp = 1,kii = 0,kdd = 0;
unsigned long times,times1;
bool w;

void setup() {
  lcd.init();lcd.backlight();lcd.clear();
  Serial.begin(9600);
  pinMode(9,OUTPUT);
  cli();TCCR1A = 0;TCCR1B = 0;TCNT1 = 0;
  TCCR1A = 1 << COM1A1 |1 << WGM11;
  TCCR1B = 1 << WGM13 |1 << WGM12 |1 << CS12 | 1 << CS10;
  TIMSK1 |= (1 << OCIE1A);
  ICR1 = 5120; 
  OCR1A = 0; sei();
  pinMode(2,INPUT_PULLUP);// UP
  pinMode(3,INPUT_PULLUP);// DW
  pinMode(4,INPUT_PULLUP);// SET
  if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}
  t=EEPROM.read(1);
  kpp = EEPROM.read(0);if(kpp==0){kpp=1;}
  EEPROM.get(10, kii);
  EEPROM.get(20, kdd);
  delay(200);
}

void loop() {
  if(digitalRead(4)==LOW){set++;if(set>3){set=0;}}
  if(digitalRead(2)==LOW && set==0){if(up>10){t+=5;}else{t++;}up++;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==0){if(dw>10){t-=5;}else{t--;}dw++;w=1;times1=millis();}
  if(digitalRead(2)==HIGH){up=0;}if(digitalRead(3)==HIGH){dw=0;}
  if(t<0){t=0;}if(t>100){t=100;}

  if(digitalRead(2)==LOW && set==1){kpp++;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==1){kpp--;if(kpp<1){kpp=1;};w=1;times1=millis();}

  if(digitalRead(2)==LOW && set==2){kii+=0.01;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==2){kii-=0.01;if(kii<0){kii=0;};w=1;times1=millis();}
  
  if(digitalRead(2)==LOW && set==3){kdd++;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==3){kdd--;if(kdd<0){kdd=0;};w=1;times1=millis();}
  
  delay(50);
  
  lcd.setCursor(0, 0);lcd.print("T=");
  lcd.print(temper,1);lcd.print((char)223);lcd.print("C  ");
  lcd.setCursor(10, 0);lcd.print("P=");lcd.print(100.00/512.00*float(nagrev),0);;lcd.print("%  ");
  lcd.setCursor(0, 1);lcd.print("t=");lcd.print(t,0);lcd.print((char)223);lcd.print("C  ");  
  
  lcd.setCursor(8, 1);
  if(set==0){lcd.print("  PID    ");}
  if(set==1){lcd.print("kp=");lcd.print(kpp,0);lcd.print("   ");}
  if(set==2){lcd.print("ki=");lcd.print(kii,2);lcd.print("   ");}
  if(set==3){lcd.print("kd=");lcd.print(kdd,0);lcd.print("   ");}

  if(millis()-times1>10000 && w==1){w=0;EEPROM.update(0,kpp);EEPROM.update(1,t);EEPROM.put(10, kii);EEPROM.put(20, kdd);
  lcd.setCursor(8, 1);lcd.print("   SAVE   ");delay(300);
  }
}

ISR(TIMER1_COMPA_vect){
  _delay_ms(50);temper = dsRead(0);_delay_ms(50);
  nagrev = computePID(temper, t, kpp, kii, kdd, 0.3, 0, 512);
  OCR1A = nagrev*10;   
  }

int computePID(float currentpoint, float setpoint, float kp, float ki, float kd, float dt, int minOut, int maxOut) {
  static float last_error;
  float P;
  float D;
  static float I;
  signed int PID;
  P = (setpoint - currentpoint);
  I = (I + (setpoint - currentpoint) * dt);
  if(I>511){I=511;}if(I<-511){I=-511;}
  D = (((setpoint - currentpoint) - last_error) / dt);
  last_error = setpoint - currentpoint;
  PID = (kp * P) + (ki * I) + (kd * D);
  return constrain(PID, minOut, maxOut);
}

float dsRead(byte x) {
  byte data[2], addr[8][8], kol = 0;
  while (ds.search(addr[kol])) {  // поиск датчиков, определение адреса и кол-ва датчиков
    kol++;
  } 
  ds.reset_search();  // Сброс поиска датчика
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0x44, 0);  // Измерение температуры с переносом данных в память
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0xBE);     // Обращение памяти
  data[0] = ds.read();// Чтение памяти byte low
  data[1] = ds.read();// Чтение памяти byte high
  float value = ((data[1] << 8) | data[0]) / 16.0; return (float)value; // Расчет температуры и вывод
}

Добавить комментарий

Войти с помощью: