«Нагрев печи. Контроллер печи шаг за шагом. Сайт о микроконтроллерах AVR.RU», версия для печати. Исходный документ: https://avr.ru/beginer/devices/furnace/heat_element

Нагрев

Итак, самое главное, что должен делать контроллер печи – это управлять нагревательным элементом.
При этом управлять напрямую мощностью нагрева через подачу различного напряжения («чуть-чуть», вполсилы включить) – мы не можем. Точнее, можем, конечно – например, поставить какой-нибудь подстроечный резистор – но вся проблема в том, что вся энергия, которую мы «отберем» у печки, пойдет в тепло на этот самый резистор (смотрим закон Ома, определение мощности и страдаем -_-).
Так что единственное, что мы можем делать – это включать-выключать весь нагревательный элемент.
Попробуем рассмотреть, как это можно сделать:

  1. Включение-выключение

Это самое простое, что можно придумать: печка остыла – включаем нагрев, печка достигла нужной температуры – выключаем нагрев, печка снова остыла – включаем нагрев и так далее.
Результат у нас будет примерно такой:


Рисунок 1. Динамика изменения температуры во времени при включении-выключении нагрева.

Красным на рисунке отображено состояние, когда нагрев включен, черным – выключен.
Так, при нагреве мы «перескакиваем» нужное нам значение температуры – даже после отключения печки температура по инерции продолжает повышаться; затем, когда печка начинает остывать и доходит до нужного нам значения, мы снова включаем нагрев – но вот незадача, температура все равно падает, опять же по инерции! Так и приходится работать по синусоиде – получается не очень-то красиво.

  1. Простой ШИМ

Мы определили, что включать нагрев чуть-чуть мы не можем – но у нас есть такая вещь, как ШИМ!
ШИМ – широтно-импульсная модуляция; «это способ кодирования аналогового сигналa путём изменения ширины (длительности) прямоугольных импульсов несущей частоты».
Теперь попробуем объяснить понятней: мы не можем задавать напряжение на ножке микроконтроллера – у нас есть либо вариант «логическая 1» - оно же напряжение питания – либо «логический 0» - подтяжка к земле.
Однако если мы будем в течение не очень большого времени посылать то 1, то 0, то «в среднем» будет уже другая ситуация:


Рисунок 2. Пример ШИМ с различной скважностью.

Посмотрим характеристики ШИМа:


Рисунок 3. Характеристики ШИМ.

Период – это длительность прямоугольного импульса
Скважность – это соотношение длительности логической 1 к периоду.
Квант ШИМа – это минимальная длительность логической 1 в импульсе.
Если говорить об аппаратных ШИМах в avr, то квант ШИМа – один «тик» соответствующего таймера.

Используя ШИМ, мы сможем, оперируя включением-выключением, перейти к работе с мощностью: сделать нагрев 10%, 50%, 90%...
Понятно, что для каждой температуры в пределах разумного мы можем подобрать такое значение мощности, при котором бы эта самая температура оставалась неизменной, то есть Wнагрева = Wрассеивания (как в кухонных духовках).
Теперь основная проблема – а как же сопоставить эти самые мощность (W) и температуру (T)
Мы знаем, как зависит температура в печке от мощности: в идеальных условиях это линейная зависимость T = a * W + b, а вот в реальности – увы, из-за конвекции воздуха зависимость преобретает примерно следующий вид:


Рисунок 4.Зависимость температуры от мощности нагрева.

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

Вроде бы все хорошо, и решение, в принципе, найдено: каким-нибудь образом – например, калибровкой или настройкой – мы сопоставим мощность температуре. будем использовать ШИМ, и тогда у нас практически не будет перевалов через заданную температуру, мы будем спокойно нагревать печку такой силой, чтобы температура не менялась.
Но, к сожалению, реальность разбивает наши мечты.
Вся проблема в том, что частота сети у нас – 50 Гц. То есть, мы не можем включать-выключать питание нагревательного элемента чаще, чем 1/50 с, более того, лучше не делать этого чаще, чем 1/16 – 1/8 с. Это проблема номер раз – таким образом, и квант ШИМа должен быть не быстрее, чем 1/16 секунды.

