Skip to content

Instantly share code, notes, and snippets.

@ivanseidel
Last active March 29, 2024 07:15
Show Gist options
  • Save ivanseidel/b1693a3be7bb38ff3b63 to your computer and use it in GitHub Desktop.
Save ivanseidel/b1693a3be7bb38ff3b63 to your computer and use it in GitHub Desktop.
Simple PID Class for Arduino Projects
// (Really Simple) PID Class by Ivan Seidel
// GitHub.com/ivanseidel
// Use as you want. Leave credits
class PID{
public:
double error;
double sample;
double lastSample;
double kP, kI, kD;
double P, I, D;
double pid;
double setPoint;
long lastProcess;
PID(double _kP, double _kI, double _kD){
kP = _kP;
kI = _kI;
kD = _kD;
P = 0;
I = 0;
D = 0;
lastProcess = -1;
}
void addNewSample(double _sample){
sample = _sample;
}
void setSetPoint(double _setPoint){
setPoint = _setPoint;
}
double process(){
if (lastProcess == -1) {
lastSample = sample;
lastProcess = millis();
}
// Implementação P ID
error = setPoint - sample;
float deltaTime = (millis() - lastProcess) / 1000.0;
lastProcess = millis();
//P
P = error * kP;
//I
I = I + (error * kI) * deltaTime;
//D
D = (lastSample - sample) * kD / deltaTime;
lastSample = sample;
// Soma tudo
pid = P + I + D;
return pid;
}
};
#define pSENSOR A1
#define pCONTROLE 3
PID meuPid(1.0, 0, 0);
void setup() {
Serial.begin(9600);
pinMode(pSENSOR, INPUT);
pinMode(pCONTROLE, OUTPUT);
}
int controlePwm = 50;
void loop() {
// Lê temperatura
double temperature = map(analogRead(pSENSOR), 0, 1023, 0, 100);
// Manda pro objeto PID!
meuPid.addNewSample(temperature);
// Converte para controle
controlePwm = (meuPid.process() + 50);
// Saída do controle
analogWrite(pCONTROLE, controlePwm);
}
@sant250784
Copy link

obrigado cara, vc merece ir pro ceu

@celturbo
Copy link

Ja tentei implementar seu código mas o meu derivativo sempre fica saindo nan ou inf.

verdade, simplesmente não funciona.

@ammjr
Copy link

ammjr commented Aug 9, 2020

Ja tentei implementar seu código mas o meu derivativo sempre fica saindo nan ou inf.

verdade, simplesmente não funciona.

O derivativo é dividido pelo deltaTime. Se está dando inf é porque o deltaTime deve ser aproximadamente zero. Algum número dividido por outro número muito próximo de zero tende ao infinito. Em teoria, forçar o código a atrasar alguns milissegundos resolveria.

@turbinex10
Copy link

Ja tentei implementar seu código mas o meu derivativo sempre fica saindo nan ou inf.

acho que faltou inicializar o setPoint da classe no setup ()

@mkazimoto
Copy link

Encontrei o bug: faltou inicializar a variável da Integral com zero no constructor: I = 0;

I = I + (error * kI) * deltaTime;
I = nan + qualquer coisa = nan

@ivanseidel
Copy link
Author

Fala pessoal! editei o arquivo mas não testei.. deve corrigir o problema de nan. Se alguém puder checar agradeço!

@dpereiraENG
Copy link

Ivan, muito bom o código. Obrigado!

O primeiro ciclo, a variavel D resulta nan, deltaTime esta zerado. Ainda não explrorei para solução, apenas inclui um pequeno delay 100ms e resolveu:

if (lastProcess == -1) {
lastSample = sample;
lastProcess = millis();
delay(100); //delay incluido.... resolveu
}

************** Código ESP32 com MAX6675 ****************
//Daniel P. 16/03/2024 - https://gist.github.com/dpereiraENG
// =========================================================================
// --- Bibliotecas ---
#include <max6675.h> //https://github.com/adafruit/MAX6675-library

// =========================================================================
// --- Definições ---
#define OUT_PWM1_GPIO 12 //resistencia tubular 24V 40W

#define PWM1_Freq 5000
#define PWM1_Ch 0
#define PWM1_Res 12 //4096 (0-4095)

