diff --git a/Dynamic/Identification/ClosedLoopUnitIdentifier.cs b/Dynamic/Identification/ClosedLoopUnitIdentifier.cs
index 2ab0760..3304252 100644
--- a/Dynamic/Identification/ClosedLoopUnitIdentifier.cs
+++ b/Dynamic/Identification/ClosedLoopUnitIdentifier.cs
@@ -37,7 +37,7 @@ public class ClosedLoopUnitIdentifier
// NB!! These three are somewhat "magic numbers", that need to be changed only after
// testing over a wide array of cases
const int firstPassNumIterations = 30;//TODO: change back to 50
- const int secondPassNumIterations = 20;
+ const int secondPassNumIterations = 0;
const double initalGuessFactor_higherbound = 2.5;// 2 is a bit low, should be a bit higher
const int nDigits = 5; //number of significant digits in results.
////////////////////////
@@ -54,6 +54,8 @@ public class ClosedLoopUnitIdentifier
/// The unit model, with the name of the newly created disturbance added to the additiveInputSignals
public static (UnitModel, double[]) Identify(UnitDataSet dataSet, PidParameters pidParams = null, int pidInputIdx = 0)
{
+ bool doConsoleDebugOut = true;
+
bool wasGainGlobalSearchDone = false;
bool doTimeDelayEstOnRun1 = false;
if (dataSet.Y_setpoint == null || dataSet.Y_meas == null || dataSet.U == null)
@@ -125,6 +127,9 @@ public static (UnitModel, double[]) Identify(UnitDataSet dataSet, PidParameters
var distIdResult1 = DisturbanceIdentifier.EstimateDisturbance
(dataSetRun1, null, pidInputIdx, pidParams);
+ if (doConsoleDebugOut)
+ Console.WriteLine("Run1,step1: " + distIdResult1.estPidProcessGain.ToString("F3", CultureInfo.InvariantCulture));
+
dataSetRun1.D = distIdResult1.d_est;
var unitModel_run1 = UnitIdentifier.IdentifyLinearAndStatic(ref dataSetRun1, fittingSpecs, doTimeDelayEstOnRun1);
unitModel_run1.modelParameters.LinearGainUnc = null;
@@ -132,8 +137,11 @@ public static (UnitModel, double[]) Identify(UnitDataSet dataSet, PidParameters
idUnitModelsList.Add(unitModel_run1);
isOK = ClosedLoopSim(dataSetRun1, unitModel_run1.GetModelParameters(), pidParams, distIdResult1.d_est, "run1");
+ if (doConsoleDebugOut)
+ Console.WriteLine("Run1,ident: " + unitModel_run1.GetModelParameters().LinearGains.First().ToString("F3", CultureInfo.InvariantCulture));
+
// run1, "step2" : "global search" for linear pid-gainsgains
- if(unitModel_run1.modelParameters.GetProcessGains()!= null)
+ if (unitModel_run1.modelParameters.GetProcessGains()!= null)
{
double pidProcessInputInitalGainEstimate = unitModel_run1.modelParameters.GetProcessGains()[pidInputIdx];
@@ -150,27 +158,39 @@ public static (UnitModel, double[]) Identify(UnitDataSet dataSet, PidParameters
var max_gain = Math.Abs(pidProcessInputInitalGainEstimate * initalGuessFactor_higherbound);
var min_gain = - max_gain;
- // min_gain = 0; // when debugging, it might be advantageous to set min_gain equal to the known true value
+ if (doConsoleDebugOut)
+ {
+ Console.WriteLine("Run1,GS Higher bound: "+ max_gain.ToString("F3", CultureInfo.InvariantCulture) );
+ }
+
+ // min_gain = 0; // when debugging, it might be advantageous to set min_gain equal to the known true value
// first pass(wider grid with larger grid size)
- var retPass1 = GlobalSearchLinearPidGain(dataSet, pidParams, pidInputIdx,
+ var retGlobalSearch1 = GlobalSearchLinearPidGain(dataSet, pidParams, pidInputIdx,
unitModel_run1, pidProcessInputInitalGainEstimate,
min_gain, max_gain, fittingSpecs,firstPassNumIterations);
- var bestUnitModel = retPass1.Item1;
+ var bestUnitModel = retGlobalSearch1.Item1;
+
+ if (doConsoleDebugOut && retGlobalSearch1.Item1 != null)
+ Console.WriteLine("Run1,GS1: " + retGlobalSearch1.Item1.GetModelParameters().LinearGains.First().ToString("F3", CultureInfo.InvariantCulture));
+ else
+ Console.WriteLine("Run1,GS1: FAILED");
if (bestUnitModel != null)
{
// second pass(finer grid around best result of first pass)
- if (retPass1.Item1.modelParameters.Fitting.WasAbleToIdentify && secondPassNumIterations > 0)
+ if (retGlobalSearch1.Item1.modelParameters.Fitting.WasAbleToIdentify && secondPassNumIterations > 0)
{
const int WIDTH_OF_SEARCH_PASS2 = 3;
- var gainPass1 = retPass1.Item1.modelParameters.LinearGains[pidInputIdx];
- var retPass2 = GlobalSearchLinearPidGain(dataSet, pidParams, pidInputIdx,
- retPass1.Item1, gainPass1, gainPass1 - retPass1.Item2* WIDTH_OF_SEARCH_PASS2, gainPass1 + retPass1.Item2* WIDTH_OF_SEARCH_PASS2,
+ var gainPass1 = retGlobalSearch1.Item1.modelParameters.LinearGains[pidInputIdx];
+ var retGlobalSearch2 = GlobalSearchLinearPidGain(dataSet, pidParams, pidInputIdx,
+ retGlobalSearch1.Item1, gainPass1, gainPass1 - retGlobalSearch1.Item2* WIDTH_OF_SEARCH_PASS2, gainPass1 + retGlobalSearch1.Item2* WIDTH_OF_SEARCH_PASS2,
fittingSpecs,secondPassNumIterations);
- bestUnitModel = retPass2.Item1;
+ bestUnitModel = retGlobalSearch2.Item1;
wasGainGlobalSearchDone = true;
+ if (doConsoleDebugOut)
+ Console.WriteLine("Run1,GS2: " + retGlobalSearch2.Item1.GetModelParameters().LinearGains.First().ToString("F3", CultureInfo.InvariantCulture));
}
}
// add the "best" model to be used in the next model run
@@ -324,6 +344,8 @@ public static (UnitModel, double[]) Identify(UnitDataSet dataSet, PidParameters
var distIdResult_step4 = DisturbanceIdentifier.EstimateDisturbance
(dataSetRun2, step2Model, pidInputIdx, pidParams);
idDisturbancesList.Add(distIdResult_step4);
+ if (doConsoleDebugOut)
+ Console.WriteLine("Run2 " + step2Model.GetModelParameters().LinearGains.First().ToString("F3", CultureInfo.InvariantCulture));
}
}
diff --git a/Dynamic/Identification/DisturbanceIdentifier.cs b/Dynamic/Identification/DisturbanceIdentifier.cs
index 338f522..3eb7330 100644
--- a/Dynamic/Identification/DisturbanceIdentifier.cs
+++ b/Dynamic/Identification/DisturbanceIdentifier.cs
@@ -256,7 +256,6 @@ private static UnitModel EstimateClosedLoopProcessGain(UnitDataSet unitDataSet,
var unitModel = new UnitModel();
var vec = new Vec(unitDataSet.BadDataID);
- //var result = new DisturbanceIdResult(unitDataSet);
if (unitDataSet.Y_setpoint == null || unitDataSet.Y_meas == null || unitDataSet.U == null)
{
return null;
@@ -277,6 +276,7 @@ private static UnitModel EstimateClosedLoopProcessGain(UnitDataSet unitDataSet,
// but works better than candiate 2 when disturbance is a step
double FilterTc_s = 0;
+
// initalizaing(rough estimate):
{
LowPass lowPass = new LowPass(unitDataSet.GetTimeBase());
@@ -316,6 +316,12 @@ private static UnitModel EstimateClosedLoopProcessGain(UnitDataSet unitDataSet,
double maxU = vec.Max(vec.Abs(uFiltered), unitDataSet.IndicesToIgnore); // sensitive to output noise/controller overshoot
double minU = vec.Min(vec.Abs(uFiltered), unitDataSet.IndicesToIgnore); // sensitive to output noise/controller overshoot
estPidInputProcessGain = pidInput_processGainSign * maxDE / (maxU - minU);
+
+ // double avgDE = vec.Mean(vec.Abs(eFiltered), unitDataSet.IndicesToIgnore).Value;
+ // double avgU = vec.Mean(vec.Abs(pidInput_deltaU), unitDataSet.IndicesToIgnore).Value;
+ // double estPidInputProcessGainUB = avgDE / avgU;
+ // Console.WriteLine("process gain upper og lower boundbound:"+estPidInputProcessGainUB+ " uncertainty:" + Math.Abs(estPidInputProcessGain- estPidInputProcessGainUB));
+
}
int indexOfFirstGoodValue = 0;
@@ -337,6 +343,8 @@ private static UnitModel EstimateClosedLoopProcessGain(UnitDataSet unitDataSet,
var unitParamters = new UnitParameters();
unitParamters.LinearGains = new double[nGains];
unitParamters.LinearGains[pidInputIdx] = estPidInputProcessGain;
+ // unitParamters.LinearGainUnc[pidInputIdx] = estPidInputProcessGain-est;
+
unitParamters.U0 = new double[nGains];
unitParamters.U0[pidInputIdx] = pidInput_u0[pidInputIdx];
unitParamters.UNorm = Vec.Fill(1, nGains);
@@ -432,6 +440,7 @@ public static DisturbanceIdResult EstimateDisturbance(UnitDataSet unitDataSet,
result.d_est = d_est;
result.d_LF = d_LF;
result.d_HF = d_HF;
+ result.estPidProcessGain = unitModel.GetModelParameters().LinearGains.ElementAt(pidInputIdx);
result.adjustedUnitDataSet = unitDataSet_adjusted;
// END STEP 2
diff --git a/TimeSeriesAnalysis/Vec.cs b/TimeSeriesAnalysis/Vec.cs
index afc3f6f..2ea5a3f 100644
--- a/TimeSeriesAnalysis/Vec.cs
+++ b/TimeSeriesAnalysis/Vec.cs
@@ -721,6 +721,29 @@ public double[] Multiply(double[] array1, double[] array2)
return retVal;
}
+ ///
+ /// Returns the mean value of array1. while ignoring given indices
+ ///
+ public double? Mean(double[] array1, List indToIgnore = null)
+ {
+ if (array1 == null)
+ return null;
+ double retVal = 0;
+ double N = 0;
+ var indices = Index.InverseIndices(array1.Length, indToIgnore);
+ foreach (var ind in indices)
+ {
+ if (!IsNaN(array1[ind]))
+ {
+ N += 1;
+ retVal = retVal * (N - 1) / N + array1[ind] * 1 / N;
+ }
+ }
+ return retVal;
+ }
+
+
+
diff --git a/docs/articles/sysid_disturbance.md b/docs/articles/sysid_disturbance.md
index 8ebe0d8..c3026c3 100644
--- a/docs/articles/sysid_disturbance.md
+++ b/docs/articles/sysid_disturbance.md
@@ -105,7 +105,7 @@ to the algorithm, so that the algorith can infer about the control error ``e``.
> is also true, what appear to be negative process gains at first sight may in
> closed-loop be positive process gains.
-### First, model-free estimate
+### First, model-free estimate of the process gain
A model-free estimate of the disturbance is required to initialize
subsequent sequential estimation.
@@ -114,18 +114,37 @@ a linear static model essentially boils down to estimating the process gain.
![init](./images/sysid_disturbance_init.png)
-This first estimate of the process gain ``G`` in a linear model ``y = G x u``
+This first estimate of the process gain ``G_0`` in a linear model ``y = G_0 x u``
is found by the approximation
```
-G = max(e)/(max(u)-min(u))
+G_0 = max(e)/(max(u)-min(u))
```
+
+The PID-controller usually has an integral effect which causes it to be somewhat delayed in
+responding to a disturbance, and the process also can have time delay and/or a time constant
+that means that the deviation ``e`` is brought back to zero over a certain period of time.
+The idea of creating an inital estimate withh ``min`` and ``max`` values is that it circumvents the lack
+of knowledge of the dynamics at this early stage of estimaton.
+
+It has been observed in unit tests that this estiamte in some cases is spot on the actual gain, such as when
+the disturbance is a perfect step.
+
+Remember, it is not needed for the inital heuritic guess of the process gain to be completely accurate, because
+subsequent steps in the search algorithm can try to improve on this estiamte.
+
+Once an inital estiamte of the process gain has been made, it is possible to estimate the disturbance vector, which is used to initate the below steps.
+
### Pid gain global search
-TODO
+After the inital estiamte of the
+
+
+### Estimating upper-and lower boundds on the process gain
+
### Time constant search algorithm
-TODO
+
### Separating the state of the system from the output