Таймер/счётчик 0 и таймер/счётчик 2 в асинхронном режиме уже заняты (так, в асинхронном режиме квант ШИМа получается 8/32768 ˜ 0.00024 с – очень мало); попробуем рассчитать квант ШИМа для обычного 16-разрядного таймера с максимальным предделителем 1024: получается 1024/8000000 = 0.000128 c. Слишком быстро! Но можно считать квант ШИМа как два тика таймера, как три… Оптимальным числом будет являться 1000 – тогда квант будет 1024/8000000*1000 = 0.128 с. Неплохо вроде, жить можно… А теперь ещё немного разочарования: тТаймер у нас 16-разрядный, то есть считает до 65535; но если квант ШИМа – это 1000 тиков таймера, то… Получаем 65635/1000 = 65 возможных значений ШИМа. Маловато будет!
Так что аппаратный таймер, похоже, нам не светит. Будем делать программный, сами, тогда сможем задать и длительность кванта ШИМа, и количество «делений» ШИМа.
Едем дальше.
Представим, что мы измеряем температуру от 0 до 300 градусов с точностью 0.5 градуса – то есть, всего 600 возможных значений.
Чтобы точно сопоставить температуре мощность, нужно использовать градацию мощности большую, чем градация температуры, иначе мы получим такую ситуацию: например, нам нужно поддерживать в печке температуру 100 ˚С; при этом при значении ШИМа «1000» поддерживается температура 99.5 ˚С, а при значении «1001» - температура 100.5 ˚С. Проблема? Проблема!
Тогда или мы делаем градаций ШИМа в несколько раз больше, чем градаций температуры – например, раз в 10 – но тогда понятно, что период ШИМа затянется на много секунд, и это будет очень неторопливая печка, или, например, можно использовать ШИМ вместе с выключением – нагрели до 100.5 градусов, выключили, снова нагрели… Тут тоже будет «синусоида», как и в первом варианте – простом включении-выключении, но уже с меньшей амплитудой.

  1. Комбинированный ШИМ

Как же реализовать ШИМ, который будет точно подходить к заданному значению температуры, и при этом у него будет не очень большой период?
А вот как – через «чередование»: можно сделать так, чтобы значение ШИМа было то 1000, то 1001 – если смотреть предыдущий пример.


Рисунок 5.Схема комбинированного ШИМ.

 

Таким образом, в зависимости от доли того или иного значения ШИМа мы будем ближе к той или иной температуре (так, если будем каждый раз чередовать 1000 и 1001 значение, то получим среднее – 100 ˚С; если будем 3 раза пускать ШИМ со значением 1000, а на четвертый делать значение 1001, то получим температуру 99.75 ˚С и т.д.)
При этом, если бы мы могли работать с аппаратным ШИМом, нужно было бы переключать значения не очень часто (уж точно не каждый цикл).
По сути своей, комбинированный ШИМ – это ШИМ в ШИМе).
Итак, попробуем сперва реализовать программный ШИМ, который будет включать-выключать нагревательный элемент с заданной частотой.


Рисунок 6.Схема виртуальной машины ШИМ нагревательного элемента.

Получаем следующий заголовочный файл:

//управление нагревательным элементом с использованием ШИМ-а; включается "нулём"
#ifndef HEAT_ELEMENT_H
#define HEAT_ELEMENT_H

#include "ioavr.h"
#include "inavr.h"
#include "stdint.h"
#include "Timings.h"

//максимальное значение счётчика печки
#define HE_PWM_MAX_COUNTER (CLOCK_TIMER_INTERRUPTS_PER_SECOND * 8)

//состояния ШИМ-а печки
typedef enum {hpwmOff, hpwmLevelOn, hpwmWaitLevelOff, hpwmLevelOff, hpwmWaitLevelOn} THE_PWM_State;
typedef struct
{
  //физическое расположение вывода для управления нагревательным элементом: DDR, PORT и номер вывода
  uint8_t *heDdr;
  uint8_t *hePort;
  uint8_t hePinMask;                                                       
  uint8_t heIsOn: 1;                                                            //флаг "печь включена"
  THE_PWM_State hePWMState;                                                     //текущее состояние ШИМ
  volatile uint8_t hePWMCounter;                                                         //счётчик ШИМ
  uint8_t hePWMLevel;                                                           //уровень ШИМ
  
} TVMHeatElement; 

extern TVMHeatElement VMHeatElement;