#define MAX6675_CS 23
#define MAX6675_SO 19
#define MAX6675_SCK 5

#define thermoDO 19
#define thermoCS 23
#define thermoCLK 5

//#define PERIODO_MEDICAO_MS 2000
// =========================================================================
// --- Variáveis Globais ---
int PWM1_DutyCycle = 0x00;
int controlePwm = 0x00;

double s1_Setpoint = 40.0;
double s1_Kp = 4.0, s1_Ki = 1.06, s1_Kd = 3.80;

// =========================================================================
// --- Constantes Auxiliares ---

// =========================================================================
// --- Declaração de Classes ---
class PID {
public:

double error;
double sample = 0;
double lastSample = 0;
double kP, kI, kD;
double P, I, D;
double pid = 0;

double setPoint;
long lastProcess;
int outMin = 0;
int outMax = 0;

PID(double _kP, double _kI, double _kD) {
  kP = _kP;
  kI = _kI;
  kD = _kD;
  P = 0;
  I = 0;
  D = 0;
  lastProcess = -1;
}

//void addNewSample(double _sample) {
//  sample = _sample;
//}

void setSetPoint(double _setPoint) {
  setPoint = _setPoint;
}

void OutputLimits(int Min, int Max) {
  outMin = Min;
  outMax = Max;
}

double process(double _sample) {
  sample = _sample;

  if (lastProcess == -1) {
    lastSample = sample;
    lastProcess = millis();
    delay(100);
    //Serial.println("debug * lasprocess <0 *");
  }
  // Implementação P ID
  error = setPoint - sample;
  //Serial.print("debug error: ");
  //Serial.println(error);

  float deltaTime = (millis() - lastProcess) / 1000.0;
  lastProcess = millis();
  //Serial.print("debug deltatime: ");
  //Serial.println(deltaTime);

  //P
  P = error * kP;
  //Serial.print("debug P: ");
  //Serial.println(P);

  //I
  I = I + (error * kI) * deltaTime;
  //Serial.print("debug I: ");
  //Serial.println(I);

  //D
  D = (lastSample - sample) * kD / deltaTime;
  lastSample = sample;
  //Serial.print("debug D: ");
  //Serial.println(D);

  // Soma tudo
  pid = P + I + D;
  //Serial.print("debug PID: ");
  //Serial.println(pid);

  if (pid > outMax) {
    pid = outMax;
  }
  else if (pid < outMin) {
    pid = outMin;
  }

  //Serial.print("debug PID filtro: ");
  //Serial.println(pid);

  return (int)pid;
}

};

// =========================================================================
// --- Declaração de tarefas ---

// =========================================================================
// --- Declaração de Objeto ---
PID meuPid(s1_Kp, s1_Ki, s1_Kd);
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

// =========================================================================
// --- Mapeamento de Hardware ---

// =========================================================================
// --- Protótipo da Função ---

// =========================================================================
// --- Desenvolvimento da Função ---

// =========================================================================
// --- Interfaces ---

// =========================================================================
// --- Configurações Iniciais ---
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);

ledcSetup(PWM1_Ch, PWM1_Freq, PWM1_Res);
ledcAttachPin(OUT_PWM1_GPIO, PWM1_Ch);
ledcWrite(PWM1_Ch, LOW);

meuPid.setSetPoint(s1_Setpoint);
meuPid.OutputLimits(0, 4095);

Serial.begin(115200);
delay(1500);

digitalWrite(LED_BUILTIN, LOW);
}

// **************************************************************************
// --- Loop Infinito ---
void loop()
{
float temperature_read = thermocouple.readCelsius();

controlePwm = meuPid.process((double)temperature_read);

ledcWrite(PWM1_Ch, controlePwm);

Serial.print(s1_Setpoint);
Serial.print(" ");
Serial.print(temperature_read);
Serial.print(" ");
Serial.print(controlePwm / 100); //divisor por 100 para o plotter serial
Serial.println(" ");
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(2000);
}

@PROJETO158
Copy link

Fala professor, estou reprogramando uma estação de solda que o stm veio a queimar, esse codigo, modificando com os botoes, e display 7 segmentos, seria possivel utilizar ?? claro fazendo os ajustes nos valores do PID

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment