помощь в ПИД

balomut
Offline
Зарегистрирован: 30.05.2018

Всем привет !!

 Есть рабочий контроллер Электрокотла с 3 тэнами . Надоело менять тэны из-за того что один постоянно в работе . Хочу переделать на Пид регулировку , что бы все 3 тэна всегда были в работе , но не получается сделать включение и выключение котла с ПИД. Помогите советом .

код ..









/*Программа для автоматики управления тэнами и насосом ТТ котла


Входа аналоговые :
А0-вход для подключения клавиатуры
А4 - подключение дисплея I2C
А5 - подключение дисплея


Входа дискретные:
D2 -вход шины OneWire для подключения цифрового датчика температуры D18B20 СО
D3 -вход шины OneWire для подключения цифрового датчика температуры D18B20 помещения
Выхода:
D5 - выход на подключение насоса СО
D9 - выход на реле  тэна 100%(шим)
D10 -  выход на реле  тэна 66%
D11 -  выход на реле  тэна 33%
D6 - бипер
*/

//подключаем библиотеки
#include <Wire.h>
#include <EEPROM2.h>
#include <LCD_1602_RUS.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <PID_v1.h>
#include <CyberLib.h>
 
                        // Установка переменных PID;
double Setpoint, Input, Output;
double aggKp=4, aggKi=0.2, aggKd=1;       // Определяем агрессивные и консервативные параметры PID;
double consKp=1, consKi=0.05, consKd=0.25;
                        // Определяем указатели и другие параметры PID;
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);

//Подключаем LCD-дисплей
LCD_1602_RUS lcd(0x27,16,2);

// Термометр котла будет подключен на Pin2
// Термометр комнаты будет подключен на Pin3
OneWire oneWire(2);//котел
OneWire oneWireOut(3);//Комната
//Создаем объект sensors, подключенный по OneWire
DallasTemperature sensors(&oneWire);
DallasTemperature sensorsOut(&oneWireOut);


//Создаем переменные для работы с термометром
DeviceAddress tempDeviceAddress;  //переменная для хранения адреса датчика
DeviceAddress tempDeviceAddressOut;



  
//текущая температура теплоносителя
float temp1 = 0;
// текущая температура в помещении
float temp2 = 0;
//заданые температуры
int setTmp = 33; // температура поддержания теплоносителя
int setTmp1 = 32; // температура включения насоса отопления
int setTmp2 = 28; // температура выключения насоса отопления
int setTmp3 = 32; // температура в помещении
int setLed=0; // Таймер подсветки
int soundPin = 6; 
// объявляем переменную с номером пина, на который мы подключили пьезоэлемент 
  

//Реле подключено к пину D9, D10, D11, D13
#define RELAY_PIN1 11// 33% 1кВт
#define RELAY_PIN2 10// 66% 2кВт
#define RELAY_PIN3 9// 100% 3кВт
#define RELAY_PIN4 5// насос циркуляционный
//Подсветка управляется через пин D12

#define BACKLIGHT_PIN 12

//Создаем переменную для хранения состояния подсветки
boolean backlightStatus = 1;

//булевые переменые
boolean pusk = LOW;                //Объявим переменную для хранения команды пуск
boolean avaria=LOW;                // объявим переменую для хранения состояния аварии

//Объявим переменную для хранения состояния реле
boolean relayStatus33=LOW;
boolean relayStatus66=LOW;
boolean relayStatus100=LOW;
boolean Vklnasos=LOW;
boolean termostat = LOW;                //Объявим переменную для хранения включения термостата.

// лонг переменые для работы таймеров
unsigned long currentTime;       // текущее время

//Объявим переменные для задания задержки
long previousMillis1 = 0;
long interval1 = 1000; // интервал опроса датчиков температуры

//Аналоговая клавиатура подключена к пину A0
#define KEYPAD_PIN A0
//Определим значения на аналоговом входе для клавиатуры 
#define ButtonUp_LOW 130
#define ButtonUp_HIGH 200
#define ButtonDown_LOW 350
#define ButtonDown_HIGH 400
#define ButtonLeft_LOW 550
#define ButtonLeft_HIGH 650
#define ButtonRight_LOW -2
#define ButtonRight_HIGH 10
#define ButtonSelect_LOW 850
#define ButtonSelect_HIGH 890