//прерывание для управления нагревательным элементом (счётчик ШИМ)
void HEMachineInterrupt();
//инициализация виртуальной машины управления нагревательным элементом
void HEMachineInit(uint8_t *HeatDdr, uint8_t *HeatPort, uint8_t HeatPinNumber);
//виртуальная машина управления нагревательным элементом
void HEMachine();
//включение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOn();
//выключение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOff();
//установка уровня ШИМ нагревательного элемента
void HESetPWMLevel(uint8_t PWMLevel);
#endif

Файл .c:

#include "HeatElement.h"

TVMHeatElement VMHeatElement;

//включение нагревательного элемента
#pragma inline=forced
void HEOn()
{
  *(VMHeatElement.hePort) &= ˜VMHeatElement.hePinMask;
}
//выключение нагревательного элемента
#pragma inline=forced
void HEOff()
{
  *(VMHeatElement.hePort) |= VMHeatElement.hePinMask;
}
//включение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOn()
{
  VMHeatElement.heIsOn = 1;
}
//выключение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOff()
{
  VMHeatElement.heIsOn = 0;
  HEOff();  
}
//инициализация виртуальной машины управления нагревательным элементом
void HEMachineInit(uint8_t *HeatDdr, uint8_t *HeatPort, uint8_t HeatPinNumber)
{
  VMHeatElement.heDdr = HeatDdr;
  VMHeatElement.hePort = HeatPort;
  VMHeatElement.hePinMask = 1 << HeatPinNumber;
  //настраиваем вывод нагревательного элемента: на выход с подтяжкой (нагревательный элемент выключен)
  *VMHeatElement.hePort |= VMHeatElement.hePinMask;
  *VMHeatElement.heDdr |= VMHeatElement.hePinMask;
  VMHeatElement.heIsOn = 0;
  VMHeatElement.hePWMCounter = 0;
  VMHeatElement.hePWMLevel = 0;
  VMHeatElement.hePWMState = hpwmOff;
}

//прерывание для управления нагревательным элементом (счётчик ШИМ)
void HEMachineInterrupt()
{
  if ((VMHeatElement.hePWMState != hpwmOff) && (VMHeatElement.hePWMCounter < HE_PWM_MAX_COUNTER))
   VMHeatElement.hePWMCounter++;
}
//виртуальная машина управления нагревательным элементом
void HEMachine()
{
  switch (VMHeatElement.hePWMState)
  {
  case hpwmOff:
    //проверяем на необходимость включения
    if (VMHeatElement.heIsOn)
    {
      VMHeatElement.hePWMCounter = 0;
      VMHeatElement.hePWMState = hpwmLevelOn;
    }
    break;
  case hpwmLevelOn:
    //включаем нагревательный элемент
    HEOn();
    VMHeatElement.hePWMState = hpwmWaitLevelOff;
    break;
  case hpwmWaitLevelOff:
    //проверяем, не нужно ли выключить машину
    if (VMHeatElement.heIsOn == 0)
    {
      VMHeatElement.hePWMState = hpwmOff;
      HEOff();
    }
    //проверяем, не нужно ли выключить нагревательный элемент (дошли до "уровня" ШИМа)
    else if (VMHeatElement.hePWMCounter >= VMHeatElement.hePWMLevel)
      VMHeatElement.hePWMState = hpwmLevelOff;
    break;
  case hpwmLevelOff:
    HEOff();
    VMHeatElement.hePWMState = hpwmWaitLevelOn;
    break;
  case hpwmWaitLevelOn:
    //проверяем, не нужно ли выключить машину
    if (VMHeatElement.heIsOn == 0)
    {
      VMHeatElement.hePWMState = hpwmOff;
      HEOff();
    }
    //проверяем, не нужно ли включить нагревательный элемент (дошли до максимума счётчика ШИМа)
    else if (VMHeatElement.hePWMCounter == HE_PWM_MAX_COUNTER)
    {
      VMHeatElement.hePWMCounter = 0;
      VMHeatElement.hePWMState = hpwmLevelOn;
    }
    break;
  }
}

//установка уровня ШИМ нагревательного элемента
void HESetPWMLevel(uint8_t PWMLevel)
{
  if (PWMLevel < HE_PWM_MAX_COUNTER)
    VMHeatElement.hePWMLevel = PWMLevel;
  else
    VMHeatElement.hePWMLevel = HE_PWM_MAX_COUNTER;
}

Добавляем в основном файле функцию прерывания в прерывание часового таймера, и пишем основную функцию:

