Ваш вариант ещё не пробовал, загвоздка скорее в том что я после нагрева охлаждаю один бак и частично второй, вот и получается на тот на который мало воздуха после во время охлаждения попадает вот он и нагревается больше, так как у него начальная точка и у бака выше чем с той стороны где я дую вентилятором, у меня есть два куллера но они не усьановлены, а чтобы было быстрее дую на них большим вентилятором.
Кстати, а если всё таки решу два датчика установить то, так пойдёт отслеживать?:
#define t_pwm 1000//период 100% мощности канал 1
#define t_pwm2 1000//период 100% мощности канал 2
#define t_time 100//период опроса температуры 1
#define t_time2 150//период опроса температуры 2
float termo=A0;// вход основного датчика температуры1
float termo2=A1;// вход основного датчика температуры2
//для ПИ регулятора
float ca,cb;
long time;//интервал замера температуры
long on_time=0;//для периода ШИМ1
long on_time2=0;//для периода ШИМ1
int zad=0;//задание мощности
float temp=0.0;//текущее значение температуры датчика 1
float temp2=0.0;//текущее значение температуры датчика 2
float pre_temp=0.0;//предыдущее значение температуры 1
float pre_temp2=0.0;//предыдущее значение температуры 2
int cd=0;
int c=0;
int w=0;
int ce=0;//пройденые циклы мощности
float ust4=489;//температура средний режим=115
int outten = 9;
int outten2 = 12;
void setup() {
analogReference(INTERNAL2V56);
pinMode(outten, OUTPUT);
pinMode(outten2, OUTPUT);
pinMode(termo, INPUT);
pinMode(termo2, INPUT);
}
void loop() {
if ((millis()-time)>=t_time){
time=millis();
temp=analogRead(termo);//получаем температуру
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
// w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку
pre_temp=temp;}
if (millis()>3000){
if((millis()-on_time)>=t_pwm){//период ШИМ
on_time=millis();
PIctl4();//расчет мощности
if(c<zad){
digitalWrite(outten,1);
}
else{
if (zad != 100){
digitalWrite(outten,0);
}
}
c=c+1;
if(c==100){
c=0;
//PIctl();
}
}
}
if ((millis()-time)>=t_time2){
time=millis();
temp2=analogRead(termo2);//получаем температуру
temp2=(pre_temp2*0.2)+(temp2*0.8);//фильтр
// w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку
pre_temp2=temp2;}
if (millis()>3000){
if((millis()-on_time2)>=t_pwm2){//период ШИМ
on_time2=millis();
PIctl4();//расчет мощности
if(c<zad){
digitalWrite(outten2,1);
}
else{
if (zad != 100){
digitalWrite(outten2,0);
}
}
c=c+1;
if(c==100){
c=0;
//PIctl();
}
}
}
}
void PIctl4()
{
if(temp>(ust4-2.12)&&temp<(ust4+2.12))//зона нечувствительности +-0,5C
{
zad=cb;//если ошибка+-0.5С то мощность = И состовляющей
}
else//иначе
{
float cf;
cf=(ust4-temp);//вычисление ошибки регулирования
// расчет выходной мощности:
ca=65.0*cf;//расчет пропорциональной состовляющей
if (ca<0.0)//мощность не может быть отрицательной
{
ca=0.0;//ограничение P
}
cb=(cb+(cf*0.014));//расчет интегральной составляющей = тепловым потерям
if (cb>100.0)//мощность не может быть больше 100%
{
cb=100.0;//ограничение I
}
if (cb<-100.0)
{
cb=-50.0;//ограничение I
}
//расчет выходной мощности
zad=ca+cb;
}
//ограничение управляющего сигнала
//для исключения частого переключения реле
//если твердотельное реле, можно не ограничивать
if (zad<4){
zad=0;
}
if (zad>96){
zad=100;
}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust4-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
cb=0;
}
if (temp>(ust4+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
cb=0;
}
}
Попробовал ваш код последний, на отдельной никуда не подпаяной ардуино мега, так замыкаю А0 на землю, в мониторе последовательного порта 0 градусов и 100% мощности, вот только на выводе 9 и 12 меги ничего нету, зато на выводе 10 появляется лог.1(ну так как 100% мощности), причём здесь вывод 10, не пойму, перепроверил 100 раз.
Я понял в чем проблемма, класс DigOut в погоне за скоростью под давлением местных прогеров я начал переделывать на прямое управление портами ... вобщем на мега он работать в таком виде не будет правильно. Я попозже вам ссылку выложу на этотже класс который со всеми дуинами работает. Просто замените его и все заработает, даже код править не нужно.
Добрый вечер. Я только начинаю изучение ардуион и наткнулся на эту тему. Сразу всплыла одна идея, которая меня волнует. Есть у меня надаче ящик с ТЭНом, порядка 1500 ватт. Есть регулятор, который держит определнную температуру в нем, за счет замыкания\размыкания реле. Так же в регуляторе есть аналоговый выход 0(4)-20 мА с функцией ПИДрегулипрвания. Так вот, как бы мне сделать плавное регулирование ТЭНами?? Я хочу чтоб при температуре близкой к уставке, потребление электричесва было минимальное. Я хочу услышать от Вас все плюсы и минусы такого подключения, есть смысл в таком регулировании?? И можно ли реализовать проект на ардуине без промышленного регулятора?? Так же у меня есть регулятор мощьностидля ТЭНа, который управляется переменным резистором, ну не вешать же на него сервопривод =))) Вообщем пока у меня тьма в котелке. Жду от вас помощи, пока только в теории.
Предложу использовать встроенный светодиод для индикации выходной мощности.
Нужно его прописать вначале
DigOut PwrInd (13,0);//индикатор мощности
И в коде где выдаем мощность
if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения
outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1
outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2
PwrInd=outten;//индикация управления симистором
}
Спасибо yul-i-an, поисправляю у себя и проверю, на чистой меге, хотя проблема у меня была в том что один бак охлаждаю, а второй частично и разница была в стартовых температурах, а так как мощность одинаковая даётся в одинаковый промежуток времени то они догнать по температуре друг друга не могли)
Ваш вариант ещё не пробовал, загвоздка скорее в том что я после нагрева охлаждаю один бак и частично второй, вот и получается на тот на который мало воздуха после во время охлаждения попадает вот он и нагревается больше, так как у него начальная точка и у бака выше чем с той стороны где я дую вентилятором, у меня есть два куллера но они не усьановлены, а чтобы было быстрее дую на них большим вентилятором.
Кстати, а если всё таки решу два датчика установить то, так пойдёт отслеживать?:
#define t_pwm 1000//период 100% мощности канал 1
#define t_pwm2 1000//период 100% мощности канал 2
#define t_time 100//период опроса температуры 1
#define t_time2 150//период опроса температуры 2
float termo=A0;// вход основного датчика температуры1
float termo2=A1;// вход основного датчика температуры2
//для ПИ регулятора
float ca,cb;
long time;//интервал замера температуры
long on_time=0;//для периода ШИМ1
long on_time2=0;//для периода ШИМ1
int zad=0;//задание мощности
float temp=0.0;//текущее значение температуры датчика 1
float temp2=0.0;//текущее значение температуры датчика 2
float pre_temp=0.0;//предыдущее значение температуры 1
float pre_temp2=0.0;//предыдущее значение температуры 2
int cd=0;
int c=0;
int w=0;
int ce=0;//пройденые циклы мощности
float ust4=489;//температура средний режим=115
int outten = 9;
int outten2 = 12;
void setup() {
analogReference(INTERNAL2V56);
pinMode(outten, OUTPUT);
pinMode(outten2, OUTPUT);
pinMode(termo, INPUT);
pinMode(termo2, INPUT);
}
void loop() {
if ((millis()-time)>=t_time){
time=millis();
temp=analogRead(termo);//получаем температуру
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
// w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку
pre_temp=temp;}
if (millis()>3000){
if((millis()-on_time)>=t_pwm){//период ШИМ
on_time=millis();
PIctl4();//расчет мощности
if(c<zad){
digitalWrite(outten,1);
}
else{
if (zad != 100){
digitalWrite(outten,0);
}
}
c=c+1;
if(c==100){
c=0;
//PIctl();
}
}
}
if ((millis()-time)>=t_time2){
time=millis();
temp2=analogRead(termo2);//получаем температуру
temp2=(pre_temp2*0.2)+(temp2*0.8);//фильтр
// w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку
pre_temp2=temp2;}
if (millis()>3000){
if((millis()-on_time2)>=t_pwm2){//период ШИМ
on_time2=millis();
PIctl4();//расчет мощности
if(c<zad){
digitalWrite(outten2,1);
}
else{
if (zad != 100){
digitalWrite(outten2,0);
}
}
c=c+1;
if(c==100){
c=0;
//PIctl();
}
}
}
}
void PIctl4()
{
if(temp>(ust4-2.12)&&temp<(ust4+2.12))//зона нечувствительности +-0,5C
{
zad=cb;//если ошибка+-0.5С то мощность = И состовляющей
}
else//иначе
{
float cf;
cf=(ust4-temp);//вычисление ошибки регулирования
// расчет выходной мощности:
ca=65.0*cf;//расчет пропорциональной состовляющей
if (ca<0.0)//мощность не может быть отрицательной
{
ca=0.0;//ограничение P
}
cb=(cb+(cf*0.014));//расчет интегральной составляющей = тепловым потерям
if (cb>100.0)//мощность не может быть больше 100%
{
cb=100.0;//ограничение I
}
if (cb<-100.0)
{
cb=-50.0;//ограничение I
}
//расчет выходной мощности
zad=ca+cb;
}
//ограничение управляющего сигнала
//для исключения частого переключения реле
//если твердотельное реле, можно не ограничивать
if (zad<4){
zad=0;
}
if (zad>96){
zad=100;
}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust4-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
cb=0;
}
if (temp>(ust4+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
cb=0;
}
}
Чтобы установить второй датчик температуры достаточно будет прописать его тут
if(currentMillis-preTempMillis>=t_time){//пришлло время мерить температуру
temp=analogRead(termo);//получаем температуру с первого
temp=temp/2.06;
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
pre_temp=temp;
temp2=analogRead(termo2);//получаем температуру со второго
temp2=temp2/2.06;
temp=(pre_temp2*0.2)+(temp2*0.8);//фильтр
pre_temp2=temp2;
preTempMillis=currentMillis;
}
Но ввиду усложнения код лучше написать функцию в которую передовать аналоговых вход на котором сидит датчик, а она вернет отфильтрованную температуру
например
float getTempLM35(byte Ain)
{
static float pre_temp=0;
float temp=analogRead(Ain);//получаем значение АЦП
temp=temp/2.06;//переводим в градусы
temp=(pre_temp*0.2)+(temp*0.8);//фильтруем
pre_temp=temp;//для фильтра в следующем цикле
return temp;
}
тогда вместо
if(currentMillis-preTempMillis>=t_time){//пришлло время мерить температуру
temp=analogRead(termo);//получаем температуру с первого
temp=temp/2.06;
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
pre_temp=temp;
temp2=analogRead(termo2);//получаем температуру со второго
temp2=temp2/2.06;
temp=(pre_temp2*0.2)+(temp2*0.8);//фильтр
pre_temp2=temp2;
preTempMillis=currentMillis;
}
проблема у меня была в том что один бак охлаждаю, а второй частично и разница была в стартовых температурах, а так как мощность одинаковая даётся в одинаковый промежуток времени то они догнать по температуре друг друга не могли)
В таком случае обязательно применение второрого датчика, темболее что это не грозит большими затратами и усложнением кода. И часть кода которая отвечает за корректировку мощности выкинуть как ненужный костыль.
Аппарат работает, разница в ТЭНах есть около 3-4 градусов но не критично, вообще разница около 7-8 градусов, но с учётом погрешности китайских мультиметров между ними разница порядка 3-4 градусов. Пока работаю на первом варианте.
Извиняюсь занят был, освобожусь скину благодарность, первый вариант это:
#define t_pwm 1000//период 100% мощности
#define t_time 1000//период опроса температуры
float t1=A0;// Вход АЦП 0
int outten=13;
//для ПИ регулятора
float p,i;
long time;//интервал замера температуры
long on_time=0;//для периода ШИМ
int zad=0;
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
int w=0;
float ust=489;//115 градусов
int c=0;//пройденные циклы мощности
void setup(){
pinMode(t1,INPUT);
pinMode(outten,OUTPUT);
time = millis();
on_time = time;
}
void loop(){
//---------------чтение температуры---------------
if ((millis()-time)>=t_time){
time=millis();
temp=analogRead(t1);//получаем температуру
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
// w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку
pre_temp=temp;
PIctl();//расчет мощности
}
//---------------------управление мощностью--------------------------
//выдаем мощность
if (millis()>3000){
if((millis()-on_time)>=t_pwm){//период ШИМ
on_time=millis();
if(c<zad){
digitalWrite(outten,1);
}
else{
if (zad != 100){
digitalWrite(outten,0);
}
}
c=c+1;
if(c==100){
c=0;
//PIctl();
}
}
}
}
void PIctl()
{
if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C
{
zad=i;
}
else
{
float e;
e=(ust-temp);
// расчет выходной мощности:
p=65.0*e;//130 коффициент пропорциональности 130.0
if (p<0.0)
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//0.7
if (i>100.0)
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=p+i;
}
//ограничение управляющего сигнала
//для исключения частого переключения реле
//если твердотельное реле, можно не ограничивать
if (zad<4){
zad=0;
}
if (zad>96){
zad=100;
}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust-2.0))//если темп < уст на 2С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+2.0))//если темп > уст на 2С
{
zad=0;//то мощность 0%
i=0;
}
}
Для первого варианта осциллограма выглядит так как всё время лог. 1 стоит, ну это понятно это же период ШИМ =1с. ( развёртка на осциле 50мс, осцил Хамелион, показания снимал задавая на вход АЦП вместо температурного датчика около 90-95% от необходимого входного для АЦП(от уставки)), так вот прямоугольные импульсы появляются как - то хаотично через разные промежутки времени , бывают как будтя два вместе соединяются, так то всё работает на симисторе, в моём устройстве, может это так осцил рисует.
С этим кодом(который на два выходы с коректировкой по мощности) всё по другому при том же периоде ШИМ=1с., прямоугольные импульсы появляются через одинаковые промежутки времени, и прямоугольные импульсы друг на друга не накладываются, в реальной работе не проверял. Праметры осцыла и задачи входного сигнала на АЦП те же. Коректировка мощности на другой канал работает.
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6
#define t_time 100//период опроса температуры
DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2
#define termo A0//вход основного датчика температуры
unsigned long time;//интервал замера температуры
byte zad=0;//задание мощности ТЭНа1
byte zad2=0;//задание мощности ТЭНа2
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
unsigned long preTempMillis=0;//для опроса температуры
unsigned long prePIctl_time=0;//для расчета мощности
float ust=0;//уставка
#define t_pwm 1000//период медленного ШИМ в мс
#define ust4 115//температура средний режим=115
void setup() {
//analogReference(INTERNAL2V56);
Serial.begin (9600);
}
void loop() {
unsigned long currentMillis=millis();
if(currentMillis-preTempMillis>=t_time){
temp=analogRead(termo);//получаем температуру
//а тут неплохобы в градусы целсия перевести
temp=temp/2.06;//переводим показания АЦП в градусы С
temp=(pre_temp*0.3)+(temp*0.7);//фильтр от резких изменений температуры
pre_temp=temp;
preTempMillis=currentMillis;//сброс таймера замера температуры
}
ust=ust4;//уставка
if(currentMillis-prePIctl_time>1000){//если подошло время расчитать мощность
zad=PIctl(temp, ust);//расчет мощности
zad=pwr(zad,0);//корректировка мощности
zad2=pwr(zad,10);//корректировка мощности
prePIctl_time=currentMillis;//сброс таймера расчета мощности
//Вывод в порт температуры и мощности
Serial.print(temp);
Serial.print("-T ");
Serial.print(zad);
Serial.print("-% ");
Serial.print(zad2);
Serial.println("-%");
}
if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения устройства
outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1
outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2
}
}//END loop
//функция расчета мощности по ПИ закону регулирования
byte PIctl(float temp, float ust)
{
byte zad=0;
static float i=0;
if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C
{
return byte(i);
}
else//иначе
{
float e, p;
e=(ust-temp);//вычисление ошибки регулирования
// расчет выходной мощности
p=(1.5*e);//расчет пропорциональной состовляющей
if (p<0.0)//мощность не может быть отрицательной
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям
if (i>100.0)//мощность не может быть больше 100%
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=byte(p+i);
if(zad>100){return 100;}
if(zad<0){return 0;}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
i=0;
}
return zad;
}///
}
byte pwr(int zad, int correct)
{
int res = zad+correct;
if(res>100){
res=100;
}
if(res<0){
res=0;
}
else
{
res=zad;
}
return res;
}
А вот такой код тоже на два выхода с корректировкой, в мониторе последовательного порта показывает 200%, на выходе только как я понимаю период ШИМ, а прямоугольные импульсы не появляются
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6
#define t_time 100//период опроса температуры
DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2
#define termo A0//вход основного датчика температуры
unsigned long time;//интервал замера температуры
byte zad=0;//задание мощности ТЭНа1
byte zad2=0;//задание мощности ТЭНа2
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
unsigned long preTempMillis=0;//для опроса температуры
float ust=0;
#define t_pwm 1000//период ШИМ в мс
#define ust4 489//температура средний режим=115
void setup() {
analogReference(INTERNAL2V56);
}
void loop() {
unsigned long currentMillis=millis();
if(currentMillis-preTempMillis>=t_time){
temp=analogRead(termo);//получаем температуру
//а тут неплохобы в градусы целсия перевести
//temp=temp/4.25;
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
pre_temp=temp;
preTempMillis=currentMillis;
}
ust=ust4;
zad=PIctl(temp, ust);//расчет мощности
zad=pwr(zad,0);//корректировка мощности
zad2=PIctl(temp, ust);//расчет мощности
zad2=pwr(zad2,10);//корректировка мощности
if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения
outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1
outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2
}
}//END loop
//расчет мощности
byte PIctl(float temp, float ust)
{
static float p, i;
if(temp>(ust-2.12)&&temp<(ust+2.12))//зона нечувствительности +-0,5C
{
zad=i;//если ошибка+-0.5С то мощность = И состовляющей
}
else//иначе
{
float e;
e=(ust-temp);//вычисление ошибки регулирования
// расчет выходной мощности:
p=(65.0*e);//расчет пропорциональной состовляющей
if (p<0.0)//мощность не может быть отрицательной
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям
if (i>100.0)//мощность не может быть больше 100%
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=p+i;
}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
i=0;
}
}
//корректировка мощности
byte pwr(int zad, int correct)
{
int res = zad+correct;
if(res>100){
return 100;
}
if(res<0 || zad == 0){
return 0;
}
return res;
}
Насчёт кода для применения двух датчиков и двух выходов разных, пост 64, а как выдать мощность на разные выхода, после получения показания с двух датчиков, не пойму.
Ну второй код мне кажеться получился более читаемым и прозрачным. А картинки с осцилогрофа какнибудь можете выложить, для второго кода?
Я на выходных этот код мучал, мне кажеться что он полностью рабочий
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6
#define t_time 1000//период опроса температуры
#define power_time 1000//период расчета мощности
DigOut outten(9), outten2(8), ind(13,0);//инициализация цифровых выходов на тен 1, 2 и индикатор
#define termo A0//вход основного датчика температуры
unsigned long time;//интервал замера температуры
byte zad=0;//задание мощности ТЭНа1
byte zad2=0;//задание мощности ТЭНа2
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
unsigned long preTempMillis=0;//для опроса температуры
unsigned long prePIctl_time=0;//для расчета мощности
float ust=0;//уставка
#define t_pwm 1000//период медленного ШИМ в мс
#define ust4 100//температура средний режим=100
void setup() {
//analogReference(INTERNAL2V56);
Serial.begin (9600);
}
void loop() {
ust=ust4;//уставка
unsigned long currentMillis=millis();
if(currentMillis-preTempMillis>=t_time){
temp = getTempLM35(termo);
//сюда второй датчик
preTempMillis=currentMillis;//сброс таймера замера температуры
}
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность
zad=PIctl(temp, ust);//расчет мощности
//если два датчика то zad2= PIctl(temp2, ust);
zad2=zad;//мощность одинаковая
prePIctl_time=currentMillis;//сброс таймера расчета мощности
//Вывод в порт температуры и мощности
serialPrint();
}
if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства
outten.blink(t_pwm, ((t_pwm)/100)*zad);//управление теном 1
outten2.blink(t_pwm, ((t_pwm)/100)*zad2);//управление теном 2
ind=outten;
}
}//END loop
//функция расчета мощности по ПИ закону регулирования
byte PIctl(float temp, float ust)
{
byte zad=0;
static float i=0;
if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C
{
return byte(i);
}
else//иначе
{
float e, p;
e=(ust-temp);//вычисление ошибки регулирования
// расчет выходной мощности
p=(1.5*e);//расчет пропорциональной состовляющей
if (p<0.0)//мощность не может быть отрицательной
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям
if (i>100.0)//мощность не может быть больше 100%
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=byte(p+i);
if(zad>100){return 100;}
if(zad<0){return 0;}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
i=0;
}
}///
return zad;
}
//получение температуры с LM35
float getTempLM35(byte Ain)
{
static float pre_temp=0;
float temp=analogRead(Ain);//получаем значение АЦП
temp=temp/2.06;//переводим в градусы
temp=(pre_temp*0.3)+(temp*0.7);//фильтруем
pre_temp=temp;//для фильтра в следующем цикле
return temp;
}
//вывод в монитор для отладки
void serialPrint()
{
Serial.print(temp);
Serial.print("-T ");
Serial.print(zad);
Serial.print("-% ");
Serial.print(zad2);
Serial.println("-%");
}
Единственное останется настроить ПИ регулятор и если всётаки две разные емкости добавить второй датчик температуры, темболее что теперь это не составит труда.
А вот такой код тоже на два выхода с корректировкой, в мониторе последовательного порта показывает 200%, на выходе только как я понимаю период ШИМ, а прямоугольные импульсы не появляются
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6
#define t_time 100//период опроса температуры
DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2
#define termo A0//вход основного датчика температуры
unsigned long time;//интервал замера температуры
byte zad=0;//задание мощности ТЭНа1
byte zad2=0;//задание мощности ТЭНа2
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
unsigned long preTempMillis=0;//для опроса температуры
float ust=0;
#define t_pwm 1000//период ШИМ в мс
#define ust4 489//температура средний режим=115
void setup() {
analogReference(INTERNAL2V56);
}
void loop() {
unsigned long currentMillis=millis();
if(currentMillis-preTempMillis>=t_time){
temp=analogRead(termo);//получаем температуру
//а тут неплохобы в градусы целсия перевести
//temp=temp/4.25;
temp=(pre_temp*0.2)+(temp*0.8);//фильтр
pre_temp=temp;
preTempMillis=currentMillis;
}
ust=ust4;
zad=PIctl(temp, ust);//расчет мощности
zad=pwr(zad,0);//корректировка мощности
zad2=PIctl(temp, ust);//расчет мощности
zad2=pwr(zad2,10);//корректировка мощности
if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения
outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1
outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2
}
}//END loop
//расчет мощности
byte PIctl(float temp, float ust)
{
static float p, i;
if(temp>(ust-2.12)&&temp<(ust+2.12))//зона нечувствительности +-0,5C
{
zad=i;//если ошибка+-0.5С то мощность = И состовляющей
}
else//иначе
{
float e;
e=(ust-temp);//вычисление ошибки регулирования
// расчет выходной мощности:
p=(65.0*e);//расчет пропорциональной состовляющей
if (p<0.0)//мощность не может быть отрицательной
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям
if (i>100.0)//мощность не может быть больше 100%
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=p+i;
}
//корректировка при отклонении температуры более чем на +-2С
//для быстрого выхода на уставку
if (temp<(ust-8.5))//если темп < уст на 2С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+8.5))//если темп > уст на 2С
{
zad=0;//то мощность 0%
i=0;
}
}
//корректировка мощности
byte pwr(int zad, int correct)
{
int res = zad+correct;
if(res>100){
return 100;
}
if(res<0 || zad == 0){
return 0;
}
return res;
}
Надо сравнить функцию ПИ регулятора с кодом из поста #75 (самая последняя версия), там помоему глюки были но я их исправил.
Выложу картинки попозже, а насчёт кода со вторым датчиком, то почему, мощность одинаковая, если допустим баки хоть и одинаковые, но допустим тот стартует с 30 градусов, а другой с 20 градусов ну если их не равномерно охладить, да и разница в ТЭНах в 3-4 градуса всё равно есть, хотя и мультиметры с термопарами около 2градусов погрешность, причём разница изначально в мультиметрах 2 градуса, а мы будем одинаковую мощность давать, или я что-то не пойму?
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность
Насчёт кода для применения двух датчиков и двух выходов разных, пост 64, а как выдать мощность на разные выхода, после получения показания с двух датчиков, не пойму.
Есть секция получения температуры
if(currentMillis-preTempMillis>=t_time){
temp = getTempLM35(termo);
temp2 = getTempLM35(termo2);//добавили второй датчик
preTempMillis=currentMillis;//сброс таймера замера температуры
}
Есть секция расчета мощности по ПИ закону
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность
zad=PIctl(temp, ust);//расчет мощности для первой емкости
zad2=PIctl(temp2, ust);//расчет мощности для второй емкости
prePIctl_time=currentMillis;//сброс таймера расчета мощности
//Вывод в порт температуры и мощности
serialPrint();
}
Есть секция управления нагрузкой
if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства
outten.blink(t_pwm, ((t_pwm)/100)*zad);//управление теном 1
outten2.blink(t_pwm, ((t_pwm)/100)*zad2);//управление теном 2
ind=outten;
}
//тут ничего не меняем
И по анологии хоть 10 добавляйте, но если емкости разные, то придется переписать функцию расчета ПИ чтобы она принимала коэффициенты для каждой емкости свои.
Это изменения для последнего кода из поста #75 для работы с двумя каналами измерения/регулирования.
Баки одинаковые но охлаждаются возможно по разному, или даже одинаково но один куллер заклинет, да и говорю же что разница в ТЭНах хоть и не большая есть, а я просто не увидел секция управления нагрузкой, спасибо, просто и думаю зачем же два датчика если включать ТЭНы как и раньше, теперь увидел, спасибо.
Баки одинаковые но охлаждаются возможно по разному, или даже одинаково но один куллер заклинет, да и говорю же что разница в ТЭНах хоть и не большая есть, а я просто не увидел секция управления нагрузкой, спасибо, просто и думаю зачем же два датчика если включать ТЭНы как и раньше, теперь увидел, спасибо.
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Ну а если бы ёмкости были разные то можно же греть их на разные уставки и всё
Ну если Вам для процесса нужно поддерживать точную температуру, зачем уставки менять? Разные так разные регулятор для каждой свою мощность расчитает при одной уставке.
Если баки разные(конкретно допустим обьём у них в два раза отличается) их и греть допустим нужно для разной температуры, так как их емкость разная, и что бы выделилось одинаковое количество пара допустим. то и нужно к разным температурам. Это так теоретически. Вы конечно правы, для моего случая да допустим разница не большая и мощность ПИ-регулятор подберёт на каждый выходной канал свою в соответствии от входных сигналов. А вообще пока что использую первый вариант и он меня устраивает, а там и этот на два датчика может пригодится. Огромное спасибо за помощь
//функция расчета мощности по ПИ закону регулирования
byte PIctl(float temp, float ust)
{
byte zad=0;
static float i=0;
if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C
{
zad=i;
}
else//иначе
{
float e, p;
e=(ust-temp);//вычисление ошибки регулирования
// расчет выходной мощности
p=(1.5*e);//расчет пропорциональной состовляющей
if (p<0.0)//мощность не может быть отрицательной
{
p=0.0;//ограничение P
}
i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям
if (i>100.0)//мощность не может быть больше 100%
{
i=100.0;//ограничение I
}
if (i<-100.0)
{
i=-50.0;//ограничение I
}
//расчет выходной мощности
zad=byte(p+i);
if(zad>100){zad = 100;}
if(zad<0){zad = 0;}
//для быстрого выхода на уставку
if (temp<(ust-8.5))//если темп < уст на 8,5С
{
zad=100;//то мощность 100%
i=0;
}
if (temp>(ust+3))//если темп > уст на 3С
{
zad=0;//то мощность 0%
i=0;
}
}
return zad;
}
Долго из за не оптимальных настроек коэффициентов. И варийантов не много, или быстро с перерегулированием или дольше но без перерегулирования, выбирается под свои нужды.
Про знаки вопросов - (условие)? если истина : если лож ;
Все будет работатть, только ж реле бери 15А и нужное напряжение коммутации, или ддва реле в параллель контакты, или у одного если есть две пары контактов по 10А в параллель.
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Здравствуйте yul-i-an
Пришла плата и остальные детали
Пробую ваш код, если можно глянете правильно ли я все сделал.
[code]
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6
#define t_time 1000//период опроса температуры
#define power_time 1000//период расчета мощности
DigOut outten(9);//инициализация цифровых выходов на тен
unsigned long time;//интервал замера температуры
byte zad=0;//задание мощности ТЭНа1
float temp=0.0;//текущее значение температуры
float pre_temp=0.0;//предыдущее значение температуры
unsigned long preTempMillis=0;//для опроса температуры
unsigned long prePIctl_time=0;//для расчета мощности
float ust=0;//уставка
#define t_pwm 1000//период медленного ШИМ в мс
#define ust4 85//температура средний режим=100
///------------------------ temp
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2//вход основного датчика температуры
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature datchik(&oneWire);
//-----------------------------------------
//функция расчета мощности по ПИ закону регулирования
//расчет мощности по ПИ закону регулирования
#define kP 6//коэффициент пропорциональности
#define p_min 0.0//минимум П составляющей - не < 0
#define p_max 100.0//максимум П составляющей - не > 100
#define kI 0.025//коэффициент интегрирования
#define i_min 0.0//минимум И составляющей
#define i_max 30.0//максимум И составляющей
#define d_ctl 1.5//зона пропорциональности ust-d_ctl
#define out_min 0//минимальный выходной %
#define out_max 100//максимальный выходной %
byte PIctl(float temp, uint8_t ust)//возвращает необходимую мощность
{
uint8_t out=0;//uint8_t
static float i=0;
float e, p;
e=(ust-temp);//ошибка регулирования
//расчет p
p=(temp<ust-d_ctl)?p_max:(temp>ust)?p_min:(kP*e);
//расчет i
i=(i<i_min)?i_min:(i>i_max)?i_max:i+(kI*e);
out=(p+i<out_min)?out_min:(p+i>out_max)?out_max:p+i;
return out;
}
//вывод в монитор для отладки
void serialPrint()
{
Serial.print(temp);
Serial.println("-T ");
Serial.print(zad);
Serial.print("-% ");
}
void setup() {
datchik.begin();
Serial.begin (9600);
}
void loop() {
ust=ust4;//уставка
unsigned long currentMillis=millis();
if(currentMillis-preTempMillis>=t_time){
temp=datchik.getTempCByIndex(0);//получаем температуру
preTempMillis=currentMillis;//сброс таймера замера температуры
}
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность
zad=PIctl(temp, ust);//расчет мощности
prePIctl_time=currentMillis;//сброс таймера расчета мощности
//Вывод в порт температуры и мощности
serialPrint();
}
if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства
outten.lpwm(t_pwm, zad);//управление теном 1
//ind=outten;// ?
}
}//END loop
[/code]
При компиляции скетча ругался на ind=outten; убрал вроде все пошло.
Также заменил как вы писали outten.lpwm(t_pwm, zad);
Вопрос, нужную мне температуру я задаю переменной zad ?
Не могу понять для чего #define ust4 85//температура средний режим, что значит средний режим?
Для чего две ust и ust4 если всеровно ust=ust4;
Ваш вариант ещё не пробовал, загвоздка скорее в том что я после нагрева охлаждаю один бак и частично второй, вот и получается на тот на который мало воздуха после во время охлаждения попадает вот он и нагревается больше, так как у него начальная точка и у бака выше чем с той стороны где я дую вентилятором, у меня есть два куллера но они не усьановлены, а чтобы было быстрее дую на них большим вентилятором.
Кстати, а если всё таки решу два датчика установить то, так пойдёт отслеживать?:
#define t_pwm 1000//период 100% мощности канал 1 #define t_pwm2 1000//период 100% мощности канал 2 #define t_time 100//период опроса температуры 1 #define t_time2 150//период опроса температуры 2 float termo=A0;// вход основного датчика температуры1 float termo2=A1;// вход основного датчика температуры2 //для ПИ регулятора float ca,cb; long time;//интервал замера температуры long on_time=0;//для периода ШИМ1 long on_time2=0;//для периода ШИМ1 int zad=0;//задание мощности float temp=0.0;//текущее значение температуры датчика 1 float temp2=0.0;//текущее значение температуры датчика 2 float pre_temp=0.0;//предыдущее значение температуры 1 float pre_temp2=0.0;//предыдущее значение температуры 2 int cd=0; int c=0; int w=0; int ce=0;//пройденые циклы мощности float ust4=489;//температура средний режим=115 int outten = 9; int outten2 = 12; void setup() { analogReference(INTERNAL2V56); pinMode(outten, OUTPUT); pinMode(outten2, OUTPUT); pinMode(termo, INPUT); pinMode(termo2, INPUT); } void loop() { if ((millis()-time)>=t_time){ time=millis(); temp=analogRead(termo);//получаем температуру temp=(pre_temp*0.2)+(temp*0.8);//фильтр // w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку pre_temp=temp;} if (millis()>3000){ if((millis()-on_time)>=t_pwm){//период ШИМ on_time=millis(); PIctl4();//расчет мощности if(c<zad){ digitalWrite(outten,1); } else{ if (zad != 100){ digitalWrite(outten,0); } } c=c+1; if(c==100){ c=0; //PIctl(); } } } if ((millis()-time)>=t_time2){ time=millis(); temp2=analogRead(termo2);//получаем температуру temp2=(pre_temp2*0.2)+(temp2*0.8);//фильтр // w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку pre_temp2=temp2;} if (millis()>3000){ if((millis()-on_time2)>=t_pwm2){//период ШИМ on_time2=millis(); PIctl4();//расчет мощности if(c<zad){ digitalWrite(outten2,1); } else{ if (zad != 100){ digitalWrite(outten2,0); } } c=c+1; if(c==100){ c=0; //PIctl(); } } } } void PIctl4() { if(temp>(ust4-2.12)&&temp<(ust4+2.12))//зона нечувствительности +-0,5C { zad=cb;//если ошибка+-0.5С то мощность = И состовляющей } else//иначе { float cf; cf=(ust4-temp);//вычисление ошибки регулирования // расчет выходной мощности: ca=65.0*cf;//расчет пропорциональной состовляющей if (ca<0.0)//мощность не может быть отрицательной { ca=0.0;//ограничение P } cb=(cb+(cf*0.014));//расчет интегральной составляющей = тепловым потерям if (cb>100.0)//мощность не может быть больше 100% { cb=100.0;//ограничение I } if (cb<-100.0) { cb=-50.0;//ограничение I } //расчет выходной мощности zad=ca+cb; } //ограничение управляющего сигнала //для исключения частого переключения реле //если твердотельное реле, можно не ограничивать if (zad<4){ zad=0; } if (zad>96){ zad=100; } //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust4-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% cb=0; } if (temp>(ust4+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% cb=0; } }Попробовал ваш код последний, на отдельной никуда не подпаяной ардуино мега, так замыкаю А0 на землю, в мониторе последовательного порта 0 градусов и 100% мощности, вот только на выводе 9 и 12 меги ничего нету, зато на выводе 10 появляется лог.1(ну так как 100% мощности), причём здесь вывод 10, не пойму, перепроверил 100 раз.
Я понял в чем проблемма, класс DigOut в погоне за скоростью под давлением местных прогеров я начал переделывать на прямое управление портами ... вобщем на мега он работать в таком виде не будет правильно. Я попозже вам ссылку выложу на этотже класс который со всеми дуинами работает. Просто замените его и все заработает, даже код править не нужно.
Перекачайте класс DigOut
Добрый вечер. Я только начинаю изучение ардуион и наткнулся на эту тему. Сразу всплыла одна идея, которая меня волнует. Есть у меня надаче ящик с ТЭНом, порядка 1500 ватт. Есть регулятор, который держит определнную температуру в нем, за счет замыкания\размыкания реле. Так же в регуляторе есть аналоговый выход 0(4)-20 мА с функцией ПИДрегулипрвания. Так вот, как бы мне сделать плавное регулирование ТЭНами?? Я хочу чтоб при температуре близкой к уставке, потребление электричесва было минимальное. Я хочу услышать от Вас все плюсы и минусы такого подключения, есть смысл в таком регулировании?? И можно ли реализовать проект на ардуине без промышленного регулятора?? Так же у меня есть регулятор мощьностидля ТЭНа, который управляется переменным резистором, ну не вешать же на него сервопривод =))) Вообщем пока у меня тьма в котелке. Жду от вас помощи, пока только в теории.
А последний класс DigOut работает только с Дунами или с УНо и другими на меге 328 тоже, или для меги 328 предыдущий класс, а для меги этот?
andreyR, с реле не очень хорошо вдруг что можно оптосимистор с симистором поставить, 4-20мА, тебе не к чему, это универсальный КИПовский сигнал
Вот тема была на форуме как измерить 4-20мА. Я так понимаю это будет 0-100% выходной мощности.
В начале темы я ссылки выкладывал, ПИ регулятор вроде я себе на дачу делал в прошлом году. Всю зиму отработал без проблемм +-0.3С.
Если хотите полностью на Ардуино собрать, просто возмите код из этой темы.
Из кода нужно будет только второй тен выбросить и всё.
А переменный резистор из Вашего блока управления мощностью можно заменить на чтото типа этого.
А последний класс DigOut работает только с Дунами или с УНо и другими на меге 328 тоже, или для меги 328 предыдущий класс, а для меги этот?
Последний работает на всех дуинах поддерживаемых средой (но он медленней - скорость как у стандартной функции digitalWrite()).
В функции корректировки мощности закралась ошибочка.
Исправил, замените на это
//корректировка мощности byte pwr(byte zad, byte correct) { byte res = zad+correct; if(res>100){ return 100; } if(res<0){ return 0; } return zad+correct; }В этой функции осталась еще ошибка, как исправлю выложу.
Предложу использовать встроенный светодиод для индикации выходной мощности.
Нужно его прописать вначале
И в коде где выдаем мощность
if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1 outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2 PwrInd=outten;//индикация управления симистором }Наверное окончательный варийант
//корректировка мощности byte pwr(int zad, int correct) { int res = zad+correct; if(res>100){ return 100; } if(res<0 || zad == 0){ return 0; } return res; }Спасибо yul-i-an, поисправляю у себя и проверю, на чистой меге, хотя проблема у меня была в том что один бак охлаждаю, а второй частично и разница была в стартовых температурах, а так как мощность одинаковая даётся в одинаковый промежуток времени то они догнать по температуре друг друга не могли)
Ваш вариант ещё не пробовал, загвоздка скорее в том что я после нагрева охлаждаю один бак и частично второй, вот и получается на тот на который мало воздуха после во время охлаждения попадает вот он и нагревается больше, так как у него начальная точка и у бака выше чем с той стороны где я дую вентилятором, у меня есть два куллера но они не усьановлены, а чтобы было быстрее дую на них большим вентилятором.
Кстати, а если всё таки решу два датчика установить то, так пойдёт отслеживать?:
#define t_pwm 1000//период 100% мощности канал 1 #define t_pwm2 1000//период 100% мощности канал 2 #define t_time 100//период опроса температуры 1 #define t_time2 150//период опроса температуры 2 float termo=A0;// вход основного датчика температуры1 float termo2=A1;// вход основного датчика температуры2 //для ПИ регулятора float ca,cb; long time;//интервал замера температуры long on_time=0;//для периода ШИМ1 long on_time2=0;//для периода ШИМ1 int zad=0;//задание мощности float temp=0.0;//текущее значение температуры датчика 1 float temp2=0.0;//текущее значение температуры датчика 2 float pre_temp=0.0;//предыдущее значение температуры 1 float pre_temp2=0.0;//предыдущее значение температуры 2 int cd=0; int c=0; int w=0; int ce=0;//пройденые циклы мощности float ust4=489;//температура средний режим=115 int outten = 9; int outten2 = 12; void setup() { analogReference(INTERNAL2V56); pinMode(outten, OUTPUT); pinMode(outten2, OUTPUT); pinMode(termo, INPUT); pinMode(termo2, INPUT); } void loop() { if ((millis()-time)>=t_time){ time=millis(); temp=analogRead(termo);//получаем температуру temp=(pre_temp*0.2)+(temp*0.8);//фильтр // w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку pre_temp=temp;} if (millis()>3000){ if((millis()-on_time)>=t_pwm){//период ШИМ on_time=millis(); PIctl4();//расчет мощности if(c<zad){ digitalWrite(outten,1); } else{ if (zad != 100){ digitalWrite(outten,0); } } c=c+1; if(c==100){ c=0; //PIctl(); } } } if ((millis()-time)>=t_time2){ time=millis(); temp2=analogRead(termo2);//получаем температуру temp2=(pre_temp2*0.2)+(temp2*0.8);//фильтр // w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку pre_temp2=temp2;} if (millis()>3000){ if((millis()-on_time2)>=t_pwm2){//период ШИМ on_time2=millis(); PIctl4();//расчет мощности if(c<zad){ digitalWrite(outten2,1); } else{ if (zad != 100){ digitalWrite(outten2,0); } } c=c+1; if(c==100){ c=0; //PIctl(); } } } } void PIctl4() { if(temp>(ust4-2.12)&&temp<(ust4+2.12))//зона нечувствительности +-0,5C { zad=cb;//если ошибка+-0.5С то мощность = И состовляющей } else//иначе { float cf; cf=(ust4-temp);//вычисление ошибки регулирования // расчет выходной мощности: ca=65.0*cf;//расчет пропорциональной состовляющей if (ca<0.0)//мощность не может быть отрицательной { ca=0.0;//ограничение P } cb=(cb+(cf*0.014));//расчет интегральной составляющей = тепловым потерям if (cb>100.0)//мощность не может быть больше 100% { cb=100.0;//ограничение I } if (cb<-100.0) { cb=-50.0;//ограничение I } //расчет выходной мощности zad=ca+cb; } //ограничение управляющего сигнала //для исключения частого переключения реле //если твердотельное реле, можно не ограничивать if (zad<4){ zad=0; } if (zad>96){ zad=100; } //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust4-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% cb=0; } if (temp>(ust4+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% cb=0; } }Чтобы установить второй датчик температуры достаточно будет прописать его тут
if(currentMillis-preTempMillis>=t_time){//пришлло время мерить температуру temp=analogRead(termo);//получаем температуру с первого temp=temp/2.06; temp=(pre_temp*0.2)+(temp*0.8);//фильтр pre_temp=temp; temp2=analogRead(termo2);//получаем температуру со второго temp2=temp2/2.06; temp=(pre_temp2*0.2)+(temp2*0.8);//фильтр pre_temp2=temp2; preTempMillis=currentMillis; }Но ввиду усложнения код лучше написать функцию в которую передовать аналоговых вход на котором сидит датчик, а она вернет отфильтрованную температуру
например
float getTempLM35(byte Ain) { static float pre_temp=0; float temp=analogRead(Ain);//получаем значение АЦП temp=temp/2.06;//переводим в градусы temp=(pre_temp*0.2)+(temp*0.8);//фильтруем pre_temp=temp;//для фильтра в следующем цикле return temp; }тогда вместо
if(currentMillis-preTempMillis>=t_time){//пришлло время мерить температуру temp=analogRead(termo);//получаем температуру с первого temp=temp/2.06; temp=(pre_temp*0.2)+(temp*0.8);//фильтр pre_temp=temp; temp2=analogRead(termo2);//получаем температуру со второго temp2=temp2/2.06; temp=(pre_temp2*0.2)+(temp2*0.8);//фильтр pre_temp2=temp2; preTempMillis=currentMillis; }будет
if(currentMillis-preTempMillis>=t_time){ temp=getTempLM35(termo);//получаем температуру 1 temp2=getTempLM35(termo2);//получаем температуру 2 preTempMillis=currentMillis; }проблема у меня была в том что один бак охлаждаю, а второй частично и разница была в стартовых температурах, а так как мощность одинаковая даётся в одинаковый промежуток времени то они догнать по температуре друг друга не могли)
В таком случае обязательно применение второрого датчика, темболее что это не грозит большими затратами и усложнением кода. И часть кода которая отвечает за корректировку мощности выкинуть как ненужный костыль.
yul-i-an, а какой симулятор для ардуино вы используете?
Симулятор использую Proteus 7.7
Ну как Ваш аппарат, заработал?
Я тут в симуляторе пыряю алгоритм, так вот если заменить
на
то количество полупериодов сети прошедших в нагрузку получается пропорционально мощности (7% - 7 полупериодов).
Вот графики при мощности в 10%
Желтый - напряжение в нагрузку
Зеленый - управление на симистр
Но особой разницы в работе на глаз не заметите.
Может в классе DigOut переименовать blink в Lpwm думаю? А то будут думать что он только для мигания годиться.
Аппарат работает, разница в ТЭНах есть около 3-4 градусов но не критично, вообще разница около 7-8 градусов, но с учётом погрешности китайских мультиметров между ними разница порядка 3-4 градусов. Пока работаю на первом варианте.
Снимал реальные осциллограмы с ваших кодов, ну потом расскажу
Про осцилограммы очень интересно, буду ждать.
А первый варийант это какой, из какого поста?
Извиняюсь занят был, освобожусь скину благодарность, первый вариант это:
#define t_pwm 1000//период 100% мощности #define t_time 1000//период опроса температуры float t1=A0;// Вход АЦП 0 int outten=13; //для ПИ регулятора float p,i; long time;//интервал замера температуры long on_time=0;//для периода ШИМ int zad=0; float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры int w=0; float ust=489;//115 градусов int c=0;//пройденные циклы мощности void setup(){ pinMode(t1,INPUT); pinMode(outten,OUTPUT); time = millis(); on_time = time; } void loop(){ //---------------чтение температуры--------------- if ((millis()-time)>=t_time){ time=millis(); temp=analogRead(t1);//получаем температуру temp=(pre_temp*0.2)+(temp*0.8);//фильтр // w=abs((ust-temp)*(pre_temp-temp));//время выхода на уставку pre_temp=temp; PIctl();//расчет мощности } //---------------------управление мощностью-------------------------- //выдаем мощность if (millis()>3000){ if((millis()-on_time)>=t_pwm){//период ШИМ on_time=millis(); if(c<zad){ digitalWrite(outten,1); } else{ if (zad != 100){ digitalWrite(outten,0); } } c=c+1; if(c==100){ c=0; //PIctl(); } } } } void PIctl() { if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C { zad=i; } else { float e; e=(ust-temp); // расчет выходной мощности: p=65.0*e;//130 коффициент пропорциональности 130.0 if (p<0.0) { p=0.0;//ограничение P } i=(i+(e*0.014));//0.7 if (i>100.0) { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=p+i; } //ограничение управляющего сигнала //для исключения частого переключения реле //если твердотельное реле, можно не ограничивать if (zad<4){ zad=0; } if (zad>96){ zad=100; } //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust-2.0))//если темп < уст на 2С { zad=100;//то мощность 100% i=0; } if (temp>(ust+2.0))//если темп > уст на 2С { zad=0;//то мощность 0% i=0; } }Для первого варианта осциллограма выглядит так как всё время лог. 1 стоит, ну это понятно это же период ШИМ =1с. ( развёртка на осциле 50мс, осцил Хамелион, показания снимал задавая на вход АЦП вместо температурного датчика около 90-95% от необходимого входного для АЦП(от уставки)), так вот прямоугольные импульсы появляются как - то хаотично через разные промежутки времени , бывают как будтя два вместе соединяются, так то всё работает на симисторе, в моём устройстве, может это так осцил рисует.
С этим кодом(который на два выходы с коректировкой по мощности) всё по другому при том же периоде ШИМ=1с., прямоугольные импульсы появляются через одинаковые промежутки времени, и прямоугольные импульсы друг на друга не накладываются, в реальной работе не проверял. Праметры осцыла и задачи входного сигнала на АЦП те же. Коректировка мощности на другой канал работает.
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6 #define t_time 100//период опроса температуры DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2 #define termo A0//вход основного датчика температуры unsigned long time;//интервал замера температуры byte zad=0;//задание мощности ТЭНа1 byte zad2=0;//задание мощности ТЭНа2 float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры unsigned long preTempMillis=0;//для опроса температуры unsigned long prePIctl_time=0;//для расчета мощности float ust=0;//уставка #define t_pwm 1000//период медленного ШИМ в мс #define ust4 115//температура средний режим=115 void setup() { //analogReference(INTERNAL2V56); Serial.begin (9600); } void loop() { unsigned long currentMillis=millis(); if(currentMillis-preTempMillis>=t_time){ temp=analogRead(termo);//получаем температуру //а тут неплохобы в градусы целсия перевести temp=temp/2.06;//переводим показания АЦП в градусы С temp=(pre_temp*0.3)+(temp*0.7);//фильтр от резких изменений температуры pre_temp=temp; preTempMillis=currentMillis;//сброс таймера замера температуры } ust=ust4;//уставка if(currentMillis-prePIctl_time>1000){//если подошло время расчитать мощность zad=PIctl(temp, ust);//расчет мощности zad=pwr(zad,0);//корректировка мощности zad2=pwr(zad,10);//корректировка мощности prePIctl_time=currentMillis;//сброс таймера расчета мощности //Вывод в порт температуры и мощности Serial.print(temp); Serial.print("-T "); Serial.print(zad); Serial.print("-% "); Serial.print(zad2); Serial.println("-%"); } if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения устройства outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1 outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2 } }//END loop //функция расчета мощности по ПИ закону регулирования byte PIctl(float temp, float ust) { byte zad=0; static float i=0; if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C { return byte(i); } else//иначе { float e, p; e=(ust-temp);//вычисление ошибки регулирования // расчет выходной мощности p=(1.5*e);//расчет пропорциональной состовляющей if (p<0.0)//мощность не может быть отрицательной { p=0.0;//ограничение P } i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям if (i>100.0)//мощность не может быть больше 100% { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=byte(p+i); if(zad>100){return 100;} if(zad<0){return 0;} //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% i=0; } if (temp>(ust+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% i=0; } return zad; }/// } byte pwr(int zad, int correct) { int res = zad+correct; if(res>100){ res=100; } if(res<0){ res=0; } else { res=zad; } return res; }А вот такой код тоже на два выхода с корректировкой, в мониторе последовательного порта показывает 200%, на выходе только как я понимаю период ШИМ, а прямоугольные импульсы не появляются
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6 #define t_time 100//период опроса температуры DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2 #define termo A0//вход основного датчика температуры unsigned long time;//интервал замера температуры byte zad=0;//задание мощности ТЭНа1 byte zad2=0;//задание мощности ТЭНа2 float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры unsigned long preTempMillis=0;//для опроса температуры float ust=0; #define t_pwm 1000//период ШИМ в мс #define ust4 489//температура средний режим=115 void setup() { analogReference(INTERNAL2V56); } void loop() { unsigned long currentMillis=millis(); if(currentMillis-preTempMillis>=t_time){ temp=analogRead(termo);//получаем температуру //а тут неплохобы в градусы целсия перевести //temp=temp/4.25; temp=(pre_temp*0.2)+(temp*0.8);//фильтр pre_temp=temp; preTempMillis=currentMillis; } ust=ust4; zad=PIctl(temp, ust);//расчет мощности zad=pwr(zad,0);//корректировка мощности zad2=PIctl(temp, ust);//расчет мощности zad2=pwr(zad2,10);//корректировка мощности if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1 outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2 } }//END loop //расчет мощности byte PIctl(float temp, float ust) { static float p, i; if(temp>(ust-2.12)&&temp<(ust+2.12))//зона нечувствительности +-0,5C { zad=i;//если ошибка+-0.5С то мощность = И состовляющей } else//иначе { float e; e=(ust-temp);//вычисление ошибки регулирования // расчет выходной мощности: p=(65.0*e);//расчет пропорциональной состовляющей if (p<0.0)//мощность не может быть отрицательной { p=0.0;//ограничение P } i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям if (i>100.0)//мощность не может быть больше 100% { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=p+i; } //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% i=0; } if (temp>(ust+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% i=0; } } //корректировка мощности byte pwr(int zad, int correct) { int res = zad+correct; if(res>100){ return 100; } if(res<0 || zad == 0){ return 0; } return res; }Насчёт кода для применения двух датчиков и двух выходов разных, пост 64, а как выдать мощность на разные выхода, после получения показания с двух датчиков, не пойму.
Ну второй код мне кажеться получился более читаемым и прозрачным. А картинки с осцилогрофа какнибудь можете выложить, для второго кода?
Я на выходных этот код мучал, мне кажеться что он полностью рабочий
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6 #define t_time 1000//период опроса температуры #define power_time 1000//период расчета мощности DigOut outten(9), outten2(8), ind(13,0);//инициализация цифровых выходов на тен 1, 2 и индикатор #define termo A0//вход основного датчика температуры unsigned long time;//интервал замера температуры byte zad=0;//задание мощности ТЭНа1 byte zad2=0;//задание мощности ТЭНа2 float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры unsigned long preTempMillis=0;//для опроса температуры unsigned long prePIctl_time=0;//для расчета мощности float ust=0;//уставка #define t_pwm 1000//период медленного ШИМ в мс #define ust4 100//температура средний режим=100 void setup() { //analogReference(INTERNAL2V56); Serial.begin (9600); } void loop() { ust=ust4;//уставка unsigned long currentMillis=millis(); if(currentMillis-preTempMillis>=t_time){ temp = getTempLM35(termo); //сюда второй датчик preTempMillis=currentMillis;//сброс таймера замера температуры } if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность zad=PIctl(temp, ust);//расчет мощности //если два датчика то zad2= PIctl(temp2, ust); zad2=zad;//мощность одинаковая prePIctl_time=currentMillis;//сброс таймера расчета мощности //Вывод в порт температуры и мощности serialPrint(); } if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства outten.blink(t_pwm, ((t_pwm)/100)*zad);//управление теном 1 outten2.blink(t_pwm, ((t_pwm)/100)*zad2);//управление теном 2 ind=outten; } }//END loop //функция расчета мощности по ПИ закону регулирования byte PIctl(float temp, float ust) { byte zad=0; static float i=0; if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C { return byte(i); } else//иначе { float e, p; e=(ust-temp);//вычисление ошибки регулирования // расчет выходной мощности p=(1.5*e);//расчет пропорциональной состовляющей if (p<0.0)//мощность не может быть отрицательной { p=0.0;//ограничение P } i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям if (i>100.0)//мощность не может быть больше 100% { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=byte(p+i); if(zad>100){return 100;} if(zad<0){return 0;} //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% i=0; } if (temp>(ust+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% i=0; } }/// return zad; } //получение температуры с LM35 float getTempLM35(byte Ain) { static float pre_temp=0; float temp=analogRead(Ain);//получаем значение АЦП temp=temp/2.06;//переводим в градусы temp=(pre_temp*0.3)+(temp*0.7);//фильтруем pre_temp=temp;//для фильтра в следующем цикле return temp; } //вывод в монитор для отладки void serialPrint() { Serial.print(temp); Serial.print("-T "); Serial.print(zad); Serial.print("-% "); Serial.print(zad2); Serial.println("-%"); }Единственное останется настроить ПИ регулятор и если всётаки две разные емкости добавить второй датчик температуры, темболее что теперь это не составит труда.
А вот такой код тоже на два выхода с корректировкой, в мониторе последовательного порта показывает 200%, на выходе только как я понимаю период ШИМ, а прямоугольные импульсы не появляются
#include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6 #define t_time 100//период опроса температуры DigOut outten(9), outten2(12);//инициализация цифровых выходов на тен 1 и 2 #define termo A0//вход основного датчика температуры unsigned long time;//интервал замера температуры byte zad=0;//задание мощности ТЭНа1 byte zad2=0;//задание мощности ТЭНа2 float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры unsigned long preTempMillis=0;//для опроса температуры float ust=0; #define t_pwm 1000//период ШИМ в мс #define ust4 489//температура средний режим=115 void setup() { analogReference(INTERNAL2V56); } void loop() { unsigned long currentMillis=millis(); if(currentMillis-preTempMillis>=t_time){ temp=analogRead(termo);//получаем температуру //а тут неплохобы в градусы целсия перевести //temp=temp/4.25; temp=(pre_temp*0.2)+(temp*0.8);//фильтр pre_temp=temp; preTempMillis=currentMillis; } ust=ust4; zad=PIctl(temp, ust);//расчет мощности zad=pwr(zad,0);//корректировка мощности zad2=PIctl(temp, ust);//расчет мощности zad2=pwr(zad2,10);//корректировка мощности if (millis()>3000){//разрешаем управление нагрузкой через 3 сек после включения outten.blink(t_pwm, (t_pwm/100)*zad);//управление теном 1 outten2.blink(t_pwm, (t_pwm/100)*zad2);//управление теном 2 } }//END loop //расчет мощности byte PIctl(float temp, float ust) { static float p, i; if(temp>(ust-2.12)&&temp<(ust+2.12))//зона нечувствительности +-0,5C { zad=i;//если ошибка+-0.5С то мощность = И состовляющей } else//иначе { float e; e=(ust-temp);//вычисление ошибки регулирования // расчет выходной мощности: p=(65.0*e);//расчет пропорциональной состовляющей if (p<0.0)//мощность не может быть отрицательной { p=0.0;//ограничение P } i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям if (i>100.0)//мощность не может быть больше 100% { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=p+i; } //корректировка при отклонении температуры более чем на +-2С //для быстрого выхода на уставку if (temp<(ust-8.5))//если темп < уст на 2С { zad=100;//то мощность 100% i=0; } if (temp>(ust+8.5))//если темп > уст на 2С { zad=0;//то мощность 0% i=0; } } //корректировка мощности byte pwr(int zad, int correct) { int res = zad+correct; if(res>100){ return 100; } if(res<0 || zad == 0){ return 0; } return res; }Надо сравнить функцию ПИ регулятора с кодом из поста #75 (самая последняя версия), там помоему глюки были но я их исправил.
Выложу картинки попозже, а насчёт кода со вторым датчиком, то почему, мощность одинаковая, если допустим баки хоть и одинаковые, но допустим тот стартует с 30 градусов, а другой с 20 градусов ну если их не равномерно охладить, да и разница в ТЭНах в 3-4 градуса всё равно есть, хотя и мультиметры с термопарами около 2градусов погрешность, причём разница изначально в мультиметрах 2 градуса, а мы будем одинаковую мощность давать, или я что-то не пойму?
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность033zad=PIctl(temp, ust);//расчет мощности034//если два датчика то zad2= PIctl(temp2, ust);035zad2=zad;//мощность одинаковаяНасчёт кода для применения двух датчиков и двух выходов разных, пост 64, а как выдать мощность на разные выхода, после получения показания с двух датчиков, не пойму.
Есть секция получения температуры
if(currentMillis-preTempMillis>=t_time){ temp = getTempLM35(termo); temp2 = getTempLM35(termo2);//добавили второй датчик preTempMillis=currentMillis;//сброс таймера замера температуры }Есть секция расчета мощности по ПИ закону
if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность zad=PIctl(temp, ust);//расчет мощности для первой емкости zad2=PIctl(temp2, ust);//расчет мощности для второй емкости prePIctl_time=currentMillis;//сброс таймера расчета мощности //Вывод в порт температуры и мощности serialPrint(); }Есть секция управления нагрузкой
if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства outten.blink(t_pwm, ((t_pwm)/100)*zad);//управление теном 1 outten2.blink(t_pwm, ((t_pwm)/100)*zad2);//управление теном 2 ind=outten; } //тут ничего не меняемИ по анологии хоть 10 добавляйте, но если емкости разные, то придется переписать функцию расчета ПИ чтобы она принимала коэффициенты для каждой емкости свои.
Это изменения для последнего кода из поста #75 для работы с двумя каналами измерения/регулирования.
Баки одинаковые но охлаждаются возможно по разному, или даже одинаково но один куллер заклинет, да и говорю же что разница в ТЭНах хоть и не большая есть, а я просто не увидел секция управления нагрузкой, спасибо, просто и думаю зачем же два датчика если включать ТЭНы как и раньше, теперь увидел, спасибо.
Ну а если бы ёмкости были разные то можно же греть их на разные уставки и всё
Баки одинаковые но охлаждаются возможно по разному, или даже одинаково но один куллер заклинет, да и говорю же что разница в ТЭНах хоть и не большая есть, а я просто не увидел секция управления нагрузкой, спасибо, просто и думаю зачем же два датчика если включать ТЭНы как и раньше, теперь увидел, спасибо.
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Ну а если бы ёмкости были разные то можно же греть их на разные уставки и всё
Ну если Вам для процесса нужно поддерживать точную температуру, зачем уставки менять? Разные так разные регулятор для каждой свою мощность расчитает при одной уставке.
Если баки разные(конкретно допустим обьём у них в два раза отличается) их и греть допустим нужно для разной температуры, так как их емкость разная, и что бы выделилось одинаковое количество пара допустим. то и нужно к разным температурам. Это так теоретически. Вы конечно правы, для моего случая да допустим разница не большая и мощность ПИ-регулятор подберёт на каждый выходной канал свою в соответствии от входных сигналов. А вообще пока что использую первый вариант и он меня устраивает, а там и этот на два датчика может пригодится. Огромное спасибо за помощь
Вот рабочая версия функции расчета мощности
//функция расчета мощности по ПИ закону регулирования byte PIctl(float temp, float ust) { byte zad=0; static float i=0; if(temp>(ust-0.5)&&temp<(ust+0.5))//зона нечувствительности +-0,5C { zad=i; } else//иначе { float e, p; e=(ust-temp);//вычисление ошибки регулирования // расчет выходной мощности p=(1.5*e);//расчет пропорциональной состовляющей if (p<0.0)//мощность не может быть отрицательной { p=0.0;//ограничение P } i=(i+(e*0.014));//расчет интегральной составляющей = тепловым потерям if (i>100.0)//мощность не может быть больше 100% { i=100.0;//ограничение I } if (i<-100.0) { i=-50.0;//ограничение I } //расчет выходной мощности zad=byte(p+i); if(zad>100){zad = 100;} if(zad<0){zad = 0;} //для быстрого выхода на уставку if (temp<(ust-8.5))//если темп < уст на 8,5С { zad=100;//то мощность 100% i=0; } if (temp>(ust+3))//если темп > уст на 3С { zad=0;//то мощность 0% i=0; } } return zad; }Если всё еще актуально?!
Переписал ПИ регулятор. (более удобная настройка)
//расчет мощности по ПИ закону регулирования byte PIctl(float temp, float ust) { #define kP 65//коэффициент пропорциональности #define p_min 0.0//минимум П составляющей - не < 0 #define p_max 100.0//максимум П составляющей - не > 100 #define kI 0.014//коэффициент интегрирования #define i_max 50.0//максимум И составляющей #define i_min -50.0//минимум И составляющей #define no_ctl 0,5//зона от уставки нечувствительности +- #define d_ctl 2//зона +- от уставки - за пределами или 100% или 0% #define out_min 0//минимальный выходной % #define out_max 100//максимальный выходной % byte zad=0; static float i=0; if(temp>(ust-no_ctl)&&temp<(ust+no_ctl))//зона нечувствительности +- no_ctl { return byte(i); } else//иначе { float e, p; e=(ust-temp);//ошибка регулирования p=(kP*e);//расчет пропорциональной состовляющей (p<p_min)? p=p_min: (p>p_max)? p=p_max: p=p;//ограничение p i=(i+(e*kI));//расчет интегральной составляющей (i>i_max)? i=i_max: (i<i_min)? i=i_min: i=i;//ограничение i zad=byte(p+i);//вычесленное воздействие на объект в % (zad>out_max)? zad=out_max: (zad<out_min)? zad=out_min: zad=zad;//ограничение zad //При отклонении температуры более чем на +-d_ctl // для быстрого выхода на уставку if (temp<(ust-d_ctl))//если темп < уст на d_ctl { zad=out_max;//то мощность максимум i=0; } if (temp>(ust+d_ctl))//если темп > уст на d_ctl { zad=out_min;//то мощность минимум i=0; } return zad; } }Спасибо, как - нибудь опробую.
Еще к теме, последняя версия функции ПИ регулятора
//расчет мощности по ПИ закону регулирования #define kP 6//коэффициент пропорциональности #define p_min 0.0//минимум П составляющей - не < 0 #define p_max 100.0//максимум П составляющей - не > 100 #define kI 0.025//коэффициент интегрирования #define i_min 0.0//минимум И составляющей #define i_max 30.0//максимум И составляющей #define d_ctl 4.0//зона пропорциональности ust-d_ctl #define out_min 0//минимальный выходной % #define out_max 100//максимальный выходной % byte PIctl(float temp, uint8_t ust)//возвращает необходимую мощность { uint8_t out=0;//uint8_t static float i=0; float e, p; e=(ust-temp);//ошибка регулирования //расчет p p=(temp<ust-d_ctl)?p_max:(temp>ust)?p_min:(kP*e); //расчет i i=(i<i_min)?i_min:(i>i_max)?i_max:i+(kI*e); out=(p+i<out_min)?out_min:(p+i>out_max)?out_max:p+i; return out; }График выхода на уставку с 36 до 41С в течении 5-7 минут, точно не засекал. Ну и конечно всё зависит от kI и kP коэффициентов.
Синяя линия - текущая температура (С)
Красная - уставка (С)
Желтая - мощность (%)
Зеленая - ошибка регулирования (С)
Сиреневая - П составляющая (%)
Голубая - И составляющая (%)
Синяя - температура
Красная - уставка
Желтая - мощность
Я за вами не успеваю, сейчас занят, не когда заняться переработкой инфы от вас, и скинуть благодарность, как только так сразу
График выхода на уставку с 36 до 41С в течении 5-7 минут
А почему так долго? И что в вашем коде и вообще в коде Ардуино ИДЕ означают вопросительные знаки?
Долго из за не оптимальных настроек коэффициентов. И варийантов не много, или быстро с перерегулированием или дольше но без перерегулирования, выбирается под свои нужды.
Про знаки вопросов - (условие)? если истина : если лож ;
И что в вашем коде и вообще в коде Ардуино ИДЕ означают вопросительные знаки?
https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%80%D0%BD%D0%B0%D1%80%D0%BD...
https://habrahabr.ru/post/205848/
http://cppstudio.com/post/304/
http://purecodecpp.com/archives/554
Здравствуйте yul-i-an
Заинтересовал ваш код
интересует будет ли он работоспособен для arduino uno + ssr реле + тэн 2кВт(от эл.плиты)
хочу это все приспособить для коптильни
ps (arduino uno + ssr реле) заказал должно скоро прийти
Все будет работатть, только ж реле бери 15А и нужное напряжение коммутации, или ддва реле в параллель контакты, или у одного если есть две пары контактов по 10А в параллель.
Здравствуйте yul-i-an
Заинтересовал ваш код
интересует будет ли он работоспособен для arduino uno + ssr реле + тэн 2кВт(от эл.плиты)
хочу это все приспособить для коптильни
ps (arduino uno + ssr реле) заказал должно скоро прийти
Будет. Для этого и писалось.
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Ругается на эту строчку, на blink
Вот эта строчка формирует управляющие сигналы на симистор, в неё мы передаем период ШИМ - 1000мс (1с) и задание в %, для того чтобы % перевести в длину управляющего импульса мы период делим на 100% и получаем продолжительность импульса для 1-го %, а затем умножаем его на zad что нам дает в результате продолжительность управляющего воздействия на симистор для получения расчетной мощности zad.
Ругается на эту строчку, на blink
В Классе DigOut blink был заменен на lpwm
Спасибо, попробую.
Здравствуйте yul-i-an
Пришла плата и остальные детали
Пробую ваш код, если можно глянете правильно ли я все сделал.
[code] #include <DigOut.h>//подключаем библиотеку выходов yadi.sk/d/NM4S6HmFrTRu6 #define t_time 1000//период опроса температуры #define power_time 1000//период расчета мощности DigOut outten(9);//инициализация цифровых выходов на тен unsigned long time;//интервал замера температуры byte zad=0;//задание мощности ТЭНа1 float temp=0.0;//текущее значение температуры float pre_temp=0.0;//предыдущее значение температуры unsigned long preTempMillis=0;//для опроса температуры unsigned long prePIctl_time=0;//для расчета мощности float ust=0;//уставка #define t_pwm 1000//период медленного ШИМ в мс #define ust4 85//температура средний режим=100 ///------------------------ temp #include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 2//вход основного датчика температуры OneWire oneWire(ONE_WIRE_BUS); DallasTemperature datchik(&oneWire); //----------------------------------------- //функция расчета мощности по ПИ закону регулирования //расчет мощности по ПИ закону регулирования #define kP 6//коэффициент пропорциональности #define p_min 0.0//минимум П составляющей - не < 0 #define p_max 100.0//максимум П составляющей - не > 100 #define kI 0.025//коэффициент интегрирования #define i_min 0.0//минимум И составляющей #define i_max 30.0//максимум И составляющей #define d_ctl 1.5//зона пропорциональности ust-d_ctl #define out_min 0//минимальный выходной % #define out_max 100//максимальный выходной % byte PIctl(float temp, uint8_t ust)//возвращает необходимую мощность { uint8_t out=0;//uint8_t static float i=0; float e, p; e=(ust-temp);//ошибка регулирования //расчет p p=(temp<ust-d_ctl)?p_max:(temp>ust)?p_min:(kP*e); //расчет i i=(i<i_min)?i_min:(i>i_max)?i_max:i+(kI*e); out=(p+i<out_min)?out_min:(p+i>out_max)?out_max:p+i; return out; } //вывод в монитор для отладки void serialPrint() { Serial.print(temp); Serial.println("-T "); Serial.print(zad); Serial.print("-% "); } void setup() { datchik.begin(); Serial.begin (9600); } void loop() { ust=ust4;//уставка unsigned long currentMillis=millis(); if(currentMillis-preTempMillis>=t_time){ temp=datchik.getTempCByIndex(0);//получаем температуру preTempMillis=currentMillis;//сброс таймера замера температуры } if(currentMillis-prePIctl_time>=power_time){//если подошло время расчитать мощность zad=PIctl(temp, ust);//расчет мощности prePIctl_time=currentMillis;//сброс таймера расчета мощности //Вывод в порт температуры и мощности serialPrint(); } if (currentMillis>5000){//разрешаем управление нагрузкой через 3 сек после включения устройства outten.lpwm(t_pwm, zad);//управление теном 1 //ind=outten;// ? } }//END loop [/code]При компиляции скетча ругался на ind=outten; убрал вроде все пошло.
Также заменил как вы писали outten.lpwm(t_pwm, zad);
Вопрос, нужную мне температуру я задаю переменной zad ?
Не могу понять для чего #define ust4 85//температура средний режим, что значит средний режим?
Для чего две ust и ust4 если всеровно ust=ust4;
Спасибо.
ust - это уставка температуры
ust4 - можете выбросить
необходимую температуру просто передовайте в переменную ust (ust=54; например) или любым иным способом.
чтобы работал индикатор на 13 пине (ind) нужно после строки
добавить
и раскоментировать
Встроеный светодиод на 13 пине отображает состояние выхода на тен.
Спасибо. Попробую. Блин Ind пропустил... А так на ваш взгляд всё правильно?
Номер тел или карты для возногрождения?