void setup()
{
   pinMode(soundPin, OUTPUT); //объявляем пин 6 как выход.
    tone(soundPin, 400, 300);
    delay(375.0);
    noTone(6); // Выключаем звук
  
  //Настроим пин для управления реле
  pinMode(RELAY_PIN1,OUTPUT);
  digitalWrite(RELAY_PIN1,LOW);
  pinMode(RELAY_PIN2,OUTPUT);
  digitalWrite(RELAY_PIN2,LOW);
  pinMode(RELAY_PIN3,OUTPUT);
  digitalWrite(RELAY_PIN3,LOW);
  pinMode(RELAY_PIN4,OUTPUT);
  digitalWrite(RELAY_PIN4,LOW);

  //Считаем из постоянной памяти заданную температуру
  setTmp=EEPROM_read_byte(0);//Температура СО
  setTmp1=EEPROM_read_byte(1);//Температура вкл насоса
  setTmp2=EEPROM_read_byte(2);//Температура выкл насоса
  setTmp3=EEPROM_read_byte(3);//Температура в комнате

  //Инициализируем термодатчик и установим разрешающую способность 12 бит (обычно она установлена по умолчанию, так что последнюю строчку можно опустить)
  sensors.begin();
  sensors.getAddress(tempDeviceAddress, 0);
  sensors.setResolution(12);
  sensorsOut.begin();
  sensorsOut.getAddress(tempDeviceAddressOut, 0);
  sensorsOut.setResolution(12);

    //работа пид регулятора в автомате
  Input = temp1;
  Setpoint = setTmp;
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, 100);

 //Выведем на дисплей стартовое сообщение на 2 секунды
  lcd.init(); 
  lcd.backlight();
  
  

   if (pusk == HIGH) {
    lcd.setCursor(0, 0);
    lcd.print("В РАБОТЕ  "); 
  }
  else {
    lcd.setCursor(0, 0);
    lcd.print("ОСТАНОВЛЕН");
  }

  //Определим функцию для опроса аналоговой клавиатуры
//Функция опроса клавиатуры, принимает адрес пина, к которому подключена клавиатура, и возвращает код клавиши:
// 1 - UP
// 2 - DOWN
// 3 - LEFT
// 4 - RIGHT
// 5 - SELECT
}
int ReadKey(int keyPin)
{
 int KeyNum=0;
 int KeyValue1=0;
 int KeyValue2=0;
 int KeyValue3=0;
//Читаем в цикле аналоговый вход, для подавления дребезга и нестабильности читаем по два раза подряд, пока значения не будут равны.
//Если значения равны 1023 – значит не была нажата ни  одна клавиша.

do {
KeyValue1=analogRead(keyPin);
 KeyValue2=analogRead(keyPin);
 } while (KeyValue1==KeyValue2&&KeyValue2!=1023);

//Интерпретируем полученное значение и определяем код нажатой клавиши
 if (KeyValue2<ButtonUp_HIGH&&KeyValue2>ButtonUp_LOW) {KeyNum=1;}//Up
 if (KeyValue2<ButtonDown_HIGH&&KeyValue2>ButtonDown_LOW) {KeyNum=2;}//Down
 if (KeyValue2<ButtonLeft_HIGH&&KeyValue2>ButtonLeft_LOW) {KeyNum=3;}//Left
 if (KeyValue2<=ButtonRight_HIGH&&KeyValue2>ButtonRight_LOW) {KeyNum=4;}//Right
 if (KeyValue2<ButtonSelect_HIGH&&KeyValue2>ButtonSelect_LOW) {KeyNum=5;}//Select

//Возвращаем код нажатой клавиши
return KeyNum;
}

 void menu()
{
  int keyCode=0;
  int k=0;
  lcd.init();
  lcd.setCursor(0,0);lcd.print("Меню настройки");
  lcd.setCursor(0, 1);lcd.print("Меню №");
  lcd.setCursor(7, 1);lcd.print(k);
  delay_ms(1000);
  

   do{
   keyCode=ReadKey(KEYPAD_PIN); 
   if(keyCode==1){k++;delay(300);k=min(k,4);lcd.setCursor(7,1);lcd.print(k);} 
   if(keyCode==2){k--;delay(300);k=max(k,0);lcd.setCursor(7,1);lcd.print(k);}
   
   }while(keyCode!=5);
   
   if(k==1)
   {setTemperature();}
   if(k==2)
   {setTemperature1();}
   if(k==3)
   {setTemperature2();}
   if(k==4)
   {setTemperature3();}


}

