Skip to content

Commit

Permalink
-added example in documentation about identification of a step distur…
Browse files Browse the repository at this point in the history
…bance
  • Loading branch information
Steinar Elgsæter authored and Steinar Elgsæter committed Mar 14, 2024
1 parent 426adb5 commit 38aac12
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Dynamic/Identification/ClosedLoopUnitIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ public class ClosedLoopUnitIdentifier
identUnitModel.modelParameters.Fitting.SolverID = "ClosedLoop local (NO global search)";
identUnitModel.modelParameters.Fitting.NFittingTotalDataPoints = dataSet.GetNumDataPoints();
}
// closed-loop simulation, adds U_sim and Y_sim to "dataset"
{
ClosedLoopSim(dataSet, identUnitModel.modelParameters, pidParams, disturbance);
}
return (identUnitModel,disturbance);
}

Expand Down
71 changes: 70 additions & 1 deletion TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public void PidIdent_Ex()
PidModelId();
}

[TestCase, Explicit]
public void ClosedLoop_Ex()
{
ClosedLoopId();
}


#region ex_NONLINEAR_UNIT_MODEL
public void NonlinearUnitModel()
Expand Down Expand Up @@ -140,7 +146,70 @@ void PidModelId()
Array2D<double>.GetColumn(pidDataSet.U_sim,(int)INDEX.FIRST) },
new List<string>{ "y1=y_sim", "y1=y_set","y3=u_pid","y3=u_pid(id)" },timeBase_s);
}
#endregion
#endregion

