Pid regulator in Arduino

I need to develop PID in Arduino.

Can anyone suggest me how implement it? It should be quick (the signal changes less 0.001 sec.) and accurate.

Thank's a lot!

There is an Arduino PID library (just Google Arduino PID), but no PID running on an Arduino is going to respond in 1 mSec. The very fastest you can run the PID on a 16MHz AVR-based Arduino is about every 5 mSec. If you use an ARM_based Due, you can get down to a 1 mSec update rate, but that’s still not going to give you close regulation with a fast-changing load.

Regards,
Ray L.

I wrote a small PID lib in C for my projects. It is based on Simulink’s (MATLAB) discrete PID and have the following features: P, I and D gains, low-pass filter on derivative action (coefficient : N), upper and lower saturations, sampling time setting and resets for both the integrator and the derivative filter.

Comments are in French, if you have troubles understanding the code let me know, I will translate them or explain further. You can find the transfer function of the regulator in the header file. It uses Backward Euler as an integrating method.

If you want to recode it as an exercise, start from that TF :wink:

PID.h

#ifndef PID_
#define PID_

// Défini un régulateur PID
typedef struct PID
{
  float Ts; // Temps d'échantillonnage du régulateur
  float Kp, Ki, Kd, N; // Gains proportionnel, intégral, dérivé et coefficient de filtrage
  float sat_down, sat_up; // Saturations de la sortie
  float integrale_precedente, erreur_precedente, derivee_precedente; // Valeurs précédentes de l'action intégrale, de l'entrée et de l'action dérivée
} PID;


// Calcule la sortie du PID envoyé en paramètre avec comme entrée "erreur"
// PID implémentation Simulink : Y/U = P + I.Ts.z/(z-1) + DN/(1+N.Ts.z/(z-1)) (Backward Euler)
float calcul_PID (PID* regulateur, float erreur);
float calcul_PID (PID*regulateur , float erreur, float derivee);
// Remet à 0 les données de mémoire du PID
void reset_PID (PID* regulateur);
void reset_integrale_PID (PID* regulateur);
void reset_filtre_PID (PID* regulateur);


 #endif // PID_

PID.cpp

#include "pid.h"


float calcul_PID (PID* regulateur, float erreur)
// Y/U = P + I.Ts.z/(z-1) + DN/(1+N.Ts.z/(z-1))
// L'intégrateur et le filtre sont de type Backward Euler
{

  // Calcul des actions intégrale et dérivée selon la fonction de transfert du PID
  float commande_derivee = (regulateur->Kd*regulateur->N*(erreur-regulateur->erreur_precedente)+regulateur->derivee_precedente)/(1+regulateur->N*regulateur->Ts);
  float commande_integrale = regulateur->integrale_precedente + erreur*regulateur->Ki*regulateur->Ts;

  // Calcul de la commande
  float commande = regulateur->Kp * erreur + commande_integrale + commande_derivee;

  // Mémorisation des valeurs
  regulateur->integrale_precedente = commande_integrale;
  regulateur->derivee_precedente = commande_derivee;
  regulateur->erreur_precedente = erreur;


  // Saturations
  if (commande > regulateur->sat_up)
    commande = regulateur->sat_up;
  else if (commande < regulateur->sat_down)
    commande = regulateur->sat_down;

  return commande;
}


float calcul_PID (PID*regulateur , float erreur, float derivee)
{
    // Calcul des actions intégrale et dérivée selon la fonction de transfert du PID
  float commande_derivee = regulateur->Ts/(1+regulateur->N*regulateur->Ts) * (regulateur->Kd*regulateur->N*derivee + regulateur->derivee_precedente/regulateur->Ts);
  float commande_integrale = regulateur->integrale_precedente + erreur*regulateur->Ki*regulateur->Ts;

  // Calcul de la commande
  float commande = regulateur->Kp * erreur + commande_integrale + commande_derivee;

  // Mémorisation des valeurs
  regulateur->integrale_precedente = commande_integrale;
  regulateur->derivee_precedente = commande_derivee;
  regulateur->erreur_precedente = erreur;


  // Saturations
  if (commande > regulateur->sat_up)
    commande = regulateur->sat_up;
  else if (commande < regulateur->sat_down)
    commande = regulateur->sat_down;

  return commande;
}


void reset_PID (PID* regulateur)
{
    regulateur->integrale_precedente = 0;
    regulateur->derivee_precedente = 0;
    regulateur->erreur_precedente = 0;
}

void reset_integrale_PID (PID* regulateur)
{
    regulateur->integrale_precedente = 0;
}

void reset_filtre_PID (PID* regulateur)
{
    regulateur->derivee_precedente = 0;
    regulateur->erreur_precedente = 0;
}

Maybe it can be optimized to run faster. Why do you need to run the loop so fast?

For the ultimate in speed you may have to consider using fixed-point or integer arithmetic
rather than floats in your PID implementation - this brings up issues of quantization,
the possible range of values and often involves doing some extra scaling of values.

Indeed.
Another great time optimization is to use multiplication by inverse number instead of divisions (i.e. do *0.1 instead of /10.), since compilers might, by default, not optimize divisions by constants.

I replay to RayLivingston.

Why can Arduino not elaborate down 0.001s? The uController has an clock speed about 16MHz. What is the real elaboration speed in Arduino?

If you don't need to do anything else and if you can communicate fast enough with your sensor, that might be possible... Just try to write your loop and measure the execution time. If it's too high, try code optimization.

What is the process you're controlling? Does it need such a small time step or is it possible to increase it?

Mebe it can be decreased, but in this moment I suppose the worst case. The regulation process is electrical generator moved by oil pump. I must control the frequency produced by the generator, it must be stable to 60Hz. I think the signal can vary within the period.

1000 Hz is REALLY fast, and your process seems to have, let's say, a moderate inertia. I'm sure you can reduce it.

Take a look at this: http://arduino-info.wikispaces.com/Arduino-PWM-Frequency
Timer 0 can be used to output at 61 Hz. If that's not precise enough, you'll have to play with the timer registers in CTC mode, see your board's MCU's datasheet for precise information.

So what is it you're actually controlling? Oil Pressure? And how? PWM output to a DC motor? An air-controled pressure regulator? What is your feedback? Simple once/rev frequency measurement? Quadrature encoder? A/D readings? From what you describe, it's not clear you NEED a particularly fast PID update rate.

Regards,
Ray L.

My feedback is zero-crossing voltage produced by generator.

gio5:
My feedback is zero-crossing voltage produced by generator.

OK, so, at the most, you need to update your control parameter 120 times per second since that will be your maximum feedback rate. Anything beyond that will be just wasting time. With any kind of smoothing at all, it’ll be even slower than that. You don’t need a fast update rate at all.

Regards,
Ray L.

Thank you very much to everybody.

But what is the systematic method to expect the elaboration time in Arduino?

Can I ask you something for autotunig algorithm in PID parameter?