ПИД ШИМ регулятор температурі
- Войдите на сайт для отправки комментариев
Вс, 01/01/2017 - 15:39
Здравствуйте, пытаюсь сделать ПИД ШИМ регулятор на основе Uno для регулировки включения ТЭНа обогревателя, так как в "режиме термостата" качество регулировки не очень удовлетворительное.
Температура измеряется с помощью DS18B20, ее значение подается на вход ПИД-регулятора (или туда нужен сигнал разбаланса?), регулятор выдает некоторое значение, которое в свою очередь подается на вход симметрического мультивибратора и определяет длительность импульсов на пин выхода к реле, которое управляет ТЭНом.
Правильный ли такой алгоритм, или можно как-то упростить программу? Возможно у кого-то есть простые скетчи каких-то ПИД-регуляторов? Нашел только для управления серводвигателями и пытаюсь их разобрать. Кнопки и дисплей использоваться не будут.

P.S. занимаюсь программированием промышленных контроллеров на LD, FBD и ST почти два года, наконец прикупил Arduino, но пытаюсь применять методы оттуда прихожу в замешательство..
P.S. занимаюсь программированием промышленных контроллеров на LD, FBD и ST почти два года, наконец прикупил Arduino, но пытаюсь применять методы оттуда прихожу в замешательство..
Ну можно программировать в Ардуино так как показано в учебных скетчах.
Но это не мой метод. Мой метод millis(),millis() и еще millis(). То есть многопоточное программирование. Чего нет в большом программировании.
Собственно, сейчас у меня что-то получилось, но на выходе не могу получить импульсы. Добился только плавного изменения яркости светодиода на пине в зависимости от температуры:
#include <PID_v1.h> #include <DallasTemperature.h> #include <OneWire.h> #define ONE_WIRE_BUS 2// Подключение цифрового вывода датчика к 10-му пину Ардуино. OneWire oneWire(ONE_WIRE_BUS);// Запуск интерфейса OneWire для подключения OneWire устройств первой линии. DallasTemperature sensors(&oneWire);// Указание, что устройством oneWire является термодатчик от Dallas Temperature. double Setpoint, Input, Output; PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT); void setup() { Serial.begin(9600);// Запуск СОМ порта. Serial.println("Начало измерения температуры"); sensors.begin(); // Запуск датчика. Setpoint = 30; //Задание регулятора //Запуск ПИД-регулятора myPID.SetMode(AUTOMATIC); pinMode(3, OUTPUT); } void loop() { sensors.requestTemperatures(); // Команда опроса температуры. Serial.print("Tемпература = "); Serial.println(sensors.getTempCByIndex(0)); //Вывод температуры Input = (sensors.getTempCByIndex(0)); //Подача значения с датчика на вход ПИД регулятора myPID.Compute(); analogWrite(3,Output); //Выход ПИД- регулятора }Надо как-то преобразовать сигнал с выхода регулятора в импульсы с определенной частотой.
P.S. Как отредактировать ошибки в тексте первого поста?
А зачем управленние именно шимом? Если 220 и нагрузка более 1квт то применение шим дает помехи в сети.
В примерах к библиотеке пид есть скетч управления нагревателем
Никак не отредактировать
многопоточное программирование. Чего нет в большом программировании.
Точно? А мужики-то и не в курсе! :))))
Спасибо, об этом не подумал. Допустим, это будет что-то менее мощное.
Не могли бы тыкнуть?
С телефона, а по памяти , примеры, pid, и там будет три примера, один как выше, один вкл, выкл для обогревателя, а третий непомню какой
Касаемо помех в сети, если импульсы будут с маленькой частотой и большой скважностью (система инерционная), то помехи будут не так заметны. Например, импульс 5 сек, пауза 15 сек. Хотя и это нельзя будет назвать ШИМ, но такой метод подойдет даже лучше. Чего я и пытаюсь добиться.
Касаемо помех в сети, если импульсы будут с маленькой частотой и большой скважностью (система инерционная), то помехи будут не так заметны. Например, импульс 5 сек, пауза 15 сек. Хотя и это нельзя будет назвать ШИМ, но такой метод подойдет даже лучше. Чего я и пытаюсь добиться.
Ищите Blink через millis(). А то мужики знают, но молчат как белорусские партизаны. Ну что с мужиков взять-то. Одним словом Мужики.
Ищите Blink через millis().
Это оно! Что-то похожее, только не совсем так, как работает импульсный регулятор. Наверное, импульс и пауза должны немного отличаться по длительности..
Код программы сейчас выглядит вот так (вместо реле встроенный светодиод):
#include #include #include #define ONE_WIRE_BUS 2// Подключение цифрового вывода датчика к 10-му пину Ардуино. OneWire oneWire(ONE_WIRE_BUS);// Запуск интерфейса OneWire для подключения OneWire устройств первой линии. DallasTemperature sensors(&oneWire);// Указание, что устройством oneWire является термодатчик от Dallas Temperature. double Setpoint, Input, Output; int value = LOW; long previousMillis = 0; long interval = 0; PID myPID(&Input, &Output, &Setpoint,2,0.5,1, DIRECT); void setup() { Serial.begin(9600);// Запуск СОМ порта. Serial.println("Начало измерения температуры"); sensors.begin(); // Запуск датчика. Setpoint = 22; //Запуск ПИД-регулятора myPID.SetMode(AUTOMATIC); pinMode(3, OUTPUT); } void loop() { sensors.requestTemperatures(); // Команда опроса температуры. Serial.print("Tемпература = "); Serial.print(sensors.getTempCByIndex(0)); Serial.println ("°"); Serial.print("Выход = "); Serial.println(Output); Input = (sensors.getTempCByIndex(0)); myPID.Compute(); analogWrite(3,Output); Serial.println(interval); interval=Output*100; if (millis() - previousMillis > interval) { previousMillis = millis(); if (value == LOW and interval > 0) value = HIGH; else value = LOW; } digitalWrite(LED_BUILTIN, value); if (interval == 0) { value=LOW;} } }Примерно то, что я желаю получить (температура в комнате, сигнал выхода на регуляторе, импульсы на реле)
И чтo вам мешает? Вот код над которым я работаю. Может навеет вам решение.
/*viktor001_cl.ino #1 кнопка -> 2 (btn1_pin) 0 нажата/ 1 нет #2 светодиод 1 -> 3 (Led1_pin) 1 погашено/ аналог горит по уровню светодиод 2 -> 5 (Led2_pin) светодиод 3 -> 6 (Led3_pin) светодиод 4 -> 9 (Led4_pin) Принцип кода: чем дольше держишь кнопку, тем ярче горят светодиоды(50%,100%) */ class Cl_Led { public: Cl_Led(int _pin, uint32_t _time_1, uint32_t _time_2); void setup(); void loop(int _btn); private: int pin;// нога на светодиод int Led;// величина на светодиоде const int light0 = 0; // уровень не горит const int light50 = 50; // уровень горит 50% const int light100 = 100; // уровень горит 100% void Out_Led(int _Led); uint32_t past_led; uint32_t time_1; // задержка времени вкл light0->light50 и обратно uint32_t time_2; // задержка времени вкл light50->light100 и обратно }; Cl_Led::Cl_Led(int _pin, uint32_t _time_1, uint32_t _time_2) { pin = _pin; time_1 = _time_1; time_2 = _time_2; } void Cl_Led::Out_Led(int _Led) { // функция управ.свет с нужным уровнем switch (Led = _Led) { case 0: analogWrite(pin, light0); break; case 1: analogWrite(pin, light0); break; case 2: analogWrite(pin, light50); break; default: analogWrite(pin, light100); } } void Cl_Led::setup() { past_led = 0; Out_Led(0); } void Cl_Led::loop(int _btn) { if (Led == 3 && millis() - past_led >= time_2) { past_led = millis() ; if ( _btn) Out_Led( 2); } if (Led == 2 && millis() - past_led >= time_2 ) { past_led = millis() ; if (_btn) Out_Led(0); else Out_Led(3); } if (Led == 1 && millis() - past_led >= time_1 ) { past_led = millis() ; Out_Led(2); } if (Led == 0 && ! _btn) { past_led = millis() ; Led = 1; } } //#1 const int btn1_pin = 2; //нога кнопки bool btn1;// инверс сост кнопки 1 нажата /0 нет //#2 const int Led1_pin = 3; // управление яркостью с помощью ШИМ Cl_Led Led1(Led1_pin, 500, 3000); // 0,1 - негорит / 2 горит в полнакала / 3 горит полностью const int Led2_pin = 5; // управление яркостью с помощью ШИМ Cl_Led Led2(Led2_pin, 700, 3000); // 0,1 - негорит / 2 горит в полнакала / 3 горит полностью const int Led3_pin = 6; // управление яркостью с помощью ШИМ Cl_Led Led3(Led3_pin, 1000, 3000); // 0,1 - негорит / 2 горит в полнакала / 3 горит полностью const int Led4_pin = 9; // управление яркостью с помощью ШИМ Cl_Led Led4(Led4_pin, 1500, 3000); // 0,1 - негорит / 2 горит в полнакала / 3 горит полностью void setup() { //#1 pinMode(btn1_pin, INPUT_PULLUP); //#2 Led1.setup(); Led2.setup(); Led3.setup(); Led4.setup(); } void loop() { //#1 btn1 = digitalRead(btn1_pin); //#2 Led1.loop(btn1); Led2.loop(btn1); Led3.loop(btn1); Led4.loop(btn1); }А вот работаю с сервами
/* #1 Серва1 упр. выв -> 12 (servo1_pin) +5В -> +5В GND -> GND Серва2 упр. выв -> 11 (servo2_pin) +5В -> +5В GND -> GND Серва3 упр. выв -> 10 (servo3_pin) +5В -> +5В GND -> GND Серва4 упр. выв -> 9 (servo4_pin) +5В -> +5В GND -> GND Серва5 упр. выв -> 8 (servo5_pin) +5В -> +5В GND -> GND #2 резистор нижний выв -> GND средний выв -> A0 (R1_pin) верхний выв -> +5 резистор нижний выв -> GND средний выв -> A1 (R2_pin) верхний выв -> +5 резистор нижний выв -> GND средний выв -> A2 (R3_pin) верхний выв -> +5 резистор нижний выв -> GND средний выв -> A3 (R4_pin) верхний выв -> +5 резистор нижний выв -> GND средний выв -> A4 (R5_pin) верхний выв -> +5 */ //#1 const int servo1_pin = 12; uint16_t servo1_value = 544 ; // величина на серве 544-2400 мкс. uint8_t servo1;// const int servo2_pin = 11; uint16_t servo2_value = 544 ; // величина на серве 544-2400 мкс. uint8_t servo2;// const int servo3_pin = 10; uint16_t servo3_value = 544 ; // величина на серве 544-2400 мкс. uint8_t servo3;// const int servo4_pin = 9; uint16_t servo4_value = 544 ; // величина на серве 544-2400 мкс. uint8_t servo4;// const int servo5_pin = 8; uint16_t servo5_value = 544 ; // величина на серве 544-2400 мкс. uint8_t servo5;// //#2 const int R1_pin = A0;// нога резистора const int R2_pin = A1;// нога резистора const int R3_pin = A2;// нога резистора const int R4_pin = A3;// нога резистора const int R5_pin = A4;// нога резистора void setup() { //#1 pinMode (servo1_pin, OUTPUT); digitalWrite (servo1_pin, servo1 = 0); pinMode (servo2_pin, OUTPUT); digitalWrite (servo2_pin, servo2 = 0); pinMode (servo3_pin, OUTPUT); digitalWrite (servo3_pin, servo3 = 0); pinMode (servo4_pin, OUTPUT); digitalWrite (servo4_pin, servo4 = 0); pinMode (servo5_pin, OUTPUT); digitalWrite (servo5_pin, servo5 = 0); //#2 } void loop() { //#1 20 миллисекунд обслуживание сервы 1 и 2 static uint32_t past_1 = 0 ; if (servo1 && (micros() - past_1 >= servo1_value)) digitalWrite (servo1_pin, servo1 = 0); if (servo2 && (micros() - past_1 >= servo2_value)) digitalWrite (servo2_pin, servo2 = 0); if (servo3 && (micros() - past_1 >= servo3_value)) digitalWrite (servo3_pin, servo3 = 0); if (servo4 && (micros() - past_1 >= servo4_value)) digitalWrite (servo3_pin, servo3 = 0); if (servo5 && (micros() - past_1 >= servo5_value)) digitalWrite (servo5_pin, servo5 = 0); if (micros() - past_1 >= 20000) { // если прошло 20000 микросек past_1 = micros() ; digitalWrite (servo1_pin, servo1 = 1); digitalWrite (servo2_pin, servo2 = 1); digitalWrite (servo3_pin, servo3 = 1); digitalWrite (servo4_pin, servo4 = 1); digitalWrite (servo5_pin, servo5 = 1); } //#2 static uint32_t past_2 = 0 ; if (millis() - past_2 >= 200) { // если прошло 200000 микросек past_2 = millis() ; servo1_value = map (analogRead(R1_pin), 0, 1023, 544, 2400); servo2_value = map (analogRead(R2_pin), 0, 1023, 544, 2400); servo3_value = map (analogRead(R3_pin), 0, 1023, 544, 2400); servo4_value = map (analogRead(R4_pin), 0, 1023, 544, 2400); servo5_value = map (analogRead(R5_pin), 0, 1023, 544, 2400); } }Cпасибо, попробую разбораться, с сервами более понятно. Копать в millis(). Там где uint16_t - это какие-то таймеры?
Для своего регулятора, думаю, целесообразно изменить алгоритм работы. Например, выдавать импульсы через определенные промежутки времени, например 10 сек? Длинна этих импульсов будет зависеть от значения на выходе ПИД. Хотя и это не совсем правильно.
uint16_t это тип переменной беззнаковое int и все.
Почему не правильно. Если нет в аппаратной части, приходится реализовывать программно. Считайте, что ставите программный драйвер ШИМ.
Добился того, чтобы "мигалка" изменяла периоды паузы и свечения поуродовав код "blink без delay":
unsigned long interval = 1000; unsigned long interval2 = 5000; unsigned long previousMillis = 0; unsigned long previousMillis2 = 0; uint32_t time1 = 0; void setup() { pinMode (LED_BUILTIN, OUTPUT); } void loop() { static unsigned long previousMillis = 0; if (millis() - previousMillis > interval) { previousMillis = millis(); digitalWrite(LED_BUILTIN, HIGH ); if (millis() - previousMillis2 > interval2) { previousMillis2 = millis(); digitalWrite(LED_BUILTIN, LOW); } } }Подозреваю, что есть решения получше, но пока на ум больше ничего не приходит..
/* Led_resist.ino #1 подстроечник -> A0 (R1_pin) 0...1023 #2 светодиод -> 2 (led_pin) 1 горит / 0 нет */ //#1 const int R1_pin = A0;// нога светодиода int R1; //#2 const int led_pin = 2;// нога светодиода bool led; const uint16_t period = 5000; // полный период ШИМ uint16_t time_ON; void setup() { //#1 //#2 pinMode (led_pin, OUTPUT); digitalWrite(led_pin, led = 0 ); } void loop() { //#1 R1 = analogRead(R1_pin); //#2 time_ON = map (R1, 0, 1023, 0, period); static uint32_t past_2 = 0; if (led == 1 && millis() - past_2 >= time_ON) { digitalWrite(led_pin, led = 0 ); } if (millis() - past_2 >= period) { past_2 = millis(); digitalWrite(led_pin, led = 1 ); } }Чёт ругается компилятор :((( Говорит, что past_1 Пушкин будет описывать in this fucked scope :(((
Вот тема http://arduino.ru/forum/programmirovanie/upravlenie-tenami-propuskom-per...
Тут и ПИД и ШИМ мдленный. Может интересно будет ознакомиться.
Про ШИМ медленный http://arduino.ru/forum/programmirovanie/upravlenie-tenami-propuskom-per...
Исправил
/* Led_resist.ino #1 подстроечник -> A0 (R1_pin) 0...1023 #2 светодиод -> 2 (led_pin) 1 горит / 0 нет */ //#1 const int R1_pin = A0;// нога светодиода int R1; //#2 const int led_pin = 2;// нога светодиода bool led; const uint16_t period = 5000; // полный период ШИМ uint16_t time_ON; void setup() { //#1 //#2 pinMode (led_pin, OUTPUT); digitalWrite(led_pin, led = 0 ); } void loop() { //#1 R1 = analogRead(R1_pin); //#2 time_ON = map (R1, 0, 1023, 0, period); static uint32_t past_2 = 0; if (led == 1 && millis() - past_2 >= time_ON) { digitalWrite(led_pin, led = 0 ); } if (millis() - past_2 >= period) { past_2 = millis(); digitalWrite(led_pin, led = 1 ); } }Еще пара варийантов
#define led 13 int period=2000;//период медленного ШИМ int fill=250;//заполнение в мс unsigned long currentTime=0;//для текущенго времени unsigned long startFillTime=0;//для запоминания времени void setup(){ pinMode(led,OUTPUT); digitalWrite(led,0); } void loop(){ currentTime=millis();//запоминаем текущее время if(currentTime-startFillTime>=1){//если прошла милисекунда startFillTime=currentTime;//запоминаем текущее время if (period !=0){//если период не прошол period--;//убавляем digitalWrite(led,(period>(fill))?0:1);//управляем выходом } else{//если период прошел period=2000;//начинаем заново } } }//END LOOPЭтот лучше.
#define led 13 int period=2000; int fill=250; unsigned long startPeriodTime=0;// unsigned long currentTime=0;//для запоминания текущего времени void setup(){ pinMode(led,OUTPUT); digitalWrite(led,0); } void loop(){ currentTime=millis(); digitalWrite(led,(currentTime-startPeriodTime>=fill)?0:1);//управляем выходом в зависимости от прошло ли заполнение шим? if (currentTime-startPeriodTime>=period){//если время периода прошло startPeriodTime=currentTime;//запоминаем время и начинаем отсчет нового периода } }//END LOOPно скважность лучше задавать в процентах от периода fill=(период/100*%заполнения)
вот стандартный пример для управления реле
/******************************************************** * PID RelayOutput Example * Same as basic example, except that this time, the output * is going to a digital pin which (we presume) is controlling * a relay. the pid is designed to Output an analog value, * but the relay can only be On/Off. * * to connect them together we use "time proportioning * control" it's essentially a really slow version of PWM. * first we decide on a window size (5000mS say.) we then * set the pid to adjust its output between 0 and that window * size. lastly, we add some logic that translates the PID * output into "Relay On Time" with the remainder of the * window being "Relay Off Time" ********************************************************/ #include <PID_v1.h> #define RelayPin 6 //Define Variables we'll be connecting to double Setpoint, Input, Output; //Specify the links and initial tuning parameters PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT); int WindowSize = 5000; unsigned long windowStartTime; void setup() { windowStartTime = millis(); //initialize the variables we're linked to Setpoint = 100; //tell the PID to range between 0 and the full window size myPID.SetOutputLimits(0, WindowSize); //turn the PID on myPID.SetMode(AUTOMATIC); } void loop() { Input = analogRead(0); myPID.Compute(); /************************************************ * turn the output pin on/off based on pid output ************************************************/ if(millis() - windowStartTime>WindowSize) { //time to shift the Relay Window windowStartTime += WindowSize; } if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH); else digitalWrite(RelayPin,LOW); }То, что нужно, попробую еще так. Когда в самописный код с millis() пытаюсь вставить регулятор, чтобы задавать значение интервала, то задержки не срабатывают, мигает с одной частотой.
Также решил не очень кошерным способом через FLProg. Сгеренированный код показывать не буду, так как он мерзкий. Тем не менее, похоже, что работает нормально.