autotune help

78 views
Skip to first unread message

Emre özger

unread,
Jul 14, 2023, 3:27:04 PM7/14/23
to DIY PID Control
#include <Arduino.h>
#include <avr/io.h>
#include <math.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>

uint8_t seg[10] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };
uint8_t birler, onlar, yuzler, binler, digit;
float currentTemp;
uint16_t temp;

byte ATuneModeRemember = 2;
float input, output, setpoint;
float kp = 2, ki = 0.5, kd = 2;

float kpmodel = 1.5, taup = 100, theta[50];
float outputStart = 5;
float aTuneStep = 50, aTuneNoise = 1, aTuneStartValue = 100;
unsigned int aTuneLookBack = 20;

boolean tuning = false;
uint32_t modelTime;

uint16_t windowSize = 300;
uint32_t windowStartTime, nextSwitchTime;
bool relayStatus;

boolean useSimulation = false;

PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
PID_ATune aTune(&input, &output);

void adc_init() {
  ADCSRA = 0;
  ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADIE);
  sei();
}

uint16_t analogNoiseReducedRead(byte pinNumber) {
  uint16_t reading = 0;
  ADCSRA |= (1 << ADEN) | (1 << ADIF);
  ADMUX = (0x0F & pinNumber) | (1 << REFS0);
  _delay_us(32);
  set_sleep_mode(SLEEP_MODE_ADC);
  for (uint8_t i = 0; i < 32; i++) {
    sleep_mode();
    while (ADCSRA & (1 << ADSC))
      ;
    reading += ADC;
  }
  ADCSRA &= ~(1 << ADEN);
  return (reading >> 5);
}

float getTemp() {
  float sicaklik;
  sicaklik = analogNoiseReducedRead(4);
  sicaklik = log(((10240000 / sicaklik) - 10000));
  sicaklik = 1 / (0.001129148f + (0.000234125f + (0.0000000876741f * sicaklik * sicaklik)) * sicaklik);
  sicaklik = (sicaklik - 273.15f);
  return (sicaklik);
}

EMPTY_INTERRUPT(ADC_vect);

void send_data(uint8_t data_out) {
  for (uint8_t i = 0; i < 8; i++) {
    if (data_out & 0x80) {
      PORTB |= (1 << 2);
    } else {
      PORTB &= ~(1 << 2);
    }
    PORTB |= (1 << 1);
    PORTB &= ~(1 << 1);
    data_out <<= 1;
  }
  PORTB |= (1 << 0);
  PORTB &= ~(1 << 0);
}

void mcu_init() {
  DDRB |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 5);
  DDRD |= (1 << 2);
  DDRC |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
  DDRC &= ~(1 << 4);
}

void changeAutoTune() {
  if (!tuning) {
    //Set the output to the desired starting frequency.
    output = aTuneStartValue;
    aTune.SetNoiseBand(aTuneNoise);
    aTune.SetOutputStep(aTuneStep);
    aTune.SetLookbackSec((int)aTuneLookBack);
    AutoTuneHelper(true);
    tuning = true;
  } else {  //cancel autotune
    aTune.Cancel();
    tuning = false;
    AutoTuneHelper(false);
  }
}

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

void DoModel() {
  //cycle the dead time
  for (byte i = 0; i < 49; i++) {
    theta[i] = theta[i + 1];
  }
  //compute the input
  input = (kpmodel / taup) * (theta[0] - outputStart) + input * (1 - 1 / taup) + ((float)random(-10, 10)) / 100;
}

void setup() {  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  mcu_init();
  adc_init();
  _delay_ms(3000);

  if (useSimulation) {
    for (byte i = 0; i < 50; i++) {
      theta[i] = outputStart;
    }
    modelTime = 0;
  }

  setpoint = 37.7f;
  myPID.SetOutputLimits(0, windowSize);
  myPID.SetMode(AUTOMATIC);

  if (tuning) {
    tuning = false;
    changeAutoTune();
    tuning = true;
  }
  windowStartTime = millis();
}

void loop() {  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  uint32_t now = millis();

  currentTemp = getTemp();

  if (!useSimulation) {  //pull the input in from the real world
    input = currentTemp;
  }


  if (tuning) {
    byte val = (aTune.Runtime());
    if (val != 0) {
      tuning = false;
    }

    if (!tuning) {  //we're done, set the tuning parameters
      kp = aTune.GetKp();
      ki = aTune.GetKi();
      kd = aTune.GetKd();
      myPID.SetTunings(kp, ki, kd);
      AutoTuneHelper(false);
    }
  }

  else myPID.Compute();

  if (useSimulation) {
    theta[30] = output;
    if (now >= modelTime) {
      modelTime += 100;
      DoModel();
    }
  }

  uint32_t msNow = millis();
  if (msNow - windowStartTime >= windowSize) {
    windowStartTime = msNow;
  }
  if (!relayStatus && output > (msNow - windowStartTime)) {
    if (msNow > nextSwitchTime) {
      nextSwitchTime = msNow + 1;
      relayStatus = true;
      PORTD |= (1 << 2);
    }
  } else if (relayStatus && output < (msNow - windowStartTime)) {
    if (msNow > nextSwitchTime) {
      nextSwitchTime = msNow + 1;
      relayStatus = false;
      PORTD &= ~(1 << 2);
    }
  }

  temp = currentTemp * 100;
  digit++;
  digit &= 0x03;
  birler = (temp % 10);
  onlar = (temp / 10) % 10;
  yuzler = (temp / 100) % 10;
  binler = (temp / 1000) % 10;
  switch (digit) {
    case 0: send_data(seg[binler]); break;
    case 1:
      if (digit == 1) send_data(seg[yuzler] & 0x7F);
      else send_data(seg[yuzler]);
      break;
    case 2: send_data(seg[onlar]); break;
    case 3: send_data(seg[birler]); break;
  }
  PORTC = (1 << digit);
  return;
}
Reply all
Reply to author
Forward
0 new messages