VB.net PID

The forum for discussing all kinds of brewing paraphernalia.
Post Reply
EccentricDyslexic

VB.net PID

Post by EccentricDyslexic » Tue Aug 04, 2015 3:38 pm

Can someone have a nose at my current pid compute sub routine and give me their thoughts?

Sub Compute(ByVal Kp As Double, ByVal Ki As Double, ByVal Kd As Double)

Dim input = RIMS_CurrentTemp
Dim SetPoint = desiredtemp
Dim outMax = 99
Dim outMin = 1

' Compute all the working error variables
Dim err As Double = SetPoint - input
Label6.Text = err
ITerm += (Ki * err)
Label7.Text = ITerm

If ITerm > outMax Then ITerm = outMax
If ITerm < outMin Then ITerm = outMin
Dim dInput As Double = input - lastInput

' Compute PID Output
Output = Kp * err + ITerm - Kd * dInput
If Output > outMax Then Output = outMax
If Output < outMin Then Output = outMin

' Remember some variables for the next time
lastInput = input
percent = Output
Label8.Text = percent
End Sub

The above is based on this - http://brettbeauregard.com/blog/2011/04 ... roduction/

I have a 2kw heater element in a 22mm copper pipe, past which the wort is flowed. The sensor reading the output of the rims is two elbows past the heater element to allow for mixing of heated wort before sensing the temperature.

I'd appreciate it if someone could critique it for me.

Ta peeps

Steve

Fil
Telling imaginary friend stories
Posts: 5229
Joined: Sun Oct 16, 2011 1:49 pm
Location: Cowley, Oxford

Re: VB.net PID

Post by Fil » Tue Aug 04, 2015 9:55 pm

Ha I just dug out the same link..
tbh i read just enough about what a pid was to gladly treat it as a black box when using the arduino pid lib..

Why VB? what are you planning to run it on? and how will you be triggering the heating input? and measuring temps?

Details???? sounds like a corker of a project..


as pseudo code it all seems ok, though im not going to read up on it all right now, i assume you have thrashed out the algorithm in your head, you will have some scope issues if the function lives as is tho, what with output defined within the function and not applied within that scope.
tho simply typing the function and returning output would sort that..

for an alternative platform have you come across the esp8266 mini wifi boards Andy is using in his fridge logging project?

Testing time perhaps, an aquarium heater in a 2liter jug of water makes a good small scale test set up to allow you to quickly run through a test cycle.. stirring and monitoring temp with a 2ndary thermometer, wont take all evening like a full scale test ;)..
ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate :(

EccentricDyslexic

Re: VB.net PID

Post by EccentricDyslexic » Tue Aug 04, 2015 10:10 pm

Hi fil, will do a better reply tomorrow, but I automated my brewery four years back, using vb.net on my office PC in the room next to the brewery room. It uses a PC Control Digibee + to control 16 items. Ie, hlt elements, hlt stirrer, boiler elements, hop hoppers(4), extracter fan, CF cooler water supply, three peristaltic pumps, a rims pump a rims element, a flip shelf for a hop rocket(turns it upside down so there is no wort left in it) etc. I use one wire sensors to read temps of hlt, rims output and boiler. And finally digital inputs to ensure hoppers opened fully.
Why vb? Because it's all I know from my speccy programming days;-)

Back with more in the morning;-)

Steve

Fil
Telling imaginary friend stories
Posts: 5229
Joined: Sun Oct 16, 2011 1:49 pm
Location: Cowley, Oxford

Re: VB.net PID

Post by Fil » Tue Aug 04, 2015 10:47 pm

of course Steve, i remember being hypnotizsed by the hop release mechanism vid Doh!!

just pulled the source for the arduino pid object and it looks familiar straight away ;) especially if you take a gander at the compute method..

remove the arduino wprogram.h include, and add the M$ libs needed if any?
then the arduino function millis() returns a count of milliseconds since boot, and overuns after about 47 days so is a simple Now() timestamp.. change that for a microshaft c++ now(); or whatever call?? and I think that may be all u need do to it to compile it as a Visual studio c++ .net object resource ? tho its been a decade+ since i used microsoft VS in anger..


Code: Select all

/**********************************************************************************************
 * 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)
{
	
    myOutput = Output;
    myInput = Input;
    mySetpoint = Setpoint;
	inAuto = false;
	
	PID::SetOutputLimits(0, 255);				//default output limit corresponds to 
												//the arduino pwm limits

    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;
}


/* 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
 ******************************************************************************/ 
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)
{
   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;
   }
}

