2 setpoints met 1 PID

algemene C code
Berichten: 11
Geregistreerd: 30 Jul 2015, 16:33

2 setpoints met 1 PID

Berichtdoor Sanderoni » 13 Dec 2015, 12:59

Beste leden,

Ik probeer een Batterijlader te bouwen met een PID regeling.

Het (voorlopige) ontwerp is als volgt: Als er voldoende daglicht is, en mijn zonnepanelen leveren energie wordt er een signaal aangeboden aan de Arduino van de batterijlader dat de batterij mag worden opgeladen.

Nu wil ik dat via een PID regeling de batterij eerst via een stroomregeling wordt opgeladen (0,1x de capaciteit) en zodra de klemspanning dan de 14,1 Volt bereikt heeft, er wordt overgeschakeld op een spanningsregeling. Deze moet dan de klemspanning op 13,6 volt houden.

Beide hebben dezelfde uitsturing op uitgang 10.

De PID regeling voor de stroomregeling werkt naar tevredenheid, maar het omschakelen naar de spanningsregeling is een probleem. Weet iemand of het mogelijk is om voor 1 PID regeling 2 setpoints te maken? Of hoe je de ene PID uitzet en eventueel een 2e gebruikt maar wel op dezelfde uitgang?

Met vriendelijke groet,

Sander

Onderstaand de tot nu toe gebruikte code:

Code: Alles selecteren
#include <PID_v1.h>

int spanning = A0;
int Ibatt = A1;
int laderAan = 0;
int Float = 0;
float error = 0;
float FloatSetP = 13.6;
float Ep = 0;
float duty = 0;
float Ubatt = 0;
float stroom = 0;
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT);

void setup() {

Setpoint = 512;  // Bij een R stroom van 1 ohm = 0,7 Ampere laadstroom
Setpoint2 =
 
}

void loop() {
 
laderAan = digitalRead(2);
Ubatt = analogRead(spanning)*0.00488*3;
Input = analogRead(Ibatt);

  if (laderAan == 1 && Ubatt < 14.1 && Float == 0)
  {
   myPID.SetMode(AUTOMATIC); //turn the PID on
   Input = analogRead(Ibatt);
   myPID.Compute();
   analogWrite(10,Output);
   
  }

  else if (Ubatt > 14.1 || Float == 1 && laderAan == 1)
  {
   myPID.SetMode(AUTOMATIC);
   Input = analogRead(Ubatt);
   myPID.Compute();
   analogWrite(10,Output);
   

}


Advertisement

Berichten: 4067
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: 2 setpoints met 1 PID

Berichtdoor shooter » 13 Dec 2015, 22:48

denkfoutje:
je stroomlader werkt dus prima
dan zet je een flag aan ofwel snelladen is true
als de spanning 14.1 volt heeft bereikt zet je de flag domweg uit en dan gaat de spanningsregeling aan, jij kijkt elke keer naar die 14.1 volt en dat gaat niet goed natuurlijk.
dus knopje duwen is starten en dus snelladen aan.
de rest is goed, behalve het setpoint moet je naar setpoint2 zetten.
dus iets als mPID.setpoint=setpoint2
paul deelen
shooter@home.nl

Berichten: 11
Geregistreerd: 30 Jul 2015, 16:33

Re: 2 setpoints met 1 PID

Berichtdoor Sanderoni » 15 Dec 2015, 22:35

@ shooter: bedankt voor je reactie, ik heb het nu als volgt opgelost: De Float variabele heb ik als een soort "flag" gebruikt. Als de lader "aan" is en de spanning bereikt de 14,1 Volt wordt deze "1". En de PID regelaar gaat naar setpoint2. Als nu de lader weer uitgeschakeld wordt, wordt de Float ook weer "0".

Hoop dat dit klopt qua programmeren.

cpp code
#include <PID_v1.h>

int spanning = A0;
int Ibatt = A1;
int laderAan = 0;
int Float = 0;
float Ubatt = 0;
float stroom = 0;
double Setpoint1 = 512; // // Bij een R stroom van 1 ohm = 0,7 Ampere laadstroom
double Setpoint2 = 700; // voorbeeld
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT);

void setup() { }

void loop() {

laderAan = digitalRead(2);
Ubatt = analogRead(spanning)*0.00488*3;
Input = analogRead(Ibatt);

if (laderAan == 1 && Ubatt > 14.1)
{
Float = 1;
}
else if (laderAan == 0)
{
Float = 0;
}

if (laderAan == 1 && Ubatt < 14.1 && Float == 0)
{
myPID.SetMode(AUTOMATIC); //turn the PID on
Input = analogRead(Ibatt);
myPID.Compute();
analogWrite(10,Output);
Setpoint = Setpoint1;
}

else if (Float == 1 && laderAan == 1)
{
myPID.SetMode(AUTOMATIC);
Input = analogRead(Ubatt);
myPID.Compute();
analogWrite(10,Output);
Setpoint = Setpoint2;
}


}