//Редактирование заданой температуры включения насоса ГВС
void setTemperature() {



//Редактирование заданой температуры включения насоса ГВС
void setTemperature1() {


  
//Редактирование заданой температуры выключения насоса ГВС
void setTemperature2() {


 
//Редактирование заданой температуры комнаты
void setTemperature3() {


 

}

void loop() {

 Start
 
    currentTime=millis(); 
 // time2=currentTime/3600000;
    
    // опрос клавиатуры
  int Feature=ReadKey(KEYPAD_PIN);
 
  if(Feature==5){;menu();}// переход в меню настройки

   // запуск котла нажать кнопку вверх
 
  if(Feature==1)
  {
  delay_ms(100);
  pusk=HIGH;
  EEPROM_write_byte(4,pusk);
   lcd.setCursor(0,0);
  lcd.print("В РАБОТЕ  ");
  } 
 
  // останов котла нажать кнопку вниз

  if(Feature==2&&pusk==HIGH)
  {delay_ms(100);
  pusk=LOW;
  EEPROM_write_byte(4,pusk);
   lcd.setCursor(0,0);
  lcd.print("ОСТАНОВЛЕН");
  } 

    if(Feature==3&&pusk==HIGH)
  {delay_ms(100);
  termostat = HIGH;
  EEPROM_write_byte(5,termostat);
  } 
   if(Feature==4&&pusk==HIGH)
  {delay_ms(100);
  termostat = LOW;
  EEPROM_write_byte(5,termostat);
  lcd.setCursor(11,1);
           lcd.print("=");
  } 
//Модуль опроса датчиков и получения сведений о температуре
//Вызывается 1 раз в секунду
  unsigned long currentMillis1 = millis();
if(currentMillis1 - previousMillis1 > interval1) {
    previousMillis1 = currentMillis1;  

//Запуск процедуры измерения температуры
  //sensors.setWaitForConversion(false);
  sensors.requestTemperatures();
  sensorsOut.requestTemperatures();
  //sensors.setWaitForConversion(true);
  

//delay(750); // задержка для обработки информации внутри термометра, в данном случае можно не задавать

//Считывание значения температуры
  sensors.getAddress(tempDeviceAddress, 0);
  temp1=sensors.getTempC(tempDeviceAddress);
  
  sensorsOut.getAddress(tempDeviceAddressOut, 0);
  temp2=sensorsOut.getTempC(tempDeviceAddressOut);

// Вывод текущего значения температуры на дисплей

  lcd.setCursor(10, 0);
  lcd.print("P");
  lcd.setCursor(0, 1);
  lcd.print("TCO");
  lcd.setCursor(4,1);
  if (temp1>-127){lcd.print(temp1);} else {lcd.print("HET");delay(200);}
  if(temp1 ==(-127)){lcd.init();lcd.setCursor(4,1);lcd.print("сбой");avaria=HIGH;delay_ms(1000);}
  if(temp1 !=(-127)){avaria=LOW;}
  lcd.setCursor(8,1);
  lcd.print(" Tk");
  lcd.setCursor(12,1);
  if (temp2>-127){lcd.print(temp2);} else {lcd.print("HET");delay(200);}
 //if(temp2 ==(-127)){lcd.init();lcd.setCursor(12,1);lcd.print("сбой");avaria=HIGH;delay_ms(1000);}
 //if(temp2 !=(-127)){avaria=LOW;}
  setLed++;

}
 /****************************************** Выход котла на паузу**************************************/
  // Пауза в работе котла при достижении температуры заданой
  if (temp1 >= (setTmp)) {
 
    myPID.SetMode(MANUAL);
  }
  if (temp1 < (setTmp - 1)) {
  
    myPID.SetMode(AUTOMATIC);
  }


  /****************************************** Пид регулятор**************************************/


  // Настройка пид
  Input = temp1;
  Setpoint = setTmp - 2;
  //работа пид регулятора
  double gap = abs(Setpoint - Input);                   //смотрим разницу заданного от уставки
  if (gap < 6) {
    myPID.SetTunings(consKp, consKi, consKd); //если меньше 5 то работаем на коэфицентах
  }
  else {
    myPID.SetTunings(aggKp, aggKi, aggKd); //если больше  то работаем на коэфицентах
  }
  myPID.Compute();



 //Проверка условия включения/выключения нагревателя
 if (pusk==HIGH&&avaria==LOW) {
   if (temp1 < setTmp && relayStatus33 == LOW)
  { relayStatus33 = HIGH; digitalWrite(RELAY_PIN1, HIGH);
    lcd.setCursor(11, 0);
    lcd.print("= 33%");
     lcd.setCursor(3, 1);
    lcd.print("+");
    
  }
  if (temp1 > setTmp && relayStatus33 == HIGH)
  { relayStatus33 = LOW; digitalWrite(RELAY_PIN1, LOW);
    lcd.setCursor(11, 0);
    lcd.print("=  0%");
    lcd.setCursor(3, 1);
    lcd.print("-");
    
  }
  delay(500);
  if (temp1  < setTmp - 1 && relayStatus66 == LOW)
  { relayStatus66 = HIGH; digitalWrite(RELAY_PIN2, HIGH);
    lcd.setCursor(11, 0);
    lcd.print("= 66%");
    lcd.setCursor(3, 1);
    lcd.print("+");
    
  }
  if (temp1 >= setTmp - 1 && relayStatus66 == HIGH)
  { relayStatus66 = LOW; digitalWrite(RELAY_PIN2, LOW);
    lcd.setCursor(11, 0);
    lcd.print("= 33%");
  //  lcd.setCursor(3, 1);
  //  lcd.print("-");

  }
  delay(500);
  if (temp1 < setTmp - 2 && relayStatus100 == LOW)
  { relayStatus100 = HIGH; digitalWrite(RELAY_PIN3, HIGH);
    lcd.setCursor(11, 0);
    lcd.print("=100%");
    lcd.setCursor(3, 1);
    lcd.print("+");
    
  }
  if (temp1 >= setTmp - 2 && relayStatus100 == HIGH)
  { relayStatus100 = LOW; digitalWrite(RELAY_PIN3, LOW);
    lcd.setCursor(11, 0);
    lcd.print("= 66%");
   // lcd.setCursor(3, 1);
    //lcd.print("-");

  }}
if (pusk==LOW&&avaria==HIGH)   
{
    if(pusk==LOW && relayStatus33 == HIGH && relayStatus66 == HIGH && relayStatus100 == HIGH)
    relayStatus33 = LOW; digitalWrite(RELAY_PIN1, LOW);
    relayStatus66 = LOW; digitalWrite(RELAY_PIN2, LOW);
    relayStatus100 = LOW; digitalWrite(RELAY_PIN3, LOW);
    lcd.setCursor(11, 0);
    lcd.print("=  0%");
    lcd.setCursor(3, 1);
    lcd.print("-");

  }

  // Работа с насосом
//блок управления насосом отопления 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

balomut пишет:

Всем привет !!

 Есть рабочий контроллер Электрокотла с 3 тэнами . Надоело менять тэны из-за того что один постоянно в работе . Хочу переделать на Пид регулировку , что бы все 3 тэна всегда были в работе , но не получается сделать включение и выключение котла с ПИД. Помогите советом .

При чем здесь ПИД? Вы хоть малость себе представляете, что эта штука сделана для "успокаивания" очень быстрых процессов? Где в Ваших котлах быстрые процессы? Они у Вас перекипают? Взрываются? Если они "постоянно в работе", то скорее всего они недостаточной мощности для Ваших задач. А если их надо "менять", то возможно возле них 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Я думаю ТС имел ввиду - распределить общий нагрев по всем тэнам, так как в основном один трудится (и сгорает изсза этого), а остальные в основном в простое). 

anatoli_nik
Offline
Зарегистрирован: 17.01.2019

Поставить твердотельное реле и диммировать (пропуском периодов) сразу все тэны, но соседи будут не в восторге от того что свет мигает (по себе знаю). Если диммировать обрезанием синусоиды то не только соседи будут очень "рады"...

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Можно просто пускать нагрев в цикле по тенам. К примеру , если мало нужно греть - n минут греет один, общее выключение, потом включается другой и тоже n минут греет и так далее. А если нужно поддать газку, то интервал n уменьшать и в какой-то момент включать уже два тена сразу (к примеру из 3х), чередуя их. Ничего диммировать не нужно. 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

BOOM пишет:

Можно просто пускать нагрев в цикле по тенам. К примеру , если мало нужно греть - n минут греет один, общее выключение, потом включается другой и тоже n минут греет и так далее. А если нужно поддать газку, то интервал n уменьшать и в какой-то момент включать уже два тена сразу (к примеру из 3х), чередуя их. Ничего диммировать не нужно. 

Ой-вэй! А где тут ПИД  - хоть раз скажите.

Нахрена здесь интегральная и дифференциальная составляющие? Вот "чтобы було"????

nik182
Offline
Зарегистрирован: 04.05.2015

mykaida пишет:

При чем здесь ПИД? Вы хоть малость себе представляете, что эта штука сделана для "успокаивания" очень быстрых процессов? Где в Ваших котлах быстрые процессы? Они у Вас перекипают? Взрываются? Если они "постоянно в работе", то скорее всего они недостаточной мощности для Ваших задач. А если их надо "менять", то возможно возле них 

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

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

nik182 пишет:
ПИД регулятор с тремя каналами. Канал И может "успокаивать" довольно продолжительное время. ПИД можно использовать для многого, не обязательно для быстрых процессов.    

Вы бредите?

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

nik182
Offline
Зарегистрирован: 04.05.2015

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/etyudy-dlya-pe...

Почитайте перевод нашего b707. Обратите внимание на слово долговременный в описании И канала. 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

nik182 пишет:

http://arduino.ru/forum/pesochnitsa-razdel-dlya-novichkov/etyudy-dlya-pesochnitsy-teoriya-upravleniya-i-regulyatory#comment-512054

Почитайте перевод нашего b707. Обратите внимание на слово долговременный в описании И канала. 

Боинг, конечно, классный мужик! Но дифференциальную составляющую он обошел боком.

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

Короче- почитал ТЗ ТС. Ему надо случайно менять начальный ТЭН, чтобы сгорали реже. А ПИД здесь совсем не при чём.

anatoli_nik
Offline
Зарегистрирован: 17.01.2019

mykaida, не пишите то о чем вы вообще нихера не бум-бум!

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

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

Дифференциальная применяется, в основном, при быстрых процессах, и очень редко применяется при регулировании температуры. 

SLKH
Offline
Зарегистрирован: 17.08.2015

balomut пишет:

Всем привет !!

 Есть рабочий контроллер Электрокотла с 3 тэнами . Надоело менять тэны из-за того что один постоянно в работе . Хочу переделать на Пид регулировку , что бы все 3 тэна всегда были в работе , но не получается сделать включение и выключение котла с ПИД. Помогите советом .

 

//Реле подключено к пину D9, D10, D11, D13
#define RELAY_PIN1 11// 33% 1кВт
#define RELAY_PIN2 10// 66% 2кВт
#define RELAY_PIN3 9// 100% 3кВт
#define RELAY_PIN4 5// насос циркуляционный
//Подсветка управляется через пин D12


 

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

если я правильно понял, что работают тэн1, или тэн1+тэн2, или тэн1+тэн2+тэн3.

anatoli_nik
Offline
Зарегистрирован: 17.01.2019

Как то так

#define TIME_SWITCH 10000 //время переключения мС
uint8_t power = 0;
unsigned long prevMillis;
bool relay1, relay2, reley3;

void setup() {

}
void loop{
  
  if ((temp1 < setTmp)&&power!=33) {
  power=33;
  relay1 = HIGH;
  relay2 = LOW;
  relay3 = LOW;
  }
  if ((temp1 < (setTmp - 1))&&power!=66) {
  power=66;
  relay1 = HIGH;
  relay2 = HIGH;
  relay3 = LOW;
  }
  if (temp1 < (setTmp - 2))&&power!=100) {
  power=100;
  relay1 = HIGH;
  relay2 = HIGH;
  relay3 = HIGH;
  }
  if ((temp1>setTmp)||pusk==LOW||avaria==HIGH)={
    power = 0;
    relay1 = LOW;
    relay2 = LOW;
    relay3 = LOW;
  }
if ((millis()-prevMillis)>=TIME_SWITCH){
  bool temp;
  temp = relay1;
  relay1 = relay2;
  relay2 = relay3;
  relay3 = temp;
  prewMillis+=TIME_SWITCH;
}
digitalWrite(RELAY_PIN1, relay1);
digitalWrite(RELAY_PIN2, relay2);
digitalWrite(RELAY_PIN3, relay3);

  
}

 

Sol_37
Offline
Зарегистрирован: 10.02.2021

Подскажите, пожалуйста!

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

anatoli_nik
Offline
Зарегистрирован: 17.01.2019

Если сможете прикрутить то подходит.