//прерывание таймера 2 на часовом кварце
#pragma vector = TIMER2_OVF_vect
__interrupt void ClockTimerInterrupt(void)
{
	LogicalButtonMachineInterrupt();																							//работа с счётчиком длительности нажатия для логических кнопок
  EncoderCounterMachineInterrupt();
  FurnaceTimerInterrupt();
  HEMachineInterrupt();
}
...
void main( void )
{
  DDRB = 0;
  PORTB = 0;
  DDRD = 0;
  PORTD = 0;
  DDRC = 0;
  PORTC = 0;
  
  DisplayInit();
  InitButtonsAndEncoder();
  ADCInit();
  FurnaceTimerInit();
  HEMachineInit((uint8_t*)&DDRC, (uint8_t*)&PORTC, 1);
  HESetPWMLevel(HE_PWM_MAX_COUNTER / 3);
  HEMachineOn();
  FastTimerInit();
  ClockTimerInit();
  while(1)
  {
    HEMachine();
  }
}


Рисунок 6. Пример работы ШИМ.

Период ШИМ – 8 секунд, как и планировалось.

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

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


Рисунок 7. Формула ПИД-регулятора.

Где e(t) – текущее значение ошибки, то есть разница между заданной и текущей температурой, а Kp, Ki , Kd – соответственно, коэффициенты ПИД-регулятора. Выглядит жутко, да.
Для программной реализации мы будем использовать дискретные формулы:

u(t) = P (t) + I (t) + D (t)

P(t) = Kp * e (t)

I(t) = I (t — 1) + Ki * e (t)

D(t) = Kd * {e (t) — e (t — 1)}

Таким образом, пропорциональная составляющая зависит исключительно от разницы между текущей и заданной температуры, интегральная – накапливает ошибку, и постепенно её влияние возрастает (работает как бы «с запаздыванием»), а дифференциальная составляющая пропорциональна темпу изменений («придает ускорение»). Отметим сразу, что коэффициент интегральной составляющей обычно меньше единицы; при этом получение данных и, соответственно, регулирование уровня ШИМ должно производиться через равные промежутки времени – поэтому машина контроля за уровнем нагрева будет выглядеть примерно так же, как и машина отображения кухонного таймера или машина отображения текущей температуры – в прерывании будет некоторый счётчик, и по достижению максимума в основной программе будет запускаться функция регулировки уровня ШИМ нагрева печи.

Добавим в библиотеку ADCTemperature переменную, хранящую заданную температуру.

Так выглядит заголовочный файл:

//управление нагревательным элементом с использованием ШИМ-а; включается "нулём"
#ifndef HEAT_ELEMENT_H
#define HEAT_ELEMENT_H

#include "ioavr.h"
#include "inavr.h"
#include "stdint.h"
#include "Timings.h"
#include "ADCTemperature.h"

//максимальное значение счётчика ШИМ
#define HE_PWM_MAX_COUNTER (CLOCK_TIMER_INTERRUPTS_PER_SECOND * 8)
//маскимальное значение счётчика контроля (частота вызова ПИД-регулятора)
#define HE_PID_MAX_COUNTER (CLOCK_TIMER_INTERRUPTS_PER_SECOND)
//максимальная энергия нагрева
#define HE_MAX_ENERGY 2000																					

//состояния ШИМ-а печки
typedef enum {hpwmOff, hpwmLevelOn, hpwmWaitLevelOff, hpwmLevelOff, hpwmWaitLevelOn} THE_PWM_State;
typedef struct
{
  //физическое расположение вывода для управления нагревательным элементом: DDR, PORT и номер вывода
  uint8_t *heDdr;
  uint8_t *hePort;
  uint8_t hePinMask;                                                       
  uint8_t heIsOn;                                                               //флаг "печь включена"
  uint8_t heOnNeedControl;                                                      //посылка о необходимости пересчётка уровня ШИМ
  THE_PWM_State hePWMState;                                                     //текущее состояние ШИМ
  volatile uint8_t hePWMCounter;                                                //счётчик ШИМ
  uint8_t hePWMLevel;                                                           //уровень ШИМ
  
  volatile uint8_t hePIDCounter;                                                //счётчик частоты регулирования уровня ШИМ
  double hePIDPCoef;
  double hePIDICoef;
  double hePIDDCoef;
  double hePIDIValue;
  double hePIDTemperatureDiff;
} TVMHeatElement; 