Gebruikers-avatar
Berichten: 5043
Geregistreerd: 13 Mei 2013, 20:57
Woonplaats: Heemskerk

Re: 2 setpoints met 1 PID

Berichtdoor nicoverduin » 16 Dec 2015, 08:33

Eerste reactie:
a) zou ik de IO nog ff definieren (pinMode) in de setup()
b) analogWrite werkt met een integer waarde tussen de 0 en 255. Geen floating points
c) als je met floating points werk, zou ik ervoor zorgen dat alle getallen float zijn. ints en floats door elkaar kunnen nog wel eens voor verassingen zorgen.
d) Bij de Arduino (Uno en Mega) is nauwkeurigheid van double gelijk aan float itt 32bit processoren
e) ter voorkoming van verkeerde omzetting naar floats, alle getallen afsluiten met '.0' . dus bijvoorbeeld geen 3 maar 3.0
Docent HBO Technische Informatica, Embedded ontwikkelaar & elektronicus
http://www.verelec.nl

Berichten: 4067
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: 2 setpoints met 1 PID

Berichtdoor shooter » 16 Dec 2015, 10:33

cpp code
#include <PID_v1.h>

int pinspanning = A0;
int pinstroom = A1;
int laderAan = 0;
int Float = 0;
float spanning = 0;
float stroom = 0;
float maxspanning =14.1
double Setpoint1 = 512; // // Bij een R stroom van 1 ohm = 0,7 Ampere laadstroom
double Setpoint2 = 700; // voorbeeld
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT); // dit is voor de stroomregeling

void setup() { }

void loop() {

laderAan = digitalRead(2);
Ubatt = analogRead(pinspanning)*0.00488*3;
Ibatt = analogRead(pinstroom); // omrekening?
// ja zo was de bedoeling, om het netjes te houden even kijken wat high,true,1 etc betekent.
if (laderAan == HIGH && Ubatt > 14.1)
{
Float = true;
}
else if (laderAan == LOW)
{
Float = false;
}

if (laderAan == 1 && Ubatt < maxspanning && Float == 0)
{
Setpoint = Setpoint1;
myPID.SetMode(AUTOMATIC); //turn the PID on
// je zult hier dus de settunings moeten gebruiken.
Input = Ibatt
myPID.Compute();

}

else if (Float == 1 && laderAan == 1)
{
// ook hier zul je de tunings moeten zetten, en waarschijnlijk op andere getallen.
myPID.SetMode(AUTOMATIC);
Setpoint = Setpoint2;
Input = Ubatt
myPID.Compute();

}

analogWrite(10,byte(Output);
}

ik heb het niet getest, maar je snapt het wel.
en denk aan wat nico zei.
paul deelen
shooter@home.nl

Berichten: 11
Geregistreerd: 30 Jul 2015, 16:33

Re: 2 setpoints met 1 PID

Berichtdoor Sanderoni » 16 Dec 2015, 12:25

Heren,

Hartstikke bedankt voor jullie input! :-)

Ik ga ermee aan de gang en aan het testen.

Nog 1 klein vraagje, als ik het goed begrijp kan ik het zo gaan doen:
Om 2 verschillende settunings te maken, kan ik het dan zo maken (voorbeeld):

PID myPID1(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT); // dit is voor de stroomregeling
PID myPID2(&Input, &Output, &Setpoint,0.07,0.5,0.010, DIRECT); // dit is voor de spanningsregeling

En dan in de loop de PID "opvragen" met:

myPID1.SetMode(AUTOMATIC); // Hier worden dan de settunings van PID1 gebruikt.

Berichten: 4067
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: 2 setpoints met 1 PID

Berichtdoor shooter » 16 Dec 2015, 21:11

ja dat kan ook, maar dan zal er iets jumpen omdat de reglaar dan ineens omschakelt naar een totaal andere regelaar.
nou is dat hier niet zo spannend want na het laden duurt het lang voordat de druppelstart
paul deelen
shooter@home.nl

Berichten: 11
Geregistreerd: 30 Jul 2015, 16:33

Re: 2 setpoints met 1 PID

Berichtdoor Sanderoni » 19 Dec 2015, 14:32

Ik heb er het volgende van gemaakt:

