PID AutoTune - out of tune

Hi

I am doing a project which consists of a ceramic heater that heats up a PCB board, a PWM AC Load controller is used in relation to the input value from a K-Type Thermocouple shield from Adafruit.

The main purpose is to set a predefined temperature using a PID controller.

To experiment I am using a filament bulb to simulate the ceramic heater to be safe. Where the bulb heats up as it lights up to a set temperature and the PID keeps it constant.

I have managed to tune the PID by hand but since there is an Autotuner for a PID I tried to implement it in the project but it seems that it's not working as after it tuned to the parameters it calculated it is not even reaching the setpoint.

Below is the code for my experiment.

#include <PID_v1.h>
#include <PID_AutoTune_v0.h>
#include <SPI.h>
#include "Adafruit_MAX31855.h"

//#define input_pin  5
#define DO   11
#define CS   12
#define CLK  13

int btm_AC_LOAD = 2;    // Output to Opto Triac pin
double High_Value = 16000; //16383; // Lamp Fully Off
double Low_Value = 1000;    // Lamp Fully On
double btm_set_value = 230;
double btm_input = 0, btm_output = 0;
double Kp = 900, Ki = 250, Kd = 100; // PID manual tunings that work 

byte ATuneModeRemember = 2;
boolean tuning = false;
double aTuneStep = 100, aTuneNoise = 1, aTuneStartValue = 2000;
unsigned int aTuneLookBack = 20;

Adafruit_MAX31855 thermocouple(CLK, CS, DO);
PID btm_PID(&btm_input, &btm_output, &btm_set_value, Kp, Ki, Kd, REVERSE);
PID_ATune btm_aTune(&btm_input, &btm_output);

void setup()
{  
  btm_input = thermocouple.readDigital();
  
  btm_PID.SetMode(AUTOMATIC);
  btm_PID.SetOutputLimits(Low_Value,High_Value);
  btm_PID.SetSampleTime(200);

  btm_aTune.SetControlType(1);

  if (tuning)
  {
    tuning = false;
    changeAutoTune();
    tuning = true;
  }
  
  pinMode(btm_AC_LOAD, OUTPUT);        // Set the AC Load as output
  attachInterrupt(1, zero_crosss_int, RISING);  // Choose the zero cross interrupt # from the table above
  
  delay(500);
  Serial.begin(57600);
}

void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
{
  // Firing angle calculation :: 50Hz-> 10ms (1/2 Cycle)
  // (10000us - 10us) / 16383 = 0.6 (Approx)
  int btm_dimtime = (0.6*btm_output);      
  delayMicroseconds(btm_dimtime);    // Off cycle
  digitalWrite(btm_AC_LOAD, HIGH);   // triac firing
  delayMicroseconds(10);         // triac On propogation delay
  digitalWrite(btm_AC_LOAD, LOW);    // triac Off
}

void loop()
{
  btm_input = thermocouple.readDigital();  
  if (tuning)
  {
    Serial.println("tuning is true");
    byte val = (btm_aTune.Runtime());
    if (val!=0)
    {
      tuning = false;
      Serial.println("tuning soon");
    }
    if (!tuning)
    {
      Kp = btm_aTune.GetKp();
      Ki = btm_aTune.GetKi();
      Kd = btm_aTune.GetKd();
      btm_PID.SetTunings(Kp, Ki, Kd);
      AutoTuneHelper(false);
      Serial.println("tuning completed");
    }
  }
  else 
  {
    btm_PID.Compute();
  }
  receiveSerial();
  showSerial();
}

void showSerial()
{
  double btm_temp = thermocouple.readCelsius();
  Serial.print("Temperature: "); Serial.println(btm_temp);  
  Serial.print("Sensor Value: "); Serial.println(btm_input); 
  Serial.print("Output Value: "); Serial.println(btm_output);
  Serial.println();
  if (tuning)
  {
    Serial.println("Tuning Mode");
  }
  else
  {
    Serial.print("Kp = ");Serial.println(btm_PID.GetKp());
    Serial.print("Ki = ");Serial.println(btm_PID.GetKi());
    Serial.print("Kd = ");Serial.println(btm_PID.GetKd());
    Serial.println();
  }
}

void receiveSerial()
{
  if(Serial.available())
  {
   char b = Serial.read(); 
   Serial.flush(); 
   if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune();
  }
}

void AutoTuneHelper(boolean start)
{
  if (start)
  {
    ATuneModeRemember = btm_PID.GetMode();
  }
  else 
  {
    btm_PID.SetMode(ATuneModeRemember);
  }
}

void changeAutoTune()
{
  if(!tuning)
  {
    btm_output = aTuneStartValue;
    btm_aTune.SetNoiseBand(aTuneNoise);
    btm_aTune.SetOutputStep(aTuneStep);
    btm_aTune.SetLookbackSec((int)aTuneLookBack);
    AutoTuneHelper(true);
    tuning = true;
  }
  else
  {
    btm_aTune.Cancel();
    tuning = false;
    AutoTuneHelper(false);
  }
}

void SerialReceive()
{
  if(Serial.available())
  {
   char b = Serial.read(); 
   Serial.flush(); 
   if((b=='1' && !tuning) || (b!='1' && tuning))changeAutoTune();
  }
}

I am using the PID in REVERSE Mode since to switch off the AC LOAD the PID must increase the output value. There is a minimum and maximum output values since beyond which my AC output will go out of synch and start to oscillates.

Now the main issue is that from what I imagine, the Autotune feature should start to oscillate the output from the predefined start value > aTuneStartValue, and oscillate using the step value > aTuneStep, but, what it is happening is that it only outputs as 1900 or 2100 during tuning.

I did some research and found that this could be an issue since the PID is in REVERSE Mode and not DIRECT but I changed the code in the library in the AutoTune library as per user > bmandl in this topic Reverse type of controller · Issue #14 · br3ttb/Arduino-PID-AutoTune-Library · GitHub but with no luck.

Can anyone explain to me how to properly use AutoTune or maybe point out to me what I'm doing wrong

Thanks in advance.

I think I'm going to include millis() in the code so I can have an accurate timings of the output vs the set point, export the results in an excel sheet and plot a graph to visually look at the PID process and tune it by hand.

There is a library to do this Arduino Playground - GoBetwino, yes I know I'm lazy