//adopted from Dynamic_DistStep_EstiamtesOk(5,5) unit test
#region ex_CLOSED_LOOP
void ClosedLoopId()
{
PidParameters pidParameters1 = new PidParameters()
{
Kp = 0.2,
Ti_s = 20
};
UnitParameters trueModelParameters = new UnitParameters
{
TimeConstant_s = 10,
LinearGains = new double[] { 1.5 },
TimeDelay_s = 5,
Bias = 5
};

double stepAmplitude = 5;
int timeBase_s = 1;
int N = 300;
var trueDisturbance = TimeSeriesCreator.Step(100, N, 0, stepAmplitude);

var trueProcessModel = new UnitModel(trueModelParameters, "TrueProcessModel");

// create synthetic dataset
var pidModel1 = new PidModel(pidParameters1, "PID1");

var processSim = new PlantSimulator(
new List<ISimulatableModel> { pidModel1, trueProcessModel });
processSim.ConnectModels(trueProcessModel, pidModel1);
processSim.ConnectModels(pidModel1, trueProcessModel);
var inputData = new TimeSeriesDataSet();

inputData.Add(processSim.AddExternalSignal(pidModel1, SignalType.Setpoint_Yset), TimeSeriesCreator.Constant(50, N));

inputData.Add(processSim.AddExternalSignal(trueProcessModel, SignalType.Disturbance_D), trueDisturbance);
inputData.CreateTimestamps(timeBase_s);
var isOk = processSim.Simulate(inputData, out TimeSeriesDataSet simData);
Assert.IsTrue(isOk);
var pidDataSet = processSim.GetUnitDataSetForPID(inputData.Combine(simData), pidModel1);

var modelId = new ClosedLoopUnitIdentifier();
(var identifiedModel, var estDisturbance) = modelId.Identify(pidDataSet, pidModel1.GetModelParameters());

Console.WriteLine(identifiedModel.ToString());

Shared.EnablePlots();
Plot.FromList(new List<double[]>{ pidDataSet.Y_meas, pidDataSet.Y_setpoint,
pidDataSet.U.GetColumn(0), trueDisturbance },
new List<string> { "y1=y meas", "y1=y set", "y2=u(right)", "y3=true disturbance" },
pidDataSet.GetTimeBase(), "ClosedLoopId_dataset");
Plot.FromList(new List<double[]>{ estDisturbance, trueDisturbance },
new List<string> { "y1=est disturbance", "y1=true disturbance" },
pidDataSet.GetTimeBase(), "ClosedLoopId_disturbances");
Plot.FromList(new List<double[]> { pidDataSet.Y_meas, pidDataSet.Y_sim },
new List<string> { "y1=y_meas", "y1=y_sim" },
pidDataSet.GetTimeBase(), "ClosedLoopId_ysim");
Plot.FromList(new List<double[]> { pidDataSet.U.GetColumn(0), pidDataSet.U_sim.GetColumn(0) },
new List<string> { "y1=u_meas", "y1=u_sim" },
pidDataSet.GetTimeBase(), "ClosedLoopId_usim");
Shared.DisablePlots();
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class FindDisturbanceAndModelSimultanouslyTester_SISO


int timeBase_s = 1;
int N = 300;// TODO:influences the results!
int N = 300;
DateTime t0 = new DateTime(2010,1,1);


Expand Down
2 changes: 1 addition & 1 deletion TimeSeriesAnalysis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<RepositoryUrl>https://github.com/equinor/TimeSeriesAnalysis.git</RepositoryUrl>
<PackageReadmeFile>readme.md</PackageReadmeFile>
<Version>1.3.09</Version>
<Version>1.3.10</Version>
<Company>Equinor</Company>
<Authors>Equinor</Authors>
<IncludeSymbols>true</IncludeSymbols>
Expand Down
Binary file added docs/articles/images/sysid_ex_closedloop1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/articles/images/sysid_ex_closedloop2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/articles/images/sysid_ex_closedloop3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/articles/images/sysid_ex_closedloop4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions docs/articles/sysid_disturbance.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,8 @@ to the algorithm, so that the algorith can infer about the control error ``e``.
> closed-loop be positive process gains.
### First, model-free estimate

In order to initialize the sequential estimation, a model-free estimate of the disturbance
is require initially.
A model-free estimate of the disturbance is required to initialize
subsequent sequential estimation.

For the first iteration, all process dynamics and nonlinearities are neglected,
a linear static model essentially boils down to estimating the process gain.
Expand Down
52 changes: 52 additions & 0 deletions docs/articles/sysid_ex_closedloop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

This example shows how the ``ClosedLoodIdentifier`` can be used to identify a model of a closed-loop system
as well as the disturbance (a time-series) that acted on the system.

The code for this example:
[!code-csharp[Examples](../../TimeSeriesAnalysis.Tests/Examples/SystemIdent.cs?name=ex_CLOSED_LOOP)]

The output of ``idModel.ToString()`` gives details on the model:
```
ABLE to identify
TimeConstant : 12 sec
TimeDelay : 0 sec
ProcessGains(at u0) :
1.57 ± 4
-> Linear Gain :
1.57 ± 4
-> Curvature Gain :
NaN ± NaN
-> u0 : [30]
-> uNorm : [1]
Bias : 50 ± 1.6
```

The dataset created is shown below, a disturbance step creates a first order respone in the output ''y_meas'', as the
PID-controller works to bring ''y_meas'' back to the setpoint ''y_set``. In a real dataset, the actual disturbance would of course be unkown,
but the advantage of creating a synthetic dataet is that the disturbance is known and can be compared to the estimate.

![Ex_Dataset](./images/sysid_ex_closedloop1.png)

The below three plots show the comparison of ``u``, ``y`` and disturbance ``d`` for the given syntethic dataset with the result of
`ClosedLoopIdentifer''.

![Ex_u](./images/sysid_ex_closedloop2.png)

![Ex_y](./images/sysid_ex_closedloop3.png)

![Ex_d](./images/sysid_ex_closedloop4.png)


The measured and simulated ``y`` generally always tend to match due to how the disturbance signal is defined ``y_meas = y_model +d``,
so in essence the disturbance signal is selected so that these two signals match.

The measured and simulated ``u`` on the other hand do not neccesarily need to match, but ``ClosedLoopIdentifier`` has by a combination
of trial and error, and knowledge of the Pid-paramters chosen the UnitModel so that measured and simulated ``u`` match as closely as possible.

While the actual gain was ``1.5`` and time-constant was ``10s``, the estimated result were ``1.57`` and ``12s``, thus the estimate of
the distubance signal ``d''has a small transient overshoot and a slightly too high amplitude as well, but the estimate is still quite good, given the
complex nature of estimating processes in closed-loop.

This example is significant, because once the disturbance signal can be estimated, it becomes possible to
1. analyze correlation among PID-control loops to quantify the coupling, and
2. to re-simulate "what-if" simulations, of how the disturbance could have been rejected by other PID-tunings or even other control structures
4 changes: 3 additions & 1 deletion docs/articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@
- name: Nonlinear unit model identification
href: sysid_ex_nonlin.md
- name: PID model identification
href: sysid_ex_pid.md
href: sysid_ex_pid.md
- name: Closed-loop identification (disturbances)
href: sysid_ex_closedloop.md



Expand Down

0 comments on commit 38aac12

Please sign in to comment.