extern TVMHeatElement VMHeatElement;

//прерывание для управления нагревательным элементом (счётчик ШИМ)
void HEMachineInterrupt();
//инициализация виртуальной машины управления нагревательным элементом
void HEMachineInit(uint8_t *HeatDdr, uint8_t *HeatPort, uint8_t HeatPinNumber);
//виртуальная машина управления нагревательным элементом
void HEMachine();
//включение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOn();
//выключение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOff();
//установка уровня ШИМ нагревательного элемента
void HESetPWMLevel(uint8_t PWMLevel);

#endif

Файл .c:

#include "HeatElement.h"

TVMHeatElement VMHeatElement;

//включение нагревательного элемента
#pragma inline=forced
void HEOn()
{
  *(VMHeatElement.hePort) &= ˜VMHeatElement.hePinMask;
}
//выключение нагревательного элемента
#pragma inline=forced
void HEOff()
{
  *(VMHeatElement.hePort) |= VMHeatElement.hePinMask;
}
//включение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOn()
{
  VMHeatElement.heIsOn = 1;
}
//выключение виртуальной машины управления нагревательным элементом
#pragma inline=forced
void HEMachineOff()
{
  VMHeatElement.heIsOn = 0;
  HEOff();
}
//инициализация виртуальной машины управления нагревательным элементом
void HEMachineInit(uint8_t *HeatDdr, uint8_t *HeatPort, uint8_t HeatPinNumber)
{
  VMHeatElement.heDdr = HeatDdr;
  VMHeatElement.hePort = HeatPort;
  VMHeatElement.hePinMask = 1 << HeatPinNumber;
  //настраиваем вывод нагревательного элемента: на выход с подтяжкой (нагревательный элемент выключен)
  *VMHeatElement.hePort |= VMHeatElement.hePinMask;
  *VMHeatElement.heDdr |= VMHeatElement.hePinMask;
  VMHeatElement.heIsOn = 0;
  VMHeatElement.hePWMCounter = 0;
  VMHeatElement.hePWMLevel = 0;
  VMHeatElement.hePWMState = hpwmOff;
  
  VMHeatElement.hePIDCounter = 0;
  VMHeatElement.hePIDPCoef = 5;
  VMHeatElement.hePIDICoef = 0;
  VMHeatElement.hePIDDCoef = 0;
  VMHeatElement.hePIDIValue = 0;
  VMHeatElement.hePIDTemperatureDiff = 0;
}

//прерывание для управления нагревательным элементом (счётчик ШИМ)
void HEMachineInterrupt()
{
  if (VMHeatElement.heIsOn) 
  {
    //увеличение счётчика ШИМ
    if (VMHeatElement.hePWMCounter < HE_PWM_MAX_COUNTER)
      VMHeatElement.hePWMCounter++;
    //увеличение счётчика ПИД-регулятора
    if (VMHeatElement.hePIDCounter < HE_PID_MAX_COUNTER)
      VMHeatElement.hePIDCounter++;
  }
}

//установка уровня ШИМ нагревательного элемента
void HESetPWMLevel(uint8_t PWMLevel)
{
  if (PWMLevel < HE_PWM_MAX_COUNTER)
    VMHeatElement.hePWMLevel = PWMLevel;
  else
    VMHeatElement.hePWMLevel = HE_PWM_MAX_COUNTER;
}

//всмопогательная функция - виртуальная машина управления ШИМ
#pragma inline=forced
void HEPWMMachine()
{
  switch (VMHeatElement.hePWMState)
  {
  case hpwmOff:
    //проверяем на необходимость включения
    if (VMHeatElement.heIsOn)
    {
      VMHeatElement.hePWMCounter = 0;
      VMHeatElement.hePWMState = hpwmLevelOn;
    }
    break;
  case hpwmLevelOn:
    //включаем нагревательный элемент
    HEOn();
    VMHeatElement.hePWMState = hpwmWaitLevelOff;
    break;
  case hpwmWaitLevelOff:
    //проверяем, не нужно ли выключить машину
    if (VMHeatElement.heIsOn == 0)
    {
      VMHeatElement.hePWMState = hpwmOff;
      HEOff();
    }
    //проверяем, не нужно ли выключить нагревательный элемент (дошли до "уровня" ШИМа)
    else if (VMHeatElement.hePWMCounter >= VMHeatElement.hePWMLevel)
      VMHeatElement.hePWMState = hpwmLevelOff;
    break;
  case hpwmLevelOff:
    HEOff();
    VMHeatElement.hePWMState = hpwmWaitLevelOn;
    break;
  case hpwmWaitLevelOn:
    //проверяем, не нужно ли выключить машину
    if (VMHeatElement.heIsOn == 0)
    {
      VMHeatElement.hePWMState = hpwmOff;
      HEOff();
    }
    //проверяем, не нужно ли включить нагревательный элемент (дошли до максимума счётчика ШИМа)
    else if (VMHeatElement.hePWMCounter == HE_PWM_MAX_COUNTER)
    {
      VMHeatElement.hePWMCounter = 0;
      VMHeatElement.hePWMState = hpwmLevelOn;
    }
    break;
  }
}

