-
-
Save ivanseidel/b1693a3be7bb38ff3b63 to your computer and use it in GitHub Desktop.
// (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); | |
} |
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 ;)'
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
Obrigado Ivan! Parabéns pelo trabalho.
Ja tentei implementar seu código mas o meu derivativo sempre fica saindo nan ou inf.
obrigado cara, vc merece ir pro ceu
Ja tentei implementar seu código mas o meu derivativo sempre fica saindo nan ou inf.
verdade, simplesmente não funciona.
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.
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 ()
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
Fala pessoal! editei o arquivo mas não testei.. deve corrigir o problema de nan
. Se alguém puder checar agradeço!
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);
}
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
Great job Ivan!
Obrigado por compartlhar!
Um abraço e sucesso!