Bij het testen krijg ik echter bij de waarde "Output" hoogstens 0.26. Een rare waarde volgens mij, want de PID regelaar moet toch waardes van 0 tot 255 uitsturen? (Zoals Nico zei een integer waarde,) Het lijkt nu inderdaad of hij floating points uitstuurt, maar niet eens genoeg om de regeling te starten.
Maar stuurt de PID niet uit zichzelf een integer waarde naar de Output? Of staat er iets verkeerd?

Code: Alles selecteren
#include <PID_v1.h>

int spanning = A0;
int stroom = A1;
int laderAan = 0;
int Float = 0;
float Ubatt = 0;
float Ibatt = 0;
float maxspanning = 14.1;
double Setpoint1 = 0.7; // 0,7 Ampere laadstroom
double Setpoint2 = 13.6; // 13,6 Volt laadspanning
double Setpoint, Input, Output;
PID myPID1(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT); // PID stroomregeling
PID myPID2(&Input, &Output, &Setpoint,0.05,0.4,0.005, DIRECT); // PID spanningsregeling

void setup()

{
  Serial.begin(9600);
  pinMode(2, INPUT);  // ingang start signaal
 
}

void loop() {
 
laderAan = digitalRead(2);
Ubatt = analogRead(spanning)*0.00488*4.7; // spanningsberekening
Ibatt = analogRead(stroom)*0.00488;       // stroomberekening

Serial.print("Ubatt = ");
Serial.println(Ubatt);
Serial.print("Spanning = ");
Serial.println(spanning);
Serial.print("Ibatt = ");
Serial.println(Ibatt);
Serial.print("Float = ");
Serial.println(Float);
Serial.print("Output = ");
Serial.println(Output);
Serial.print("laderAan = ");
Serial.println(laderAan);

  if (laderAan == HIGH && Ubatt > 14.1)
  {
    Float = true;
  }
  else if (laderAan == LOW)
  {
    Float = false;
  }
 
  if (laderAan == HIGH && Ubatt < maxspanning && Float == 0)
  {
   Setpoint = Setpoint1;
   myPID1.SetMode(AUTOMATIC);
   Input = Ibatt;
   myPID1.Compute();
   }
 
  else if (Float == 1 && laderAan == HIGH)
  {
   Setpoint = Setpoint2;
   myPID2.SetMode(AUTOMATIC);
   Input = Ubatt;
   myPID2.Compute();
   }

    analogWrite(10, Output);

  }

 
 

 
 

 
 


 
 



Berichten: 4067
Geregistreerd: 16 Okt 2013, 14:31
Woonplaats: s hertogenbosch

Re: 2 setpoints met 1 PID

Berichtdoor shooter » 19 Dec 2015, 18:31

[code2=]/**********************************************************************************************
* Arduino PID Library - Version 1.1.1
* by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
*
* This Library is licensed under a GPLv3 License
**********************************************************************************************/

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include <PID_v1.h>

/*Constructor (...)*********************************************************
* The parameters specified here are those for for which we can't set up
* reliable defaults, so we need to have the user set them.
***************************************************************************/
PID::PID(double* Input, double* Output, double* Setpoint,
double Kp, double Ki, double Kd, int ControllerDirection)
// input is double
// output is also double
// eigenlijk is alles dubbel behalve de richting

{

myOutput = Output; // waar is de declaratie van output?
myInput = Input;
mySetpoint = Setpoint;
inAuto = false;

PID::SetOutputLimits(0, 255); //default output limit corresponds to
// standaard limits zijn 0 en 255 (double) the arduino pwm limits
// maar dan wel uitgang van double naar byte maken.

SampleTime = 100; //default Controller Sample Time is 0.1 seconds

PID::SetControllerDirection(ControllerDirection);
PID::SetTunings(Kp, Ki, Kd);

lastTime = millis()-SampleTime;
}


/* Compute() **********************************************************************
* This, as they say, is where the magic happens. this function should be called
* every time "void loop()" executes. the function will decide for itself whether a new
* pid Output needs to be computed. returns true when the output is computed,
* false when nothing has been done.
**********************************************************************************/
bool PID::Compute()
{
if(!inAuto) return false;
unsigned long now = millis();
unsigned long timeChange = (now - lastTime);
if(timeChange>=SampleTime)
{
/*Compute all the working error variables*/
double input = *myInput;
double error = *mySetpoint - input;
ITerm+= (ki * error);
if(ITerm > outMax) ITerm= outMax;
else if(ITerm < outMin) ITerm= outMin;
double dInput = (input - lastInput);

/*Compute PID Output*/
double output = kp * error + ITerm- kd * dInput;

if(output > outMax) output = outMax;
else if(output < outMin) output = outMin;
*myOutput = output;

/*Remember some variables for next time*/
lastInput = input;
lastTime = now;
return true;
}
else return false; // dit is resultaat van compute die in de loop moet staan. de output moet je apart opvragen

}