/* 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()
{
   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;}


the arduino header file may need a lil more editing, but the compiler errors should indicate whats needed

Code: Select all

#ifndef PID_v1_h
#define PID_v1_h
#define LIBRARY_VERSION	1.1.1

class PID
{


  public:

  //Constants used in some of the functions below
  #define AUTOMATIC	1
  #define MANUAL	0
  #define DIRECT  0
  #define REVERSE  1

  //commonly used functions **************************************************************************
    PID(double*, double*, double*,        // * constructor.  links the PID to the Input, Output, and 
        double, double, double, int);     //   Setpoint.  Initial tuning parameters are also set here
	
    void SetMode(int Mode);               // * sets PID to either Manual (0) or Auto (non-0)

    bool Compute();                       // * performs the PID calculation.  it should be
                                          //   called every time loop() cycles. ON/OFF and
                                          //   calculation frequency can be set using SetMode
                                          //   SetSampleTime respectively

    void SetOutputLimits(double, double); //clamps the output to a specific range. 0-255 by default, but
										  //it's likely the user will want to change this depending on
										  //the application
	


  //available but not commonly used functions ********************************************************
    void SetTunings(double, double,       // * While most users will set the tunings once in the 
                    double);         	  //   constructor, this function gives the user the option
                                          //   of changing tunings during runtime for Adaptive control
	void SetControllerDirection(int);	  // * Sets the Direction, or "Action" of the controller. DIRECT
										  //   means the output will increase when error is positive. REVERSE
										  //   means the opposite.  it's very unlikely that this will be needed
										  //   once it is set in the constructor.
    void SetSampleTime(int);              // * sets the frequency, in Milliseconds, with which 
                                          //   the PID calculation is performed.  default is 100
										  
										  
										  
  //Display functions ****************************************************************
	double GetKp();						  // These functions query the pid for interal values.
	double GetKi();						  //  they were created mainly for the pid front-end,
	double GetKd();						  // where it's important to know what is actually 
	int GetMode();						  //  inside the PID.
	int GetDirection();					  //

  private:
	void Initialize();
	
	double dispKp;				// * we'll hold on to the tuning parameters in user-entered 
	double dispKi;				//   format for display purposes
	double dispKd;				//
    
	double kp;                  // * (P)roportional Tuning Parameter
    double ki;                  // * (I)ntegral Tuning Parameter
    double kd;                  // * (D)erivative Tuning Parameter

	int controllerDirection;

    double *myInput;              // * Pointers to the Input, Output, and Setpoint variables
    double *myOutput;             //   This creates a hard link between the variables and the 
    double *mySetpoint;           //   PID, freeing the user from having to constantly tell us
                                  //   what these values are.  with pointers we'll just know.
			  
	unsigned long lastTime;
	double ITerm, lastInput;

	unsigned long SampleTime;
	double outMin, outMax;
	bool inAuto;
};
#endif

ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate :(

EccentricDyslexic

Re: VB.net PID

Post by EccentricDyslexic » Wed Aug 05, 2015 8:17 am

Thanks fil.

Its good to know that the Arduino pid is based on the same code:-) I wonder if the problems I have been having are due to the mixed use of integer and doubles in my code. Also wonder if the line -
Output = Kp * err + ITerm - Kd * dInput
should be bracketed ie
Output = (Kp * err) + ITerm - (Kd * dInput)

Not far off I don't think, just need to do more experimentation!

Steve

Fil
Telling imaginary friend stories
Posts: 5229
Joined: Sun Oct 16, 2011 1:49 pm
Location: Cowley, Oxford

Re: VB.net PID

Post by Fil » Wed Aug 05, 2015 1:15 pm

fwiw i had a quick go at porting the arduino pid lib over to M$ Vstudio2010 and compile it as a C++ class, didnt go as quite as easily as i suggested ;)

mixing ints and doubles in a calculation shouldnt be a problem as long as the resulting type is the correct sort for the product.. ie dont try to shoehorn a value to 2 decimal places into an int container without an explicit cast..
tho Kp Ki and Kd should be cardinal numbers iirc (dont quote me on that..)

if in doubt parenthesise :)
ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate :(

EccentricDyslexic

Re: VB.net PID

Post by EccentricDyslexic » Thu Aug 06, 2015 7:59 am

Not much in vs goes smoothly lol

Not sure the brackets will make a difference after all, because the multiplication gets done before addition and subtraction it seems, same as c++

Steve

User avatar
themadhippy
Even further under the Table
Posts: 2970
Joined: Tue Dec 08, 2009 12:53 am
Location: playing hooky

Re: VB.net PID

Post by themadhippy » Thu Aug 06, 2015 9:35 am

because the multiplication gets done before addition and subtraction it seems, same as c+
just like proper maths then , bodmas is the order things get done mathematically ,brackets,orders(routes and power),division,multiplication,addition and subtraction
Warning: The Dutch Coffeeshops products may contain drugs. Drinks containing caffeine should be used with care and moderation

Post Reply