Table of Contents

Feedforward control

pid-feedforward.

Simulating pid-control against an external disturbance without feed-forward

The first part of this example shows the performance of the process and PID-controller when a step change occurs in the disturbance signal. Note that while the PID-controller is able to stabilize the process, it does create significant offset in y, it falls to about y=46.5 at its lowest.

var processParameters = new UnitParameters
{
    TimeConstant_s = 30,
    LinearGains = new double[] { 1.1 },
    U0 = new double[] { 50 },
    TimeDelay_s = 0,
    Bias = 50
};
var disturbanceParameters = new UnitParameters
{
    TimeConstant_s = 30,
    LinearGains = new double[] { 1 },
    U0 = new double[] { 0 },
    TimeDelay_s = 5,
    Bias = 0
};
var pidParameters = new PidParameters()
{
    Kp = 0.3,
    Ti_s = 20
};

var processModel
    = new UnitModel(processParameters,  "Process1");
var disturbanceModel
    = new UnitModel(disturbanceParameters, "Disturbance1");
var pidModel = new PidModel(pidParameters, "PID");

var simNoFeedF = new PlantSimulator(
    new List<ISimulatableModel> { processModel, disturbanceModel, pidModel });

simNoFeedF.ConnectModels(pidModel, processModel);
simNoFeedF.ConnectModels(processModel, pidModel);
simNoFeedF.ConnectModelToOutput(disturbanceModel, processModel);

var inputData = new TimeSeriesDataSet();

inputData.Add(simNoFeedF.AddExternalSignal(pidModel, SignalType.Setpoint_Yset),
    TimeSeriesCreator.Constant(60, 600));
inputData.Add(simNoFeedF.AddExternalSignal(disturbanceModel, SignalType.External_U),
    TimeSeriesCreator.Step(100, 600, 25, 0));
inputData.CreateTimestamps(timeBase_s);
var isOk = simNoFeedF.Simulate(inputData,out var dataNoFeedF);

Plot.FromList(new List<double[]>
    {
    dataNoFeedF.GetValues(processModel.GetID(),SignalType.Output_Y),
    inputData.GetValues(pidModel.GetID(),SignalType.Setpoint_Yset),
    dataNoFeedF.GetValues(disturbanceModel.GetID(),SignalType.Output_Y),
    dataNoFeedF.GetValues(pidModel.GetID(),SignalType.PID_U),
    inputData.GetValues(disturbanceModel.GetID(),SignalType.External_U)
    },
    new List<string> { "y1=y_meas", "y1=y_setpoint", "y2=y_dist[right]", "y3=u_pid", "y3=u_dist" }, timeBase_s, "FeedForwardEx1");

Example 5 result

Adding feed-forward to the simulation

In the second part of this example a feed-forward term is added to the PID-controller. The input to the disturbance process is assumed to be measured and is used as the feed-forward variable. Since this variable moves slightly in advance of the disturbance as seen on y due to a time delay and time constant, a feed-forward from this variable should improve performance. As seen, the offset between y and y_set is less during the same disturbance step as in the previous part of this example, with y falling to just y=50.4 at its lowest, but at the expense of a slight overshoot in y.


var processParameters = new UnitParameters
{
    TimeConstant_s = 30,
    LinearGains = new double[] { 1.1 },
    U0 = new double[] { 50 },
    TimeDelay_s = 0,
    Bias = 50
};
var disturbanceParameters = new UnitParameters
{
    TimeConstant_s = 30,
    LinearGains = new double[] { 1 },
    U0 = new double[] { 0 },
    TimeDelay_s = 5,
    Bias = 0
};
var pidParameters = new PidParameters()
{
    Kp = 0.3,
    Ti_s = 20,
    FeedForward = new PidFeedForward()
    {
        isFFActive = true,
        FF_Gain = -0.7,
        FFHP_filter_order = 1,
        FFLP_filter_order = 1,
        FF_HP_Tc_s = 60,
        FF_LP_Tc_s = 0//120
    }
};

var processModel
    = new UnitModel(processParameters, "Process1");
var disturbanceModel
    = new UnitModel(disturbanceParameters, "Disturbance1");
var pidModel = new PidModel(pidParameters, "PID");

var simNoFeedF = new PlantSimulator(
    new List<ISimulatableModel> { processModel, disturbanceModel, pidModel });

simNoFeedF.ConnectModels(pidModel, processModel);
simNoFeedF.ConnectModels(processModel, pidModel);
simNoFeedF.ConnectModelToOutput(disturbanceModel, processModel);

var inputData = new TimeSeriesDataSet();
int N = 600;
inputData.Add(simNoFeedF.AddExternalSignal(pidModel, SignalType.Setpoint_Yset),
    TimeSeriesCreator.Constant(60, N));
string dSignalID = simNoFeedF.AddExternalSignal(disturbanceModel, SignalType.External_U);
inputData.Add(dSignalID, TimeSeriesCreator.Step(300, N, 25, 0));
inputData.CreateTimestamps(timeBase_s, N);

simNoFeedF.ConnectSignalToInput(dSignalID, pidModel, (int)PidModelInputsIdx.FeedForward);

var isOk = simNoFeedF.Simulate(inputData,out var dataNoFeedF);

Plot.FromList(new List<double[]>
    { 
        dataNoFeedF.GetValues(processModel.GetID(),SignalType.Output_Y),
        inputData.GetValues(pidModel.GetID(),SignalType.Setpoint_Yset),
        dataNoFeedF.GetValues(disturbanceModel.GetID(),SignalType.Output_Y),
        dataNoFeedF.GetValues(pidModel.GetID(),SignalType.PID_U),
        inputData.GetValues(disturbanceModel.GetID(),SignalType.External_U)
    },
    new List<string> { "y1=y_run1", "y1=y_setpoint", "y2=y_dist[right]", "y3=u_pid", "y3=u_dist" }, 
    timeBase_s, "FeedForwardEx2");

Example 5 result