/* SetTunings(...)*************************************************************
* This function allows the controller's dynamic performance to be adjusted.
* it's called automatically from the constructor, but tunings can also
* be adjusted on the fly during normal operation
******************************************************************************/
// veranderen van de Kp etc. als controller werkt.


void PID::SetTunings(double Kp, double Ki, double Kd)
{
if (Kp<0 || Ki<0 || Kd<0) return;

dispKp = Kp; dispKi = Ki; dispKd = Kd;

double SampleTimeInSec = ((double)SampleTime)/1000;
kp = Kp;
ki = Ki * SampleTimeInSec;
kd = Kd / SampleTimeInSec;

if(controllerDirection ==REVERSE)
{
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
}

/* SetSampleTime(...) *********************************************************
* sets the period, in Milliseconds, at which the calculation is performed
******************************************************************************/
void PID::SetSampleTime(int NewSampleTime)
{
if (NewSampleTime > 0)
{
double ratio = (double)NewSampleTime
/ (double)SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = (unsigned long)NewSampleTime;
}
}

/* SetOutputLimits(...)****************************************************
* This function will be used far more often than SetInputLimits. while
* the input to the controller will generally be in the 0-1023 range (which is
* the default already,) the output will be a little different. maybe they'll
* be doing a time window and will need 0-8000 or something. or maybe they'll
* want to clamp it from 0-125. who knows. at any rate, that can all be done
* here.
**************************************************************************/
void PID::SetOutputLimits(double Min, double Max)
/ hier staan de outputlimits in double dus ook hier van byte naar double maken.
{
if(Min >= Max) return;
outMin = Min;
outMax = Max;

if(inAuto)
{
if(*myOutput > outMax) *myOutput = outMax;
else if(*myOutput < outMin) *myOutput = outMin;

if(ITerm > outMax) ITerm= outMax;
else if(ITerm < outMin) ITerm= outMin;
// dit heeft ook invloed op de I factor
}
}

/* SetMode(...)****************************************************************
* Allows the controller Mode to be set to manual (0) or Automatic (non-zero)
* when the transition from manual to auto occurs, the controller is
* automatically initialized
******************************************************************************/
void PID::SetMode(int Mode)
{
bool newAuto = (Mode == AUTOMATIC);
if(newAuto == !inAuto)
{ /*we just went from manual to auto*/
PID::Initialize();
}
inAuto = newAuto;
}

/* Initialize()****************************************************************
* does all the things that need to happen to ensure a bumpless transfer
* from manual to automatic mode.
******************************************************************************/
void PID::Initialize()
// dit zie ik ook nergens.
{
ITerm = *myOutput;
lastInput = *myInput;
if(ITerm > outMax) ITerm = outMax;
else if(ITerm < outMin) ITerm = outMin;
}

/* SetControllerDirection(...)*************************************************
* The PID will either be connected to a DIRECT acting process (+Output leads
* to +Input) or a REVERSE acting process(+Output leads to -Input.) we need to
* know which one, because otherwise we may increase the output when we should
* be decreasing. This is called from the constructor.
******************************************************************************/
void PID::SetControllerDirection(int Direction)
{
if(inAuto && Direction !=controllerDirection)
{
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
controllerDirection = Direction;
}

/* Status Funcions*************************************************************
* Just because you set the Kp=-1 doesn't mean it actually happened. these
* functions query the internal state of the PID. they're here for display
* purposes. this are the functions the PID Front-end uses for example
******************************************************************************/
double PID::GetKp(){ return dispKp; }
double PID::GetKi(){ return dispKi;}
double PID::GetKd(){ return dispKd;}
int PID::GetMode(){ return inAuto ? AUTOMATIC : MANUAL;}
int PID::GetDirection(){ return controllerDirection;}

[/code2]

ofwel er zijn nog een paar regels nodig in je programma.
zoals de Kp etc die moeten ook in double staan, ik zet nooit getallen in het programma maar altijd bovenaan.
maak van input en output aub aparte variabelen, want je gebruikt ze dubbel.
de Kp kun je wijzigen met settunings en niet met setautomatic, die doet alleen maar de I 0 maken en de regelaar inschakelen.
paul deelen
shooter@home.nl

Terug naar C code

Wie is er online?

Gebruikers in dit forum: Geen geregistreerde gebruikers en 8 gasten