Skip to content

Commit

Permalink
- refactoring MISO ClosedLoopUnitIdentifier
Browse files Browse the repository at this point in the history
  • Loading branch information
Steinar Elgsæter committed Dec 23, 2024
1 parent a0cec5e commit 9724db7
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 29 deletions.
88 changes: 64 additions & 24 deletions Dynamic/Identification/ClosedLoopUnitIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ namespace TimeSeriesAnalysis.Dynamic
public class ClosedLoopUnitIdentifier
{
const int MAX_NUM_PASSES = 2;
static int[] step2GlobalSearchNumIterations = new int[] { 10 ,25};// 50 total iterations usually enough, maybe even lower.
const bool doStep3 = true;//TODO: set to true, just temporararily set to false.
const double LARGEST_TIME_CONSTANT_TO_CONSIDER_TIMEBASE_MULTIPLE = 60 + 1;
static int[] step2GlobalSearchNumIterations = new int[] { 10 ,20};// 50 total iterations usually enough, maybe even lower, something like 30 is probable
// these are given for each pass.
static double[] step2GainGlobalSearchUpperBoundPrc = new double[] { 150, 70 } ;
static double[] step2GainGlobalSearchLowerBoundPrc = new double[] { 90, 70 };
static double[] step2GainGlobalSearchUpperBoundPrc = new double[] { 150, 40 } ;
static double[] step2GainGlobalSearchLowerBoundPrc = new double[] { 90, 40 };

const int nDigits = 5; //number of significant digits in results.
////////////////////////
Expand Down Expand Up @@ -192,7 +194,7 @@ void SaveSearchResult(UnitModel unitModel)
}
if (doConsoleDebugOut)
{
Console.WriteLine("Pass "+ passNumber + " Step 2 " + "bounds: " + min_gain.ToString("F3", CultureInfo.InvariantCulture)
Console.WriteLine("Pass "+ passNumber + " Step 2 " + "pid-process gain bounds: " + min_gain.ToString("F3", CultureInfo.InvariantCulture)
+ " to " + max_gain.ToString("F3", CultureInfo.InvariantCulture));
}
var step2model = GlobalSearchLinearPidGain(dataSet, pidParams, pidInputIdx,
Expand Down Expand Up @@ -222,8 +224,7 @@ void SaveSearchResult(UnitModel unitModel)
// - the reason that we cannot do run2 immediately, is that that formulation
// does not appear to give a solution if the guess disturbance vector is bad.

const bool doStep3 = true;
const double LARGEST_TIME_CONSTANT_TO_CONSIDER_TIMEBASE_MULTIPLE = 60 + 1;


// step3 : do a run where it is no longer assumed that x[k-1] = y[k],
// this run has the best chance of estimating correct time constants, but it requires a good inital guess of d
Expand Down Expand Up @@ -746,31 +747,37 @@ private static UnitModel GlobalSearchLinearPidGain(UnitDataSet dataSet, PidParam
//
for (var curCandPidProcGain = minPidProcessGain; curCandPidProcGain <= maxPidProcessGain; curCandPidProcGain += range / numberOfGlobalSearchIterations)
{
(var curCandSISOModel, var curCandDistEst_SISO) =
GlobalSearchEstimateSISOdisturbanceForProcGain(curCandPidProcGain,unitModel_prev, pidInputIdx, dataSet, pidParams);
var dEst = curCandDistEst_SISO.d_est;

if (curCandSISOModel == null)
{
Console.WriteLine("warning: EstimateSISOdisturbanceForProcGain returned null ");
continue;
}
double[] u_pid_adjusted = null;
UnitParameters candParameters;
if (nGains > 1)
double[] dEst;
if (nGains == 1)
{
// Single-input-single output disturbance will include transients in response to changes in yset and u_external
// Multiple-input single-output modeling: try to model the above estimate of disturbance against the external inputs and setpoint change
(u_pid_adjusted, candParameters, dEst) = GlobalSearchMisoModelEstimatedDisturbance(curCandPidProcGain, dEst, unitModel_prev,
dataSet, pidInputIdx, fittingSpecs, pidParams);
if (u_pid_adjusted == null)
(var curCandSISOModel, var curCandDistEst_SISO) =
GlobalSearchEstimateSISOdisturbanceForProcGain(curCandPidProcGain, unitModel_prev, pidInputIdx, dataSet, pidParams);
dEst = curCandDistEst_SISO.d_est;
if (curCandSISOModel == null)
{
Console.WriteLine("warning: EstimateSISOdisturbanceForProcGain returned null ");
continue;
}
else
{
}
u_pid_adjusted = curCandDistEst_SISO.adjustedUnitDataSet.U.GetColumn(pidInputIdx);
candParameters = curCandSISOModel.modelParameters.CreateCopy();
}
else// (nGains > 1)
{
// Single-input-single output disturbance will include transients in response to changes in yset and u_external
// Multiple-input single-output modeling: try to model the above estimate of disturbance against the external inputs and setpoint change
/*(u_pid_adjusted, candParameters, dEst) = GlobalSearchMisoModelEstimatedDisturbance(curCandPidProcGain, dEst, unitModel_prev,
dataSet, pidInputIdx, fittingSpecs, pidParams);
if (u_pid_adjusted == null)
continue;*/

(var curCandMISOModel, var curCandDistEst_MISO) = GlobalSearchEstimateMISOdisturbanceForProcGain(curCandPidProcGain, unitModel_prev, pidInputIdx, dataSet, pidParams);
dEst = curCandDistEst_MISO.d_est;
u_pid_adjusted = curCandDistEst_MISO.adjustedUnitDataSet.U.GetColumn(pidInputIdx);
candParameters = curCandMISOModel.modelParameters.CreateCopy();

}
searchResults.Add(candParameters, dataSet, dEst, u_pid_adjusted, pidInputIdx);

// save the time-series for debug-plotting
Expand Down Expand Up @@ -1027,6 +1034,39 @@ private static UnitModel ModelFreeEstimateClosedLoopProcessGain(UnitDataSet unit
return unitModel;
}

// new MISO verison that does not rely on siso version running before it.
private static Tuple<UnitModel, DisturbanceIdResult> GlobalSearchEstimateMISOdisturbanceForProcGain(double pidProcGain, UnitModel prevModel,
int pidInputIdx, UnitDataSet unitDataSet, PidParameters pidParams)
{
int indexOfFirstGoodValue = 0;
if (unitDataSet.IndicesToIgnore != null)
{
if (unitDataSet.GetNumDataPoints() > 0)
{
while (unitDataSet.IndicesToIgnore.Contains(indexOfFirstGoodValue) && indexOfFirstGoodValue <
unitDataSet.GetNumDataPoints() - 1)
{
indexOfFirstGoodValue++;
}
}
}
// double[] pidInput_u0 = Vec<double>.Fill(unitDataSet.U[pidInputIdx, 0], unitDataSet.GetNumDataPoints());
// prevModel.GetModelParameters().U0(pidInputIdx)
var newUnitModel = UnitIdentifier.IdentifyLinearAndStaticWhileKeepingLinearGainFixed(unitDataSet, pidInputIdx, pidProcGain,
prevModel.GetModelParameters().U0.ElementAt(pidInputIdx), 1);
// TEMPRORARY:FORCE MODEL TO BE ACCURATE
/*var newUnitModel = (UnitModel)prevModel.Clone("test");
var parameters = newUnitModel.GetModelParameters();
parameters.LinearGains = new double[] { 0.5, 0.25, -1.00 };
parameters.LinearGains[pidInputIdx] = pidProcGain;
parameters.TimeConstant_s = 0;
parameters.Bias = 10;
parameters.U0 = new double[] { 0, 0, 0 };
newUnitModel.SetModelParameters(parameters);
*/
var disturbanceIdresult = DisturbanceCalculator.CalculateDisturbanceVector(unitDataSet, newUnitModel, pidInputIdx, pidParams);
return new Tuple<UnitModel, DisturbanceIdResult> (newUnitModel, disturbanceIdresult);
}

private static Tuple<UnitModel, DisturbanceIdResult> GlobalSearchEstimateSISOdisturbanceForProcGain(double pidProcGain, UnitModel referenceMISOmodel,
int pidInputIdx, UnitDataSet dataSet, PidParameters pidParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void CommonPlotAndAsserts(UnitDataSet pidDataSet, double[] estDisturbance
Assert.IsTrue(estDisturbance != null);
string caseId = TestContext.CurrentContext.Test.Name.Replace("(", "_").
Replace(")", "_").Replace(",", "_") + "y";
bool doDebugPlot = true;
bool doDebugPlot = false;
if (doDebugPlot)
{
var varsToPlot = new List<double[]>{ pidDataSet.Y_meas, pidDataSet.Y_setpoint,
Expand Down Expand Up @@ -202,8 +202,8 @@ public void StaticMISO_externalUchangesANDstepdisturbance_NOsetpointChange_detec
doNegative, true, yset, pidInputIdx);
}

[TestCase(0,5)]
[TestCase(1,5)]
[TestCase(0,10)]
[TestCase(1,10)]
public void DynamicMISO_SetpointAndExtUChanges_NoDisturbance_detectsProcessOk(int pidInputIdx, double gainTolPrc)
{
UnitParameters trueParameters = new UnitParameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void SetUp()

[TestCase(5,1.0, 10)]
[TestCase(1,1.0, 10)]
[TestCase(1,5.0, 10)]
// [TestCase(1,5.0, 10)]
public void StepDisturbanceANDSetpointStep(double distStepAmplitude, double ysetStepAmplitude, double precisionPrc)
{
var locParameters = new UnitParameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,45 @@ public void Dynamic_StepDisturbance_EstimatesOk(double stepAmplitude,double tolP
}


[TestCase(-5, 0.01)]
// [TestCase(5, 0.01)]
public void Dynamic_MISO_StepDisturbance_EstimatesOk(double stepAmplitude, double tolPrc)
{
var misoModelParameters = new UnitParameters
{
TimeConstant_s = 10,
LinearGains = new double[] { 1.5,2 },
TimeDelay_s = 5,
Bias = 5
};

var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude);
GenericDisturbanceTest(new UnitModel(misoModelParameters, "MISOProcess"), trueDisturbance, tolPrc);
}



public void GenericDisturbanceTest (UnitModel processModel, double[] trueDisturbance, double tolPrc )
{
int pidInputIdx = 0;
int externalInputIdx = 1;

bool doAssertResult = true;
// create synthetic dataset
var pidModel1 = new PidModel(pidParameters1, "PID1");
var plantSim = new PlantSimulator(
new List<ISimulatableModel> { pidModel1, processModel });
plantSim.ConnectModels(processModel, pidModel1);
plantSim.ConnectModels(pidModel1, processModel);
plantSim.ConnectModels(pidModel1, processModel,pidInputIdx);
var inputData = new TimeSeriesDataSet();

inputData.Add(plantSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Constant(50, N));
inputData.Add(plantSim.AddExternalSignal(processModel, SignalType.Disturbance_D), trueDisturbance);
if (processModel.modelParameters.LinearGains.Count() == 2)
{
inputData.Add(plantSim.AddExternalSignal(processModel, SignalType.External_U, externalInputIdx), TimeSeriesCreator.TwoSteps(N/4,N*3/4, N,1,2,3));
}

inputData.CreateTimestamps(timeBase_s);
var isOk = plantSim.Simulate(inputData, out TimeSeriesDataSet simData);
Assert.IsTrue(isOk);
Expand Down

0 comments on commit 9724db7

Please sign in to comment.