From 224de0dc61f7fbc4b5279ed8dc7c85862bee433c Mon Sep 17 00:00:00 2001 From: Michael Wetter Date: Thu, 26 May 2022 12:47:30 -0700 Subject: [PATCH] Add PID controller with gains as input (#3036) * Implemented PID block with gains as input. For #2993 * Removed duplicate name label * Rearranged connections Co-authored-by: Huang --- .../Controls/OBC/CDL/Continuous/Divide.mo | 4 - Buildings/Controls/OBC/CDL/Continuous/PID.mo | 64 +- .../OBC/CDL/Continuous/PIDWithReset.mo | 81 +-- .../OBC/Utilities/PIDWithInputGains.mo | 558 ++++++++++++++++++ .../Utilities/Validation/PIDWithInputGains.mo | 156 +++++ .../OBC/Utilities/Validation/package.order | 1 + .../Controls/OBC/Utilities/package.order | 1 + ...Utilities_Validation_PIDWithInputGains.txt | 17 + .../Validation/PIDWithInputGains.mos | 6 + ...Utilities.Validation.PIDWithInputGains.mos | 11 + Buildings/package.mo | 11 + 11 files changed, 839 insertions(+), 71 deletions(-) create mode 100644 Buildings/Controls/OBC/Utilities/PIDWithInputGains.mo create mode 100644 Buildings/Controls/OBC/Utilities/Validation/PIDWithInputGains.mo create mode 100644 Buildings/Resources/ReferenceResults/Dymola/Buildings_Controls_OBC_Utilities_Validation_PIDWithInputGains.txt create mode 100644 Buildings/Resources/Scripts/Dymola/Controls/OBC/Utilities/Validation/PIDWithInputGains.mos create mode 100644 Buildings/Resources/Scripts/OpenModelica/compareVars/Buildings.Controls.OBC.Utilities.Validation.PIDWithInputGains.mos diff --git a/Buildings/Controls/OBC/CDL/Continuous/Divide.mo b/Buildings/Controls/OBC/CDL/Continuous/Divide.mo index 2cb7a2f5578..1165ee65155 100644 --- a/Buildings/Controls/OBC/CDL/Continuous/Divide.mo +++ b/Buildings/Controls/OBC/CDL/Continuous/Divide.mo @@ -76,10 +76,6 @@ Modelica Standard Library. Ellipse( fillPattern=FillPattern.Solid, extent={{-5,-30},{5,-20}}), - Text( - textColor={0,0,255}, - extent={{-150,110},{150,150}}, - textString="%name"), Line( points={{-100,60},{-66,60},{-40,30}}, color={0,0,127}), diff --git a/Buildings/Controls/OBC/CDL/Continuous/PID.mo b/Buildings/Controls/OBC/CDL/Continuous/PID.mo index 41bd1e82446..e647c34f761 100644 --- a/Buildings/Controls/OBC/CDL/Continuous/PID.mo +++ b/Buildings/Controls/OBC/CDL/Continuous/PID.mo @@ -54,7 +54,7 @@ block PID "Connector of actuator output signal" annotation (Placement(transformation(extent={{220,-20},{260,20}}),iconTransformation(extent={{100,-20},{140,20}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract controlError "Control error (set point - measurement)" - annotation (Placement(transformation(extent={{-200,-10},{-180,10}}))); + annotation (Placement(transformation(extent={{-200,-16},{-180,4}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter P(final k=k) "Gain for proportional control action" annotation (Placement(transformation(extent={{-50,130},{-30,150}}))); @@ -75,10 +75,10 @@ block PID annotation (Placement(transformation(extent={{-140,60},{-120,80}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract errI1 if with_I "I error (before anti-windup compensation)" - annotation (Placement(transformation(extent={{-140,-10},{-120,10}}))); + annotation (Placement(transformation(extent={{-140,-4},{-120,16}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract errI2 if with_I "I error (after anti-windup compensation)" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + annotation (Placement(transformation(extent={{-100,-10},{-80,10}}))); Buildings.Controls.OBC.CDL.Continuous.Limiter lim( final uMax=yMax, final uMin=yMin) @@ -107,16 +107,17 @@ protected Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Dzero( final k=0) if not with_D "Zero input signal" - annotation (Evaluate=true,HideResult=true,Placement(transformation(extent={{-20,110},{0,130}}))); + annotation (Evaluate=true,HideResult=true,Placement(transformation(extent={{-50,90}, + {-30,110}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uS_revAct( final k=revAct/r) "Set point multiplied by reverse action sign" annotation (Placement(transformation(extent={{-200,30},{-180,50}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uMea_revAct( final k=revAct/r) "Set point multiplied by reverse action sign" - annotation (Placement(transformation(extent={{-180,-50},{-160,-30}}))); + annotation (Placement(transformation(extent={{-200,-50},{-180,-30}}))); Buildings.Controls.OBC.CDL.Continuous.Add addPD "Outputs P and D gains added" - annotation (Placement(transformation(extent={{20,116},{40,136}}))); + annotation (Placement(transformation(extent={{20,124},{40,144}}))); Buildings.Controls.OBC.CDL.Continuous.Add addPID "Outputs P, I and D gains added" annotation (Placement(transformation(extent={{80,80},{100,100}}))); @@ -138,7 +139,7 @@ protected Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Izero( final k=0) if not with_I "Zero input signal" - annotation (Placement(transformation(extent={{40,74},{60,94}}))); + annotation (Placement(transformation(extent={{-50,20},{-30,40}}))); Buildings.Controls.OBC.CDL.Continuous.Sources.Constant con( final k=0) if with_I "Constant zero" @@ -150,37 +151,40 @@ protected equation connect(u_s,uS_revAct.u) - annotation (Line(points={{-240,0},{-212,0},{-212,40},{-202,40}},color={0,0,127})); + annotation (Line(points={{-240,0},{-210,0},{-210,42},{-206,42},{-206,40},{ + -202,40}}, color={0,0,127})); connect(u_m,uMea_revAct.u) - annotation (Line(points={{0,-220},{0,-160},{-190,-160},{-190,-40},{-182,-40}},color={0,0,127})); + annotation (Line(points={{0,-220},{0,-160},{-210,-160},{-210,-40},{-202,-40}},color={0,0,127})); connect(D.u,errD.y) annotation (Line(points={{-52,70},{-118,70}}, color={0,0,127})); connect(errI1.u1,uS_revAct.y) - annotation (Line(points={{-142,6},{-170,6},{-170,40},{-178,40}},color={0,0,127})); + annotation (Line(points={{-142,12},{-170,12},{-170,40},{-178,40}}, + color={0,0,127})); connect(addPID.u1,addPD.y) - annotation (Line(points={{78,96},{70,96},{70,126},{42,126}},color={0,0,127})); + annotation (Line(points={{78,96},{50,96},{50,134},{42,134}},color={0,0,127})); connect(lim.y,y) annotation (Line(points={{142,90},{200,90},{200,0},{240,0}},color={0,0,127})); connect(antWinErr.y,antWinGai.u) annotation (Line(points={{182,60},{190,60},{190,-20},{182,-20}},color={0,0,127})); connect(addPD.u2,Dzero.y) - annotation (Line(points={{18,120},{2,120}}, color={0,0,127})); + annotation (Line(points={{18,128},{-10,128},{-10,100},{-28,100}},color={0,0,127})); connect(D.y,addPD.u2) - annotation (Line(points={{-28,70},{10,70},{10,120},{18,120}}, color={0,0,127})); + annotation (Line(points={{-28,70},{-10,70},{-10,128},{18,128}},color={0,0,127})); connect(addPID.u2,I.y) - annotation (Line(points={{78,84},{72,84},{72,0},{-28,0}},color={0,0,127})); + annotation (Line(points={{78,84},{60,84},{60,0},{-28,0}},color={0,0,127})); connect(antWinErr.u2,lim.y) annotation (Line(points={{158,54},{150,54},{150,90},{142,90}}, color={0,0,127})); connect(I.u,errI2.y) - annotation (Line(points={{-52,0},{-68,0}},color={0,0,127})); + annotation (Line(points={{-52,0},{-78,0}},color={0,0,127})); connect(errI1.y,errI2.u1) - annotation (Line(points={{-118,0},{-100,0},{-100,6},{-92,6}}, + annotation (Line(points={{-118,6},{-102,6}}, color={0,0,127})); connect(cheYMinMax.y,assMesYMinMax.u) annotation (Line(points={{142,-150},{158,-150}},color={255,0,255})); connect(Izero.y,addPID.u2) - annotation (Line(points={{62,84},{78,84}}, color={0,0,127})); + annotation (Line(points={{-28,30},{60,30},{60,84},{78,84}}, + color={0,0,127})); connect(con.y,I.y_reset_in) annotation (Line(points={{-78,-40},{-60,-40},{-60,-8},{-52,-8}},color={0,0,127})); connect(con1.y,I.trigger) @@ -190,27 +194,27 @@ equation connect(errD.u1,uS_revAct.y) annotation (Line(points={{-142,76},{-170,76},{-170,40},{-178,40}},color={0,0,127})); connect(addPD.u1, P.y) - annotation (Line(points={{18,132},{10,132},{10,140},{-28,140}}, - color={0,0,127})); + annotation (Line(points={{18,140},{-28,140}}, color={0,0,127})); connect(P.u, errP.y) annotation (Line(points={{-52,140},{-118,140}},color={0,0,127})); connect(addPID.y, lim.u) annotation (Line(points={{102,90},{118,90}},color={0,0,127})); - connect(addPID.y, antWinErr.u1) annotation (Line(points={{102,90},{110,90},{ - 110,66},{158,66}}, + connect(addPID.y, antWinErr.u1) annotation (Line(points={{102,90},{114,90},{ + 114,66},{158,66}}, color={0,0,127})); - connect(u_s, controlError.u1) annotation (Line(points={{-240,0},{-212,0},{ - -212,6},{-202,6}}, color={0,0,127})); + connect(u_s, controlError.u1) annotation (Line(points={{-240,0},{-202,0}}, + color={0,0,127})); connect(u_m, controlError.u2) annotation (Line(points={{0,-220},{0,-160},{ - -212,-160},{-212,-6},{-202,-6}}, color={0,0,127})); - connect(uMea_revAct.y, errP.u2) annotation (Line(points={{-158,-40},{-150,-40}, + -210,-160},{-210,-12},{-202,-12}}, + color={0,0,127})); + connect(uMea_revAct.y, errP.u2) annotation (Line(points={{-178,-40},{-150,-40}, {-150,134},{-142,134}}, color={0,0,127})); - connect(uMea_revAct.y, errD.u2) annotation (Line(points={{-158,-40},{-150,-40}, + connect(uMea_revAct.y, errD.u2) annotation (Line(points={{-178,-40},{-150,-40}, {-150,64},{-142,64}}, color={0,0,127})); - connect(uMea_revAct.y, errI1.u2) annotation (Line(points={{-158,-40},{-150, - -40},{-150,-6},{-142,-6}}, color={0,0,127})); - connect(antWinGai.y, errI2.u2) annotation (Line(points={{158,-20},{-100,-20}, - {-100,-6},{-92,-6}}, color={0,0,127})); + connect(uMea_revAct.y, errI1.u2) annotation (Line(points={{-178,-40},{-150, + -40},{-150,0},{-142,0}}, color={0,0,127})); + connect(antWinGai.y, errI2.u2) annotation (Line(points={{158,-20},{-110,-20}, + {-110,-6},{-102,-6}},color={0,0,127})); connect(kDer.y, D.k) annotation (Line(points={{-78,120},{-58,120},{-58,78},{ -52,78}}, color={0,0,127})); connect(TDer.y, D.T) annotation (Line(points={{-78,90},{-60,90},{-60,74},{-52, diff --git a/Buildings/Controls/OBC/CDL/Continuous/PIDWithReset.mo b/Buildings/Controls/OBC/CDL/Continuous/PIDWithReset.mo index 7cb8d02a2ca..f1852d4ab4d 100644 --- a/Buildings/Controls/OBC/CDL/Continuous/PIDWithReset.mo +++ b/Buildings/Controls/OBC/CDL/Continuous/PIDWithReset.mo @@ -61,7 +61,7 @@ block PIDWithReset annotation (Placement(transformation(extent={{-20,-20},{20,20}},rotation=90,origin={-160,-220}),iconTransformation(extent={{-20,-20},{20,20}},rotation=90,origin={-60,-120}))); Buildings.Controls.OBC.CDL.Continuous.Subtract controlError "Control error (set point - measurement)" - annotation (Placement(transformation(extent={{-200,-10},{-180,10}}))); + annotation (Placement(transformation(extent={{-200,-16},{-180,4}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter P(final k=k) "Proportional action" annotation (Placement(transformation(extent={{-50,130},{-30,150}}))); Buildings.Controls.OBC.CDL.Continuous.IntegratorWithReset I( @@ -81,10 +81,10 @@ block PIDWithReset annotation (Placement(transformation(extent={{-140,60},{-120,80}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract errI1 if with_I "I error (before anti-windup compensation)" - annotation (Placement(transformation(extent={{-140,-10},{-120,10}}))); + annotation (Placement(transformation(extent={{-140,-4},{-120,16}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract errI2 if with_I "I error (after anti-windup compensation)" - annotation (Placement(transformation(extent={{-90,-10},{-70,10}}))); + annotation (Placement(transformation(extent={{-100,-10},{-80,10}}))); Buildings.Controls.OBC.CDL.Continuous.Limiter lim( final uMax=yMax, final uMin=yMin) @@ -112,12 +112,12 @@ protected Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Dzero( final k=0) if not with_D "Zero input signal" - annotation (Evaluate=true,HideResult=true,Placement(transformation(extent={{-20,110}, - {0,130}}))); + annotation (Evaluate=true,HideResult=true,Placement(transformation(extent={{-50,90}, + {-30,110}}))); Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Izero( final k=0) if not with_I "Zero input signal" - annotation (Placement(transformation(extent={{20,74},{40,94}}))); + annotation (Placement(transformation(extent={{-50,20},{-30,40}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uS_revAct( final k=revAct/r) "Set point multiplied by reverse action sign" @@ -125,16 +125,16 @@ protected Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uMea_revAct( final k=revAct/r) "Set point multiplied by reverse action sign" - annotation (Placement(transformation(extent={{-180,-50},{-160,-30}}))); + annotation (Placement(transformation(extent={{-200,-50},{-180,-30}}))); Buildings.Controls.OBC.CDL.Continuous.Add addPD "Outputs P and D gains added" - annotation (Placement(transformation(extent={{20,116},{40,136}}))); + annotation (Placement(transformation(extent={{20,124},{40,144}}))); Buildings.Controls.OBC.CDL.Continuous.Add addPID "Outputs P, I and D gains added" annotation (Placement(transformation(extent={{80,80},{100,100}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract antWinErr if with_I "Error for anti-windup compensation" - annotation (Placement(transformation(extent={{162,50},{182,70}}))); + annotation (Placement(transformation(extent={{160,50},{180,70}}))); Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter antWinGai( k=1/(k*Ni)) if with_I "Gain for anti-windup compensation" @@ -142,7 +142,7 @@ protected Buildings.Controls.OBC.CDL.Continuous.Sources.Constant yResSig( final k=y_reset) if with_I "Signal for y_reset" - annotation (Placement(transformation(extent={{-140,-90},{-120,-70}}))); + annotation (Placement(transformation(extent={{-140,-84},{-120,-64}}))); Buildings.Controls.OBC.CDL.Continuous.Subtract addRes if with_I "Adder for integrator reset" annotation (Placement(transformation(extent={{-100,-90},{-80,-70}}))); @@ -159,65 +159,72 @@ equation connect(trigger,I.trigger) annotation (Line(points={{-160,-220},{-160,-140},{-40,-140},{-40,-12}},color={255,0,255})); connect(u_s,uS_revAct.u) - annotation (Line(points={{-240,0},{-212,0},{-212,40},{-202,40}},color={0,0,127})); + annotation (Line(points={{-240,0},{-210,0},{-210,40},{-202,40}},color={0,0,127})); connect(u_m,uMea_revAct.u) - annotation (Line(points={{0,-220},{0,-160},{-210,-160},{-210,-40},{-182,-40}},color={0,0,127})); + annotation (Line(points={{0,-220},{0,-160},{-210,-160},{-210,-40},{-202,-40}},color={0,0,127})); connect(errD.u2,uMea_revAct.y) - annotation (Line(points={{-142,64},{-150,64},{-150,-40},{-158,-40}}, color={0,0,127})); + annotation (Line(points={{-142,64},{-150,64},{-150,-40},{-178,-40}}, color={0,0,127})); connect(D.u,errD.y) annotation (Line(points={{-52,70},{-118,70}}, color={0,0,127})); connect(errI1.u1,uS_revAct.y) - annotation (Line(points={{-142,6},{-170,6},{-170,40},{-178,40}},color={0,0,127})); + annotation (Line(points={{-142,12},{-170,12},{-170,40},{-178,40}}, + color={0,0,127})); connect(addPID.u1,addPD.y) - annotation (Line(points={{78,96},{60,96},{60,126},{42,126}},color={0,0,127})); + annotation (Line(points={{78,96},{50,96},{50,134},{42,134}},color={0,0,127})); connect(lim.y,y) annotation (Line(points={{142,90},{200,90},{200,0},{240,0}},color={0,0,127})); connect(antWinErr.y,antWinGai.u) - annotation (Line(points={{184,60},{190,60},{190,-20},{182,-20}},color={0,0,127})); + annotation (Line(points={{182,60},{190,60},{190,-20},{182,-20}},color={0,0,127})); connect(addPD.u2,Dzero.y) - annotation (Line(points={{18,120},{2,120}}, color={0,0,127})); + annotation (Line(points={{18,128},{-10,128},{-10,100},{-28,100}}, + color={0,0,127})); connect(D.y,addPD.u2) - annotation (Line(points={{-28,70},{10,70},{10,120},{18,120}}, color={0,0,127})); + annotation (Line(points={{-28,70},{-10,70},{-10,128},{18,128}},color={0,0,127})); connect(addPID.u2,I.y) - annotation (Line(points={{78,84},{68,84},{68,0},{-28,0}},color={0,0,127})); + annotation (Line(points={{78,84},{60,84},{60,0},{-28,0}},color={0,0,127})); connect(addRes.y,I.y_reset_in) annotation (Line(points={{-78,-80},{-60,-80},{-60,-8},{-52,-8}},color={0,0,127})); connect(antWinErr.u2,lim.y) - annotation (Line(points={{160,54},{150,54},{150,90},{142,90}}, color={0,0,127})); + annotation (Line(points={{158,54},{150,54},{150,90},{142,90}}, color={0,0,127})); connect(I.u,errI2.y) - annotation (Line(points={{-52,0},{-68,0}},color={0,0,127})); + annotation (Line(points={{-52,0},{-78,0}},color={0,0,127})); connect(errI1.y,errI2.u1) - annotation (Line(points={{-118,0},{-100,0},{-100,6},{-92,6}}, color={0,0,127})); + annotation (Line(points={{-118,6},{-102,6}}, color={0,0,127})); connect(controlError.u1,u_s) - annotation (Line(points={{-202,6},{-212,6},{-212,0},{-240,0}}, color={0,0,127})); + annotation (Line(points={{-202,0},{-240,0}}, color={0,0,127})); connect(cheYMinMax.y,assMesYMinMax.u) annotation (Line(points={{142,-150},{158,-150}},color={255,0,255})); connect(Izero.y,addPID.u2) - annotation (Line(points={{42,84},{78,84}}, color={0,0,127})); + annotation (Line(points={{-28,30},{60,30},{60,84},{78,84}}, + color={0,0,127})); connect(errP.u1,uS_revAct.y) annotation (Line(points={{-142,146},{-170,146},{-170,40},{-178,40}},color={0,0,127})); connect(errD.u1,uS_revAct.y) annotation (Line(points={{-142,76},{-170,76},{-170,40},{-178,40}},color={0,0,127})); connect(addPD.u1, P.y) - annotation (Line(points={{18,132},{10,132},{10,140},{-28,140}}, color={0,0,127})); + annotation (Line(points={{18,140},{-28,140}}, color={0,0,127})); connect(P.u, errP.y) annotation (Line(points={{-52,140},{-118,140}},color={0,0,127})); connect(addPID.y, lim.u) annotation (Line(points={{102,90},{118,90}},color={0,0,127})); - connect(addPID.y, antWinErr.u1) annotation (Line(points={{102,90},{110,90},{110, - 66},{160,66}}, color={0,0,127})); + connect(addPID.y, antWinErr.u1) annotation (Line(points={{102,90},{114,90},{ + 114,66},{158,66}}, color={0,0,127})); connect(addRes.u1, yResSig.y) - annotation (Line(points={{-102,-74},{-110,-74},{-110,-80},{-118,-80}}, color={0,0,127})); - connect(u_m, controlError.u2) annotation (Line(points={{0,-220},{0,-160},{-210, - -160},{-210,-6},{-202,-6}}, color={0,0,127})); - connect(uMea_revAct.y, errP.u2) annotation (Line(points={{-158,-40},{-150,-40}, + annotation (Line(points={{-102,-74},{-118,-74}}, color={0,0,127})); + connect(u_m, controlError.u2) annotation (Line(points={{0,-220},{0,-160},{ + -210,-160},{-210,-12},{-202,-12}}, + color={0,0,127})); + connect(uMea_revAct.y, errP.u2) annotation (Line(points={{-178,-40},{-150,-40}, {-150,134},{-142,134}}, color={0,0,127})); - connect(uMea_revAct.y, errI1.u2) annotation (Line(points={{-158,-40},{-150,-40}, - {-150,-6},{-142,-6}}, color={0,0,127})); - connect(antWinGai.y, errI2.u2) annotation (Line(points={{158,-20},{-100,-20},{ - -100,-6},{-92,-6}}, color={0,0,127})); - connect(addPD.y, addRes.u2) annotation (Line(points={{42,126},{60,126},{60,-100}, - {-110,-100},{-110,-86},{-102,-86}}, color={0,0,127})); + connect(uMea_revAct.y, errI1.u2) annotation (Line(points={{-178,-40},{-150, + -40},{-150,0},{-142,0}}, + color={0,0,127})); + connect(antWinGai.y, errI2.u2) annotation (Line(points={{158,-20},{-110,-20}, + {-110,-6},{-102,-6}}, + color={0,0,127})); + connect(addPD.y, addRes.u2) annotation (Line(points={{42,134},{50,134},{50, + -100},{-110,-100},{-110,-86},{-102,-86}}, + color={0,0,127})); connect(kDer.y, D.k) annotation (Line(points={{-78,120},{-58,120},{-58,78},{ -52,78}}, color={0,0,127})); connect(TDer.y, D.T) annotation (Line(points={{-78,90},{-60,90},{-60,74},{-52, diff --git a/Buildings/Controls/OBC/Utilities/PIDWithInputGains.mo b/Buildings/Controls/OBC/Utilities/PIDWithInputGains.mo new file mode 100644 index 00000000000..e3aa0f2c028 --- /dev/null +++ b/Buildings/Controls/OBC/Utilities/PIDWithInputGains.mo @@ -0,0 +1,558 @@ +within Buildings.Controls.OBC.Utilities; +block PIDWithInputGains + "P, PI, PD, and PID controller with output reset and input gains" + parameter Buildings.Controls.OBC.CDL.Types.SimpleController controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.PI + "Type of controller"; + parameter Real r( + min=100*CDL.Constants.eps)=1 + "Typical range of control error, used for scaling the control error"; + parameter Real yMax=1 + "Upper limit of output" + annotation (Dialog(group="Limits")); + parameter Real yMin=0 + "Lower limit of output" + annotation (Dialog(group="Limits")); + parameter Real Ni( + min=100*CDL.Constants.eps)=0.9 + "Ni*Ti is time constant of anti-windup compensation" + annotation (Dialog(tab="Advanced",group="Integrator anti-windup",enable=controllerType == CDL.Types.SimpleController.PI or controllerType ==CDL.Types.SimpleController.PID)); + parameter Real Nd( + min=100*CDL.Constants.eps)=10 + "The higher Nd, the more ideal the derivative block" + annotation (Dialog(tab="Advanced",group="Derivative block",enable=controllerType == CDL.Types.SimpleController.PD or controllerType ==CDL.Types.SimpleController.PID)); + parameter Real xi_start=0 + "Initial value of integrator state" + annotation (Dialog(tab="Advanced",group="Initialization",enable=controllerType == CDL.Types.SimpleController.PI or controllerType == CDL.Types.SimpleController.PID)); + parameter Real yd_start=0 + "Initial value of derivative output" + annotation (Dialog(tab="Advanced",group="Initialization",enable=controllerType == CDL.Types.SimpleController.PD or controllerType == CDL.Types.SimpleController.PID)); + parameter Boolean reverseActing=true + "Set to true for reverse acting, or false for direct acting control action"; + parameter Real y_reset=xi_start + "Value to which the controller output is reset if the boolean trigger has a rising edge" + annotation (Dialog(enable=controllerType == CDL.Types.SimpleController.PI or controllerType == CDL.Types.SimpleController.PID,group="Integrator reset")); + Buildings.Controls.OBC.CDL.Interfaces.RealInput u_s + "Connector for setpoint input signal" + annotation (Placement(transformation(extent={{-260,-20},{-220,20}}),iconTransformation(extent={{-140,-20},{-100,20}}))); + Buildings.Controls.OBC.CDL.Interfaces.RealInput u_m + "Connector for measurement input signal" + annotation (Placement(transformation(origin={0,-220},extent={{20,-20},{-20,20}},rotation=270),iconTransformation(extent={{20,-20},{-20,20}},rotation=270,origin={0,-120}))); + Buildings.Controls.OBC.CDL.Interfaces.RealInput k( + min=100*Buildings.Controls.OBC.CDL.Constants.eps) + "Connector for control gain signal" + annotation (Placement(transformation(extent={{-260,160},{-220,200}}),iconTransformation(extent={{-140,60},{-100,100}}))); + Buildings.Controls.OBC.CDL.Interfaces.RealInput Ti( + quantity="Time", + unit="s", + min=100*Buildings.Controls.OBC.CDL.Constants.eps) + if with_I + "Connector for time constant signal for the integral term" + annotation (Placement(transformation(extent={{-260,100},{-220,140}}), + iconTransformation(extent={{-140,20},{-100,60}}))); + Buildings.Controls.OBC.CDL.Interfaces.RealInput Td( + quantity="Time", + unit="s", + min=100*Buildings.Controls.OBC.CDL.Constants.eps) + if with_D + "Connector for time constant signal for the derivative term" + annotation (Placement(transformation(extent={{-260,40},{-220,80}}), iconTransformation(extent={{-140,-60},{-100,-20}}))); + Buildings.Controls.OBC.CDL.Interfaces.RealOutput y + "Connector for actuator output signal" + annotation (Placement(transformation(extent={{220,-20},{260,20}}),iconTransformation(extent={{100,-20},{140,20}}))); + Buildings.Controls.OBC.CDL.Interfaces.BooleanInput trigger + "Resets the controller output when trigger becomes true" + annotation (Placement(transformation(extent={{-20,-20},{20,20}},rotation=90,origin={-160,-220}),iconTransformation(extent={{-20,-20},{20,20}},rotation=90,origin={-60,-120}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract controlError + "Control error (set point - measurement)" + annotation (Placement(transformation(extent={{-200,-16},{-180,4}}))); + Buildings.Controls.OBC.CDL.Continuous.Multiply P + "Proportional action" + annotation (Placement(transformation(extent={{-50,130},{-30,150}}))); + Buildings.Controls.OBC.CDL.Continuous.IntegratorWithReset I( + final k=1, + final y_start=xi_start) if with_I + "Integral term" + annotation (Placement(transformation(extent={{-50,-10},{-30,10}}))); + Buildings.Controls.OBC.CDL.Continuous.Derivative D( + final y_start=yd_start) if with_D + "Derivative term" + annotation (Placement(transformation(extent={{-50,60},{-30,80}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract errP + "P error" + annotation (Placement(transformation(extent={{-140,130},{-120,150}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract errD if with_D + "D error" + annotation (Placement(transformation(extent={{-140,60},{-120,80}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract errI1 if with_I + "I error (before anti-windup compensation)" + annotation (Placement(transformation(extent={{-140,-4},{-120,16}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract errI2 if with_I + "I error (after anti-windup compensation)" + annotation (Placement(transformation(extent={{-100,-10},{-80,10}}))); + Buildings.Controls.OBC.CDL.Continuous.Limiter lim( + final uMax=yMax, + final uMin=yMin) + "Limiter" + annotation (Placement(transformation(extent={{120,80},{140,100}}))); + Buildings.Controls.OBC.CDL.Continuous.Divide antWinGai2 "Outputs of anti-windup compensation" + annotation (Placement(transformation(extent={{100,-30},{80,-10}}))); + Buildings.Controls.OBC.CDL.Continuous.Divide gaiI if with_I "Gain of the integral term" + annotation (Placement(transformation(extent={{-200,116},{-180,136}}))); + Buildings.Controls.OBC.CDL.Continuous.Multiply errIWithGai if with_I + "I error (after multiplying with the gain of the integral term)" + annotation (Placement(transformation(extent={{-84,28},{-64,48}}))); + Buildings.Controls.OBC.CDL.Continuous.Multiply mulkTd if with_D + "Product of k and Td" + annotation (Placement(transformation(extent={{-200,150},{-180,170}}))); + Buildings.Controls.OBC.CDL.Continuous.GreaterThreshold greThrkTd( + t=1E-6, + h=1E-6/2) + if with_D + "Check if k*Td is larger than 0" + annotation (Placement(transformation(extent={{140,160},{160,180}}))); + +protected + final parameter Real revAct= + if reverseActing then + 1 + else + -1 + "Switch for sign for reverse or direct acting controller"; + final parameter Boolean with_I=controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PI or controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PID + "Boolean flag to enable integral action" + annotation (Evaluate=true,HideResult=true); + final parameter Boolean with_D=controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PD or controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PID + "Boolean flag to enable derivative action" + annotation (Evaluate=true,HideResult=true); + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Dzero( + final k=0) if not with_D + "Zero input signal" + annotation (Evaluate=true,HideResult=true,Placement(transformation(extent={{-50,90}, + {-30,110}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant Izero( + final k=0) if not with_I + "Zero input signal" + annotation (Placement(transformation(extent={{-50,20},{-30,40}}))); + Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uS_revAct( + final k=revAct/r) "Set point multiplied by reverse action sign" + annotation (Placement(transformation(extent={{-200,30},{-180,50}}))); + Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter uMea_revAct( + final k=revAct/r) + "Set point multiplied by reverse action sign" + annotation (Placement(transformation(extent={{-200,-50},{-180,-30}}))); + Buildings.Controls.OBC.CDL.Continuous.Add addPD + "Outputs P and D gains added" + annotation (Placement(transformation(extent={{20,124},{40,144}}))); + Buildings.Controls.OBC.CDL.Continuous.Add addPID + "Outputs P, I and D gains added" + annotation (Placement(transformation(extent={{80,80},{100,100}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract antWinErr if with_I + "Error for anti-windup compensation" + annotation (Placement(transformation(extent={{160,50},{180,70}}))); + Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter antWinGai1(k=1/Ni) + if with_I "Gain for anti-windup compensation without the proportional gain" + annotation (Placement(transformation(extent={{180,-30},{160,-10}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant yResSig( + final k=y_reset) if with_I + "Signal for y_reset" + annotation (Placement(transformation(extent={{-140,-84},{-120,-64}}))); + Buildings.Controls.OBC.CDL.Continuous.Subtract addRes if with_I + "Adder for integrator reset" + annotation (Placement(transformation(extent={{-100,-90},{-80,-70}}))); + Buildings.Controls.OBC.CDL.Logical.Sources.Constant cheYMinMax( + final k=yMin < yMax) "Check for values of yMin and yMax" + annotation (Placement(transformation(extent={{140,120},{160,140}}))); + Buildings.Controls.OBC.CDL.Utilities.Assert assMesYMinMax( + message="LimPID: Limits must be yMin < yMax") "Assertion on yMin and yMax" + annotation (Placement(transformation(extent={{180,120},{200,140}}))); + Buildings.Controls.OBC.CDL.Utilities.Assert assMeskTd( + message="LimPIDWithInputGains: Limits must be k*Td > 0") + if with_D + "Assertion on k and Td" + annotation (Placement(transformation(extent={{180,160},{200,180}}))); + Buildings.Controls.OBC.CDL.Continuous.MultiplyByParameter gaiT(final k=1/Nd) if with_D + "Gain to compute time constant for derivative action" + annotation (Placement(transformation(extent={{-140,100},{-120,120}}))); + +equation + connect(trigger,I.trigger) + annotation (Line(points={{-160,-220},{-160,-140},{-40,-140},{-40,-12}},color={255,0,255})); + connect(u_s,uS_revAct.u) + annotation (Line(points={{-240,0},{-210,0},{-210,40},{-202,40}},color={0,0,127})); + connect(u_m,uMea_revAct.u) + annotation (Line(points={{0,-220},{0,-160},{-210,-160},{-210,-40},{-202,-40}},color={0,0,127})); + connect(errD.u2,uMea_revAct.y) + annotation (Line(points={{-142,64},{-150,64},{-150,-40},{-178,-40}}, color={0,0,127})); + connect(D.u,errD.y) + annotation (Line(points={{-52,70},{-118,70}}, color={0,0,127})); + connect(errI1.u1,uS_revAct.y) + annotation (Line(points={{-142,12},{-170,12},{-170,40},{-178,40}}, + color={0,0,127})); + connect(addPID.u1,addPD.y) + annotation (Line(points={{78,96},{50,96},{50,134},{42,134}},color={0,0,127})); + connect(lim.y,y) + annotation (Line(points={{142,90},{200,90},{200,0},{240,0}},color={0,0,127})); + connect(antWinErr.y, antWinGai1.u) + annotation (Line(points={{182,60},{190,60},{190,-20},{182,-20}}, color={0,0,127})); + connect(addPD.u2,Dzero.y) + annotation (Line(points={{18,128},{-10,128},{-10,100},{-28,100}}, + color={0,0,127})); + connect(D.y,addPD.u2) + annotation (Line(points={{-28,70},{-10,70},{-10,128},{18,128}},color={0,0,127})); + connect(addPID.u2,I.y) + annotation (Line(points={{78,84},{68,84},{68,0},{-28,0}},color={0,0,127})); + connect(addRes.y,I.y_reset_in) + annotation (Line(points={{-78,-80},{-60,-80},{-60,-8},{-52,-8}},color={0,0,127})); + connect(antWinErr.u2,lim.y) + annotation (Line(points={{158,54},{150,54},{150,90},{142,90}}, color={0,0,127})); + connect(errI1.y,errI2.u1) + annotation (Line(points={{-118,6},{-102,6}}, color={0,0,127})); + connect(controlError.u1,u_s) + annotation (Line(points={{-202,0},{-240,0}}, color={0,0,127})); + connect(cheYMinMax.y,assMesYMinMax.u) + annotation (Line(points={{162,130},{178,130}}, color={255,0,255})); + connect(Izero.y,addPID.u2) + annotation (Line(points={{-28,30},{58,30},{58,84},{78,84}}, + color={0,0,127})); + connect(errP.u1,uS_revAct.y) + annotation (Line(points={{-142,146},{-170,146},{-170,40},{-178,40}},color={0,0,127})); + connect(errD.u1,uS_revAct.y) + annotation (Line(points={{-142,76},{-170,76},{-170,40},{-178,40}},color={0,0,127})); + connect(addPD.u1, P.y) + annotation (Line(points={{18,140},{-28,140}}, color={0,0,127})); + connect(addPID.y, lim.u) + annotation (Line(points={{102,90},{118,90}},color={0,0,127})); + connect(addPID.y, antWinErr.u1) + annotation (Line(points={{102,90},{114,90},{114,66},{158,66}},color={0,0,127})); + connect(addRes.u1, yResSig.y) + annotation (Line(points={{-102,-74},{-118,-74}}, color={0,0,127})); + connect(u_m, controlError.u2) + annotation (Line(points={{0,-220},{0,-160},{-210,-160},{-210,-12},{-202,-12}}, + color={0,0,127})); + connect(uMea_revAct.y, errP.u2) + annotation (Line(points={{-178,-40},{-150,-40},{-150,134},{-142,134}}, color={0,0,127})); + connect(uMea_revAct.y, errI1.u2) + annotation (Line(points={{-178,-40},{-150,-40},{-150,0},{-142,0}}, color={0,0,127})); + connect(addPD.y, addRes.u2) + annotation (Line(points={{42,134},{50,134},{50,-100},{-110,-100},{-110,-86}, + {-102,-86}}, color={0,0,127})); + connect(errP.y, P.u2) + annotation (Line(points={{-118,140},{-74,140},{-74,134},{-52,134}}, color={0,0,127})); + connect(P.u1, k) + annotation (Line(points={{-52,146},{-70,146},{-70,188},{-212,188},{-212,180}, + {-240,180}}, color={0,0,127})); + connect(antWinGai1.y, antWinGai2.u1) + annotation (Line(points={{158,-20},{140,-20},{140,-14},{102,-14}}, + color={0,0,127})); + connect(antWinGai2.u2, k) + annotation (Line(points={{102,-26},{108,-26},{108,188},{-212,188},{-212,180}, + {-240,180}}, color={0,0,127})); + connect(antWinGai2.y, errI2.u2) + annotation (Line(points={{78,-20},{-110,-20},{-110,-6},{-102,-6}}, + color={0,0,127})); + connect(gaiI.u1, k) + annotation (Line(points={{-202,132},{-212,132},{-212,180},{-240,180}}, color={0,0,127})); + connect(gaiI.u2, Ti) + annotation (Line(points={{-202,120},{-240,120}}, color={0,0,127})); + connect(gaiI.y, errIWithGai.u1) + annotation (Line(points={{-178,126},{-100,126},{-100,44},{-86,44}},color={0,0,127})); + connect(errI2.y, errIWithGai.u2) + annotation (Line(points={{-78,0},{-70,0},{-70,20},{-100,20},{-100,32},{-86, + 32}}, color={0,0,127})); + connect(errIWithGai.y, I.u) + annotation (Line(points={{-62,38},{-60,38},{-60,0},{-52,0}}, color={0,0,127})); + connect(mulkTd.u1, k) + annotation (Line(points={{-202,166},{-212,166},{-212,180},{-240,180}},color={0,0,127})); + connect(greThrkTd.y, assMeskTd.u) + annotation (Line(points={{162,170},{178,170}}, color={255,0,255})); + connect(mulkTd.y, greThrkTd.u) + annotation (Line(points={{-178,160},{-168,160},{-168,184},{132,184},{132, + 170},{138,170}}, color={0,0,127})); + connect(mulkTd.u2, Td) + annotation (Line(points={{-202,154},{-206,154},{-206,60},{-240,60}},color={0,0,127})); + connect(Td, gaiT.u) + annotation (Line(points={{-240,60},{-180,60},{-180,110},{-142,110}}, color={0,0,127})); + connect(gaiT.y, D.T) + annotation (Line(points={{-118,110},{-84,110},{-84,74},{-52,74}}, color={0,0,127})); + connect(mulkTd.y, D.k) annotation (Line(points={{-178,160},{-168,160},{-168, + 184},{-80,184},{-80,78},{-52,78}}, color={0,0,127})); + annotation ( + defaultComponentName="conPID", + Icon( + coordinateSystem( + extent={{-100,-100},{100,100}}), + graphics={ + Rectangle( + extent={{-100,-100},{100,100}}, + lineColor={0,0,127}, + fillColor={255,255,255}, + fillPattern=FillPattern.Solid), + Rectangle( + extent={{-6,-20},{66,-66}}, + lineColor={255,255,255}, + fillColor={255,255,255}, + fillPattern=FillPattern.Solid), + Text( + visible=(controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.P), + extent={{-32,-22},{68,-62}}, + lineColor={0,0,0}, + textString="P", + fillPattern=FillPattern.Solid, + fillColor={175,175,175}), + Text( + visible=(controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PI), + extent={{-26,-22},{74,-62}}, + lineColor={0,0,0}, + textString="PI", + fillPattern=FillPattern.Solid, + fillColor={175,175,175}), + Text( + visible=(controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PD), + extent={{-16,-22},{88,-62}}, + lineColor={0,0,0}, + fillPattern=FillPattern.Solid, + fillColor={175,175,175}, + textString="P D"), + Text( + visible=(controllerType == Buildings.Controls.OBC.CDL.Types.SimpleController.PID), + extent={{-14,-22},{86,-62}}, + lineColor={0,0,0}, + textString="PID", + fillPattern=FillPattern.Solid, + fillColor={175,175,175}), + Polygon( + points={{-80,82},{-88,60},{-72,60},{-80,82}}, + lineColor={192,192,192}, + fillColor={192,192,192}, + fillPattern=FillPattern.Solid), + Line( + points={{-80,68},{-80,-100}}, + color={192,192,192}), + Line( + points={{-90,-80},{70,-80}}, + color={192,192,192}), + Polygon( + points={{74,-80},{52,-72},{52,-88},{74,-80}}, + lineColor={192,192,192}, + fillColor={192,192,192}, + fillPattern=FillPattern.Solid), + Text( + extent={{-150,150},{150,110}}, + textString="%name", + textColor={0,0,255}), + Line( + points={{-80,-80},{-80,-22}}, + color={0,0,0}), + Line( + points={{-80,-22},{6,56}}, + color={0,0,0}), + Line( + points={{6,56},{68,56}}, + color={0,0,0}), + Rectangle( + extent=DynamicSelect({{100,-100},{84,-100}},{{100,-100},{84,-100+(y-yMin)/(yMax-yMin)*200}}), + fillColor={175,175,175}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None, + lineColor={0,0,0})}), + Diagram( + coordinateSystem( + extent={{-220,-200},{220,200}}), graphics={Rectangle( + extent={{-56,180},{-24,-16}}, + fillColor={215,215,215}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), Text( + extent={{-52,184},{-28,156}}, + pattern=LinePattern.None, + fillColor={215,215,215}, + fillPattern=FillPattern.Solid, + lineColor={0,0,0}, + textString="PID")}), + Documentation( + info=" +

+PID controller in the standard form +

+

+yu = k/r   (e(t) + 1 ⁄ Ti   ∫ e(τ) dτ + Td d⁄dt e(t)), +

+

+with output reset, +where +yu is the control signal before output limitation, +e(t) = us(t) - um(t) is the control error, +with us being the set point and um being +the measured quantity, +k is the gain, +Ti is the time constant of the integral term, +Td is the time constant of the derivative term, +r is a scaling factor, with default r=1. +The scaling factor should be set to the typical order of magnitude of the range of the error e. +For example, you may set r=100 to r=1000 +if the control input is a pressure of a heating water circulation pump in units of Pascal, or +leave r=1 if the control input is a room temperature. +

+

+Note that the units of k are the inverse of the units of the control error, +while the units of Ti and Td are seconds. +

+

+The actual control output is +

+

+y = min( ymax, max( ymin, y)), +

+

+where ymin and ymax are limits for the control signal. +

+

+This block is identical to + +Buildings.Controls.OBC.CDL.Continuous.PIDWithReset, +except that the controller gains +k, Ti and Td are inputs rather than parameters. +

+

P, PI, PD, or PID action

+

+Through the parameter controllerType, the controller can be configured +as P, PI, PD or PID controller. The default configuration is PI. +

+

Reverse or direct action

+

+Through the parameter reverseActing, the controller can be configured to +be reverse or direct acting. +The above standard form is reverse acting, which is the default configuration. +For a reverse acting controller, for a constant set point, +an increase in measurement signal u_m decreases the control output signal y +(Montgomery and McDowall, 2008). +Thus, +

+ +

+If reverseAction=false, then the error e above is multiplied by -1. +

+

Anti-windup compensation

+

+The controller anti-windup compensation is as follows: +Instead of the above basic control law, the implementation is +

+

+yu = k   (e(t) ⁄ r + 1 ⁄ Ti   ∫ (-Δy + e(τ) ⁄ r) dτ + Td ⁄ r d⁄dt e(t)), +

+

+where the anti-windup compensation Δy is +

+

+Δy = (yu - y) ⁄ (k Ni), +

+

+where +Ni > 0 is the time constant for the anti-windup compensation. +To accelerate the anti-windup, decrease Ni. +

+

+Note that the anti-windup term (-Δy + e(τ) ⁄ r) shows that the range of +the typical control error r should be set to a reasonable value so that +

+

+e(τ) ⁄ r = (us(τ) - um(τ)) ⁄ r +

+

+has order of magnitude one, and hence the anti-windup compensation should work well. +

+

Reset of the controller output

+

+Whenever the value of boolean input signal trigger changes from +false to true, the controller output is reset by setting +y to the value of the parameter y_reset. +

+

Approximation of the derivative term

+

+The derivative of the control error d ⁄ dt e(t) is approximated using +

+

+d⁄dt x(t) = (e(t)-x(t)) Nd ⁄ Td, +

+

+and +

+

+d⁄dt e(t) ≈ Nd (e(t)-x(t)), +

+

+where x(t) is an internal state. +

+

Guidance for tuning the control gains

+

+The parameters of the controller can be manually adjusted by performing +closed loop tests (= controller + plant connected +together) and using the following strategy: +

+
    +
  1. Set very large limits, e.g., set ymax = 1000. +
  2. +
  3. +Select a P-controller and manually enlarge the parameter k +(the total gain of the controller) until the closed-loop response +cannot be improved any more. +
  4. +
  5. +Select a PI-controller and manually adjust the parameters +k and Ti (the time constant of the integrator). +The first value of Ti can be selected such that it is in the +order of the time constant of the oscillations occurring with +the P-controller. If, e.g., oscillations in the order of 100 seconds +occur in the previous step, start with Ti=1/100 seconds. +
  6. +
  7. +If you want to make the reaction of the control loop faster +(but probably less robust against disturbances and measurement noise) +select a PID-controller and manually adjust parameters +k, Ti, Td (time constant of derivative block). +
  8. +
  9. +Set the limits yMax and yMin according to your specification. +
  10. +
  11. +Perform simulations such that the output of the PID controller +goes in its limits. Tune Ni (Ni Ti is the time constant of +the anti-windup compensation) such that the input to the limiter +block (= lim.u) goes quickly enough back to its limits. +If Ni is decreased, this happens faster. If Ni is very large, the +anti-windup compensation is not effective and the controller works bad. +
  12. +
+

References

+

+R. Montgomery and R. McDowall (2008). +\"Fundamentals of HVAC Control Systems.\" +American Society of Heating Refrigerating and Air-Conditioning Engineers Inc. Atlanta, GA. +

+", + revisions=" + +")); +end PIDWithInputGains; diff --git a/Buildings/Controls/OBC/Utilities/Validation/PIDWithInputGains.mo b/Buildings/Controls/OBC/Utilities/Validation/PIDWithInputGains.mo new file mode 100644 index 00000000000..09edd6b26fb --- /dev/null +++ b/Buildings/Controls/OBC/Utilities/Validation/PIDWithInputGains.mo @@ -0,0 +1,156 @@ +within Buildings.Controls.OBC.Utilities.Validation; +model PIDWithInputGains + "Test model for PIDWithInputGains" + Buildings.Controls.OBC.CDL.Continuous.Sources.Pulse pulse( + period=0.25) + "Setpoint" + annotation (Placement(transformation(extent={{-30,14},{-10,34}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Constant const( + k=0.5) + "Measured value" + annotation (Placement(transformation(extent={{-30,-22},{-10,-2}}))); + Buildings.Controls.OBC.Utilities.PIDWithInputGains PIDWitInpGai( + controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.PID) + "PID controller with input gains" + annotation (Placement(transformation(extent={{20,-30},{40,-10}}))); + Buildings.Controls.OBC.CDL.Continuous.PIDWithReset PID( + controllerType=Buildings.Controls.OBC.CDL.Types.SimpleController.PID, + k=1, + Ti=0.5, + Td=0.1) + "PID controller with constant gains" + annotation (Placement(transformation(extent={{20,20},{40,40}}))); + Buildings.Controls.OBC.CDL.Logical.Sources.Pulse resSig(period=1) + "Reset signal" + annotation (Placement(transformation(extent={{-80,40},{-60,60}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Pulse k( + amplitude=0.2, + width=0.4, + period=1, + shift=0.6, + offset=1) + "Control gain signal" + annotation (Placement(transformation(extent={{-80,0},{-60,20}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Pulse Ti( + amplitude=0.2, + width=0.4, + period=1, + shift=0.6, + offset=0.5) + "Time constant signal for the integral term" + annotation (Placement(transformation(extent={{-80,-40},{-60,-20}}))); + Buildings.Controls.OBC.CDL.Continuous.Sources.Pulse Td( + amplitude=0.1, + width=0.4, + period=1, + shift=0.6, + offset=0.1) + "Time constant signal for the derivative term" + annotation (Placement(transformation(extent={{-80,-80},{-60,-60}}))); + + CDL.Continuous.Abs abs1 "Absolute value of controller output" + annotation (Placement(transformation(extent={{110,-10},{130,10}}))); + CDL.Continuous.Subtract sub "Difference in controller output" + annotation (Placement(transformation(extent={{80,-10},{100,10}}))); + CDL.Continuous.LessThreshold lesThr(t=1E-5, h=1E-4) + "Output true if outputs are bigger than threshold" + annotation (Placement(transformation(extent={{140,-10},{160,10}}))); + CDL.Utilities.Assert assMes(message="Control outputs differ more than expected") + "Make sure outputs are within expected tolerance" + annotation (Placement(transformation(extent={{200,20},{220,40}}))); + CDL.Continuous.Sources.ModelTime modTim + "Model time" + annotation (Placement(transformation(extent={{80,40},{100,60}}))); + CDL.Continuous.GreaterThreshold greThr(t=0.59) + "Output true if model time is below 0.6" + annotation (Placement(transformation(extent={{140,40},{160,60}}))); + CDL.Logical.Or or2 + "Output true either if time is bigger than 0.59, or if tolerance is maintained" + annotation (Placement(transformation(extent={{170,20},{190,40}}))); +equation + connect(resSig.y, PID.trigger) annotation (Line(points={{-58,50},{0,50},{0,10}, + {24,10},{24,18}}, color={255,0,255})); + connect(PIDWitInpGai.trigger, PID.trigger) annotation (Line(points={{24, + -32},{24,-60},{0,-60},{0,10},{24,10},{24,18}}, color={255,0,255})); + connect(pulse.y, PID.u_s) annotation (Line(points={{-8,24},{14,24},{14,30},{ + 18,30}}, color={0,0,127})); + connect(PIDWitInpGai.u_s, PID.u_s) annotation (Line(points={{18,-20},{14, + -20},{14,30},{18,30}}, color={0,0,127})); + connect(const.y, PID.u_m) annotation (Line(points={{-8,-12},{8,-12},{8,0},{30, + 0},{30,18}}, color={0,0,127})); + connect(PIDWitInpGai.u_m, PID.u_m) annotation (Line(points={{30,-32},{30, + -40},{8,-40},{8,0},{30,0},{30,18}}, color={0,0,127})); + connect(k.y, PIDWitInpGai.k) annotation (Line(points={{-58,10},{-40,10},{ + -40,4},{12,4},{12,-12},{18,-12}}, color={0,0,127})); + connect(PIDWitInpGai.Ti, Ti.y) annotation (Line(points={{18,-16},{2,-16}, + {2,-30},{-58,-30}}, color={0,0,127})); + connect(PIDWitInpGai.Td, Td.y) annotation (Line(points={{18,-24},{14,-24}, + {14,-70},{-58,-70}}, color={0,0,127})); + connect(PID.y, sub.u1) + annotation (Line(points={{42,30},{50,30},{50,6},{78,6}}, color={0,0,127})); + connect(PIDWitInpGai.y, sub.u2) annotation (Line(points={{42,-20},{50,-20},{50, + -6},{78,-6}}, color={0,0,127})); + connect(sub.y, abs1.u) + annotation (Line(points={{102,0},{108,0}}, color={0,0,127})); + connect(abs1.y, lesThr.u) + annotation (Line(points={{132,0},{138,0}}, color={0,0,127})); + connect(modTim.y, greThr.u) + annotation (Line(points={{102,50},{138,50}}, color={0,0,127})); + connect(greThr.y, or2.u1) annotation (Line(points={{162,50},{166,50},{166,30}, + {168,30}}, color={255,0,255})); + connect(lesThr.y, or2.u2) annotation (Line(points={{162,0},{166,0},{166,22},{168, + 22}}, color={255,0,255})); + connect(or2.y, assMes.u) + annotation (Line(points={{192,30},{198,30}}, color={255,0,255})); + annotation ( + experiment( + StopTime=1.0, + Tolerance=1e-06), + __Dymola_Commands( + file="modelica://Buildings/Resources/Scripts/Dymola/Controls/OBC/Utilities/Validation/PIDWithInputGains.mos" "Simulate and plot"), + Documentation( + info=" +

+Validation test for the block + +Buildings.Controls.OBC.Utilities.PIDWithInputGains. +

+

+For t ∈ [0, 0.6] both PID controllers have the same gains. +During this time, they generate the same output. +Afterwards, the gains, and hence also their outputs, differ. + +Buildings.Controls.OBC.CDL.Continuous.PIDWithReset. +

+", + revisions=" + +"), + Icon(coordinateSystem(extent={{-100,-100},{100,100}}), + graphics={ + Ellipse( + lineColor={75,138,73}, + fillColor={255,255,255}, + fillPattern=FillPattern.Solid, + extent={{-100,-100},{100,100}}), + Polygon( + lineColor={0,0,255}, + fillColor={75,138,73}, + pattern=LinePattern.None, + fillPattern=FillPattern.Solid, + points={{-36,60},{64,0},{-36,-60},{-36,60}})}), + Diagram(coordinateSystem(extent={{-100,-100},{240,100}}), graphics={ + Rectangle( + extent={{72,80},{228,-20}}, + fillColor={215,215,215}, + fillPattern=FillPattern.Solid, + pattern=LinePattern.None), Text( + extent={{78,78},{168,68}}, + textColor={0,0,0}, + textString="Trigger an assertion if outputs differ more than threshold")})); +end PIDWithInputGains; diff --git a/Buildings/Controls/OBC/Utilities/Validation/package.order b/Buildings/Controls/OBC/Utilities/Validation/package.order index 9755bad2e72..8aae0d17d7f 100644 --- a/Buildings/Controls/OBC/Utilities/Validation/package.order +++ b/Buildings/Controls/OBC/Utilities/Validation/package.order @@ -3,3 +3,4 @@ OptimalStartCoolingPositiveStartTime OptimalStartHeating OptimalStartHeatingCooling OptimalStartNoHeatingNoCooling +PIDWithInputGains diff --git a/Buildings/Controls/OBC/Utilities/package.order b/Buildings/Controls/OBC/Utilities/package.order index 9960eb206c5..b77eda14782 100644 --- a/Buildings/Controls/OBC/Utilities/package.order +++ b/Buildings/Controls/OBC/Utilities/package.order @@ -1,4 +1,5 @@ OptimalStart +PIDWithInputGains SetPoints Validation BaseClasses diff --git a/Buildings/Resources/ReferenceResults/Dymola/Buildings_Controls_OBC_Utilities_Validation_PIDWithInputGains.txt b/Buildings/Resources/ReferenceResults/Dymola/Buildings_Controls_OBC_Utilities_Validation_PIDWithInputGains.txt new file mode 100644 index 00000000000..b722033bb8a --- /dev/null +++ b/Buildings/Resources/ReferenceResults/Dymola/Buildings_Controls_OBC_Utilities_Validation_PIDWithInputGains.txt @@ -0,0 +1,17 @@ +last-generated=2022-05-20 +statistics-simulation= +{ + "linear": " ", + "nonlinear": " ", + "number of continuous time states": "4", + "numerical Jacobians": "0" +} +time=[0e+00, 1e+00] +PID.k=[1e+00, 1e+00] +PIDWitInpGai.k=[1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00, 1.200000047683716e+00] +PID.Ti=[5e-01, 5e-01] +PIDWitInpGai.Ti=[5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 5e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01, 6.99999988079071e-01] +PID.Td=[1.000000014901161e-01, 1.000000014901161e-01] +PIDWitInpGai.Td=[1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 1.000000014901161e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01, 2.000000029802322e-01] +PID.y=[5e-01, 5.09999990686778e-01, 5.19999981373556e-01, 5.299999720603333e-01, 5.400000223517302e-01, 5.500000111758584e-01, 5.600000037252728e-01, 5.699999925494228e-01, 5.799999850988654e-01, 5.899999701977309e-01, 6.000000223517196e-01, 6.100000149011523e-01, 6.200000074505406e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 8.137588987134604e-01, 7.079578216303023e-01, 6.75346736289788e-01, 6.697009218262596e-01, 6.739532864769328e-01, 6.818436353351124e-01, 6.910501775726797e-01, 7.007831194056565e-01, 7.1069317582545e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 8.774170243203979e-01, 7.716368014726137e-01, 7.390448492627223e-01, 7.333810937881043e-01, 7.376160141842284e-01, 7.455072635850436e-01, 7.547188771106752e-01, 7.644033888020471e-01, 7.742881131527446e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 9.219928346223786e-01, 8.162043864878715e-01, 7.836006921384461e-01, 7.779276979318089e-01, 7.82165300588035e-01, 7.900578016496561e-01, 7.992791903056254e-01, 8.089927294509028e-01, 8.18887348207449e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00] +PIDWitInpGai.y=[5e-01, 5.09999990686778e-01, 5.19999981373556e-01, 5.299999720603333e-01, 5.400000223517302e-01, 5.500000111758584e-01, 5.600000037252728e-01, 5.699999925494228e-01, 5.799999850988654e-01, 5.899999701977309e-01, 6.000000223517196e-01, 6.100000149011523e-01, 6.200000074505406e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 8.137588987134604e-01, 7.079578216303023e-01, 6.75346736289788e-01, 6.697009218262596e-01, 6.739532864769328e-01, 6.818436353351124e-01, 6.910501775726797e-01, 7.007831194056565e-01, 7.1069317582545e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 8.774170243203979e-01, 7.716368014726137e-01, 7.390448492627223e-01, 7.333810937881043e-01, 7.376160141842284e-01, 7.455072635850436e-01, 7.547188771106752e-01, 8.631590484799047e-01, 8.715933520988245e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 1e+00, 9.999999413593323e-01, 9.626983615673881e-01, 9.395208008269087e-01, 9.288343820753084e-01, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00, 0e+00] diff --git a/Buildings/Resources/Scripts/Dymola/Controls/OBC/Utilities/Validation/PIDWithInputGains.mos b/Buildings/Resources/Scripts/Dymola/Controls/OBC/Utilities/Validation/PIDWithInputGains.mos new file mode 100644 index 00000000000..b50ef197f03 --- /dev/null +++ b/Buildings/Resources/Scripts/Dymola/Controls/OBC/Utilities/Validation/PIDWithInputGains.mos @@ -0,0 +1,6 @@ +simulateModel("Buildings.Controls.OBC.Utilities.Validation.PIDWithInputGains", method="Cvode", tolerance=1e-06, stopTime=1.0, resultFile="PIDWithInputGains"); +createPlot(id=1, position={15, 15, 800, 600}, y={"PID.k", "PIDWitInpGai.k"}, range={0.0, 1.0, 0.9, 1.3}, grid=true, subPlot=101, colors={{28,108,200}, {238,46,47}}); +createPlot(id=1, position={15, 15, 800, 600}, y={"PID.Ti", "PIDWitInpGai.Ti"}, range={0.0, 1.0, 0.4, 0.8}, grid=true, subPlot=102, colors={{28,108,200}, {238,46,47}}); +createPlot(id=1, position={15, 15, 800, 600}, y={"PID.Td", "PIDWitInpGai.Td"}, range={0.0, 1.0, 0.05, 0.25}, grid=true, subPlot=103, colors={{28,108,200}, {238,46,47}}); +createPlot(id=1, position={15, 15, 800, 600}, y={"PID.y", "PIDWitInpGai.y"}, range={0.0, 1.0, -0.5, 1.5}, grid=true, subPlot=104, colors={{28,108,200}, {238,46,47}}); + diff --git a/Buildings/Resources/Scripts/OpenModelica/compareVars/Buildings.Controls.OBC.Utilities.Validation.PIDWithInputGains.mos b/Buildings/Resources/Scripts/OpenModelica/compareVars/Buildings.Controls.OBC.Utilities.Validation.PIDWithInputGains.mos new file mode 100644 index 00000000000..dda5584095e --- /dev/null +++ b/Buildings/Resources/Scripts/OpenModelica/compareVars/Buildings.Controls.OBC.Utilities.Validation.PIDWithInputGains.mos @@ -0,0 +1,11 @@ +compareVars := + { + "PID.k", + "PIDWitInpGai.k", + "PID.Ti", + "PIDWitInpGai.Ti", + "PID.Td", + "PIDWitInpGai.Td", + "PID.y", + "PIDWitInpGai.y" + }; diff --git a/Buildings/package.mo b/Buildings/package.mo index 3860b4a137d..525b0e28a04 100644 --- a/Buildings/package.mo +++ b/Buildings/package.mo @@ -320,6 +320,15 @@ to existing libraries: This is for #2823.
+ Buildings.Controls.OBC.Utilities + + + Buildings.Controls.OBC.Utilities.PIDWithInputGains + + Added PID controller with anti-windup and control gains exposed as inputs.
+ This is for #2993.
+ + Buildings.Fluid.Storage.Ice @@ -10641,6 +10650,8 @@ The following people have directly contributed to the implementation of the Buil
  • Brandon M. Hencey, Cornell University, USA
  • +
  • Sen Huang, Pacific Northwest National Laboratory, USA +
  • Kathryn Hinkelman, University of Colorado Boulder, Colorado, USA
  • Jianjun Hu, Lawrence Berkeley National Laboratory, USA