//всмопогательная функция - виртуальная машина ПИД-регулятора
#pragma inline=forced
void HEPIDMachine()
{
  double currTemperatureDiff, Energy;
  if ((VMHeatElement.heIsOn) && (VMHeatElement.hePIDCounter >= HE_PID_MAX_COUNTER))
  {
    //пересчитываем уровень ШИМ
    if (ADCGetNewTemperatureValue())                                            //если есть новое значение температуры
    {
      //пересчитываем разницу температур
      currTemperatureDiff = ADCTemperature.atSetTemperature - ADCTemperature.atCurrTemperature;
      //пересчитываем I-составляющую
      VMHeatElement.hePIDIValue += VMHeatElement.hePIDICoef * currTemperatureDiff; 
      Energy = (VMHeatElement.hePIDPCoef * currTemperatureDiff) +                                         //P-составляющая
               VMHeatElement.hePIDIValue +                                                                //I-составляющая
               (VMHeatElement.hePIDDCoef * (currTemperatureDiff - VMHeatElement.hePIDTemperatureDiff));   //D-составляющая
      VMHeatElement.hePIDTemperatureDiff = currTemperatureDiff;
      //ограничиваем мощность между 0 и максимально допустимой
      if (Energy < 0)
        Energy = 0;
      else if (Energy > HE_MAX_ENERGY)
        Energy = HE_MAX_ENERGY;
      //устанавливаем новый уровень ШИМ
		  HESetPWMLevel((uint8_t)((double)(Energy / HE_MAX_ENERGY) * HE_PWM_MAX_COUNTER));      
      VMHeatElement.hePIDCounter = 0;
    }
  }
}
//виртуальная машина управления нагревательным элементом
void HEMachine()
{
  HEPWMMachine();
  HEPIDMachine();
}

Вот проект (http://avr.ru/int/Files/Download/beginners/devices/furnace/HeatElement.rar). Для того, чтобы проверить его работу, в функцию машины PID-регулятора можно добавить отображение значения текущего уровня ШИМ нагрева на дисплее, и попробовать нагреть конец термопары - тогда уровень ШИМ будет постепенно снижаться; прошивка такой программы лежит тут (http://avr.ru/int/Files/Download/beginners/devices/furnace/HeatElementHex.rar).

Основная проблема – настроить коэффициенты ПИД-регулятора. Есть несколько способов: самый простой и горячо любимый – метод научного тыка. О нём хорошо рассказано у наших товарищей (http://we.easyelectronics.ru/Theory/pid-regulyatory--dlya-chaynikov-praktikov.html).
Также есть метод Циглера-Никольса – он требует экспериментальных данных на реальном объекте: сначала используем P-регулятор (убираем I- и D-составляющие), P-коэффициент увеличиваем до тех пор, пока на выходе системы (это показания текущей температуры) не установятся колебания с постоянной амплитудой. Тогда текущее значение P-коэффициента фиксируется (Kp*), также измеряется период установившихся колебаний (T).
Значения коэффициентов ПИД-регулятора будут рассчитываться по следующим формулам:

Kp = 0.60 * Kp*

Ki  = 1.2 * Kp* / T

Kd = 0,075 * Kp* * T

После установки

Наверх (#top)

Автор - Moriam (http://forum.avr.ru/member.php?u=69506)  =ˆˆ=

Обсудить на форуме (http://forum.avr.ru/sozdanie-ustroystv-shag-za-shagom-t36264.html)

Все права защищены © AVR.RU, 2021.