[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8. Creating a Controller

Controllers compute Actuator orders. A Controller has Sensors and Observers for inputs, maintains its state, and produces orders for one or more Actuators.

controller_inout


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.1 Controller Code Structure

Like Observers, the Scilab code for Controllers is structured in four steps, two of them being initialization steps and two which are run during each control loop. These steps are:

The Setup step is where the Controller is described: the needed inputs are declared, and the produced commands, parameters, and state variables are defined.

The Init step is where the initial values are set.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

8.2 Controller Example

Note that other examples can be found in the ‘controllers’ directory of your Odin install.

// date: 2008-JAN-22

function hrs = secToDays(tSec)
   hrs = tSec/(24.*60.*60.);
endfunction

//// Computes the system state's derivative. ////

function ydot = ctr(t,y)
,
  qIn = Inputs("SensorB-qIn")("value");
  sIn = Inputs("SensorB-sIn")("value");
  qm = Inputs("SensorA-qm")("value");

  
  vLiq = Parameters("vLiq")("value");
  k1   = Parameters("k1")("value");
  k6   = Parameters("k6")("value");

  d    = qIn/vLiq;
  
  ydot = -d*y+d*sIn-k1/k6*qm;

,
endfunction

function sv = doContinuousIntegration(Inputs, Parameters, StateVars),
   t     = secToDays(tCurr);
   StateVarValue = [StateVars("sc")("value")];
   sv     = ode(SolverOption,StateVarValue,tLast,t,ctr);
endfunction

function sv = doDiscreteIntegration(Inputs, Parameters, StateVars),
  //fill in this function
  sv = 0.;
endfunction

//// Control loop entry points ////

select CtrJob,

  // Set up controller data structures
  case 'Setup' then
    exec('./controllers/factories.sci');
  
    // Controller global properties
    CtrName = 'ctr_dcoqm';
    CtrDescription = 'Dynamic controller on qIn';
    
    SolverOption = 'adams';
    continuousScheme = %t; //set to %f if integration step is to be discrete

    InputsRequiredUid = ['SensorA-qm'; 'SensorB-qIn'; 'SensorB-sIn'];
    Inputs = tlist([["Inputs"] ; InputsRequiredUid]);

    //Note that parameters are constant
    ParametersUid = ['vLiq';  'k1'; 'k6' ; 'lambda'];
    Parameters = tlist([["Parameters"] ; ParametersUid]);

    //State variables (or estimated variables)
    StateVarsUid = ['sc'  ];
    StateVars = tlist([["StateVars"] ; StateVarsUid]);

    SetPointsUid = ['se'];
    SetPoints = tlist([["SetPoints"] ; SetPointsUid]);

    CommandsSuppliedUid = ['ActuatorA-qIn';'ActuatorA-sIn'];
    Commands = tlist([["Commands"] ; CommandsSuppliedUid]);

    //Initialize individual inputs
    IName                                = "SensorA-qm";
    Inputs(IName)                        = makeSensor();
    Inputs(IName)("physicalDeviceLabel") = 'SensorA';
    Inputs(IName)("physicalDeviceUid")   = 'SensorA';
    Inputs(IName)("deviceUid")           = 'SensorA-qm';
    Inputs(IName)("dataLabel")           = 'qm';
    Inputs(IName)("dataDescription")     = 'Methane flow rate';
    Inputs(IName)("unit")                = 'Day^{-1}';
    Inputs(IName)("domain")              = [-1000; 1000];
    Inputs(IName)("validityPeriod")      = -1.;
    Inputs(IName)("value")               = 0.;
    Inputs(IName)("timestamp")           = 0.;

    IName                                = "SensorB-qIn";
    Inputs(IName)                        = makeSensor();
    Inputs(IName)("physicalDeviceLabel") = 'SensorB';
    Inputs(IName)("physicalDeviceUid")   = 'SensorB';
    Inputs(IName)("deviceUid")           = 'SensorB-qIn';
    Inputs(IName)("dataLabel")           = 'qIn';
    Inputs(IName)("dataDescription")     = 'Inlet Flow';
    Inputs(IName)("unit")                = 'L/Day';
    Inputs(IName)("domain")              = [-1000; 1000];
    Inputs(IName)("validityPeriod")      = -1.;
    Inputs(IName)("value")               = 0.;
    Inputs(IName)("timestamp")           = 0.;

    IName                                = "SensorB-sIn";
    Inputs(IName)                        = makeSensor();
    Inputs(IName)("physicalDeviceLabel") = 'SensorB';
    Inputs(IName)("physicalDeviceUid")   = 'SensorB';
    Inputs(IName)("deviceUid")           = 'SensorB-sIn';
    Inputs(IName)("dataLabel")           = 'sIn';
    Inputs(IName)("dataDescription")     = 'Inlet substrate concentration';
    Inputs(IName)("unit")                = 'g/L';
    Inputs(IName)("domain")              = [-1000; 1000];
    Inputs(IName)("validityPeriod")      = -1.;
    Inputs(IName)("value")               = 0.;
    Inputs(IName)("timestamp")           = 0.;

    //Initialize individual parameters
    PName                            = "vLiq";
    Parameters(PName)                = makeParameter();
    Parameters(PName)("label")       = 'vLiq';
    Parameters(PName)("uid")         = 'vLiq';
    Parameters(PName)("description") = 'Bioreactor volume';
    Parameters(PName)("value")       = 900.;
    Parameters(PName)("unit")        = 'L';

    PName                            = "k1";
    Parameters(PName)                = makeParameter();
    Parameters(PName)("label")       = 'k1';
    Parameters(PName)("uid")         = 'k1';
    Parameters(PName)("description") = 'k1';
    Parameters(PName)("value")       = 1.;
    Parameters(PName)("unit")        = 'mmol/g';

    PName                            = "k6";
    Parameters(PName)                = makeParameter();
    Parameters(PName)("label")       = 'k6';
    Parameters(PName)("uid")         = 'k6';
    Parameters(PName)("description") = 'k6';
    Parameters(PName)("value")       = 10.;
    Parameters(PName)("unit")        = 'none';    

    PName                            = "lambda";
    Parameters(PName)                = makeParameter();
    Parameters(PName)("label")       = 'lambda';
    Parameters(PName)("uid")         = 'lambda';
    Parameters(PName)("description") = 'lambda';
    Parameters(PName)("value")       = 1.; 
    Parameters(PName)("unit")        = 'none';


    SVName                           = "sc";
    StateVars(SVName)                = makeStateVar();
    StateVars(SVName)("label")       = 'sc';
    StateVars(SVName)("uid")         = 'sc';
    StateVars(SVName)("description") = 'Substrate concentration';
    StateVars(SVName)("value")       = 0.;
    StateVars(SVName)("unit")        = 'g/L';

    SPName                                   = "se";
    SetPoints(SPName)                        = makeSetPoint();
    SetPoints(SPName)("label")               = 'se';
    SetPoints(SPName)("uid")                 = 'se';
    SetPoints(SPName)("description")         = 'target substrate concentration';
    SetPoints(SPName)("value")               = 3.2;
    SetPoints(SPName)("unit")                = 'g/L';
    SetPoints(SPName)("physicalDeviceLabel") = 'se';
    SetPoints(SPName)("physicalDeviceUid")   = 'se';
    SetPoints(SPName)("domain")              = [-1000; 1000];
    SetPoints(SPName)("validityPeriod")      = -1.;

    CName                                  = "ActuatorA-qIn";
    Commands(CName)                        = makeCommand();
    Commands(CName)("physicalDeviceLabel") = 'ActuatorA';
    Commands(CName)("physicalDeviceUid")   = 'ActuatorA';
    Commands(CName)("deviceLabel")         = 'ActuatorA-pump';
    Commands(CName)("deviceUid")           = 'ActuatorA-qIn';
    Commands(CName)("dataLabel")           = 'qIn';
    Commands(CName)("dataDescription")     = 'Inlet flow rate command';
    Commands(CName)("unit")                = 'L/Day';
    Commands(CName)("domain")              = [-1000.; 1000.];
    Commands(CName)("value")               = 0.0;
    Commands(CName)("timestamp")           = [-1000.; 1000.];

    CName                                  = "ActuatorA-sIn";
    Commands(CName)                        = makeCommand();
    Commands(CName)("physicalDeviceLabel") = 'ActuatorA';
    Commands(CName)("physicalDeviceUid")   = 'ActuatorA';
    Commands(CName)("deviceLabel")         = 'ActuatorA-dilution';
    Commands(CName)("deviceUid")           = 'ActuatorA-sIn';
    Commands(CName)("dataLabel")           = 'sIn';
    Commands(CName)("dataDescription")     = 'Substrate inlet concentration command';
    Commands(CName)("unit")                = 'g/L';
    Commands(CName)("domain")              = [-1000.; 1000.];
    Commands(CName)("value")               = 10.;
    Commands(CName)("timestamp")           = [-1000.; 1000.];

  ,

   case 'Init' then
   //
     tLast = secToDays(tCurr);
     StateVars("sc")("value") = 0;
   ,

  //Do not edit this step!
  //Instead, edit either the 'doContinuousIntegration' or 'doDiscreteIntegration' functions
  case 'Integration' then
	if continuousScheme then,
          y = doContinuousIntegration();
	        StateVars("sc")("value") = y(1);
        else,
          doDiscreteIntegration();
        end
  ,

  //Command Computation
  case 'CommandComputation' then

    vLiq   =  Parameters("vLiq")("value");
  
    lambda =  Parameters("lambda")("value");
    k1     =  Parameters("k1")("value");
    k6     =  Parameters("k6")("value");

    se     =  SetPoints("se")("value");

    qm     =  Inputs("SensorA-qm")("value");
    sIn    =  Inputs("SensorB-sIn")("value");

    sc     = StateVars("sc")("value"); 



    dint   = min(max(0,(qm*k1/k6+lambda*(se-sc))/max(sIn-sc,1e-4)),10);
    qInCommand  = dint*vLiq;

    Commands("ActuatorA-qIn")("value") = qInCommand;
    Commands("ActuatorA-sIn")("value") = 15.;

    tLast = secToDays(tCurr);
  ,

  //Otherwise
  else
    disp('Controller Job flag unknown');
    //make controller fail if the flag is unknown
    //(better fail explicitly than silently)
    pause;

end;

Monitoring the Bioprocess is mainly done through the Graphical User Interface (UI). Some command-line based utilities are also useful to get a peek at the system and send various commands. These can also be used to perform some automatic maintenance if needed.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Fabien Dilet on July, 22 2010 using texi2html 1.78.