-
-
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); | |
} |
Thank you Ivan...
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!
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
Great job Ivan!
Obrigado por compartlhar!
Um abraço e sucesso!
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
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