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);
}
@gelimang
Copy link

gelimang commented Jul 7, 2015

Thank you ivan for the code.
I'll use your PID code as reference for my final project about Controlling DC Motor Models in MATLAB from Arduino

@EduardoLezano
Copy link

Thank you Ivan...

@fringebishopeuler
Copy link

Nice and simple approach :)
However, I have a question:

  • The 1st time meuPID.process() executes the variable lastProcess is uninitialized. So what would be the result of line 35?
  • Is it not best to initialize it directly by default or even better (I guess) at the time of construction with the currentTime ( millis() )? This way the initial error would not be that big.

Nice work. Kudos!

@ozzyaraujo
Copy link

Ivan boa noite. sou meio leigo em programação. onde eu insiro o valor de Kp,Ki e Kd? e o que seria esse " PID meuPid(1.0, 0, 0)"?

obrigado

@Celimar
Copy link

Celimar commented May 11, 2016

Great job Ivan!

Obrigado por compartlhar!
Um abraço e sucesso!

@YhanChristian
Copy link

Obrigado por compartilhar, vi seu vídeo e agora consegui entender a função do PID.
Só tinha ouvido falar e to estudando isso para aplicar na faculdade, valeu abraços ;)'

@rhonei
Copy link

rhonei commented Nov 6, 2016

Olá Ivan, estou a implementar um controlador PID em um conversor cc-cc, e gostaria de saber se posso utilizar este código como base para o projeto, quais as limitações dele e quais melhorias podem ser feitas para um melhor desempenho do controlador

@leorventura
Copy link

Obrigado Ivan! Parabéns pelo trabalho.

@JulioNegreiro
Copy link

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

@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