INOS
Control

Overview

The control classes of INOS are responsible for all kinds of control work except axes handling which is documented under Motion. The main focus lies currently on temperature control.

dot_inline_dotgraph_8.png

Overview of all classes which are involved in controller handling of INOS. Clicking on a class directs to the corresponding class documentation.

As shown in the class diagram, a control instance is represented by the class CINOSControl. It has two parts :

General

A control instance is created dynamically via the INCO tree and is usually done with our good old stream file mechanism. Refere to the temperature controller for an example.

Definition cinosmcmodule.h:1900

The values have the following meaning :

  • Name The controller name
  • TrajectoryType The trajectory type. Valid types are INOSJerk, INOSTrapeze or an own customer specific type
  • ControllerType The controller type. Valid types are INOSPid, INOSTemp, INOS2Pt or an own customer specific type
  • InputName The controller input name
  • OutputName The controller output name

Example :

Control.Cmd.Create("Zone0","INOSJerk","INOSTemp","ADP_02.AI_02_00","ADP_02.DO_02_00")

This call creates a control instane named 'Zone0' with a jerk ramp trajectory generator and a controller of type INOSTemp. The name of the input channel (in this case the name of the temperature process image value) is ADP_02.AI_02_00. The name of the command output (in this case a digital output) is ADP_02.DO_02_00.

To be able to use the digital output as a pseudo 'analog' output, one has the possibility tell the system to handle it as a software PWM (pulse wide modulation). This can be done like this :

Example of a pwmoutput.dt2:

// ! table-name
// ! table-data-record-start
Name; text_32; "ADP_02.DO_02_00";
SyncName; text_32; "ADP_02.DI_02_00";
SwitchTime; uint32; 10000;
SyncShift; uint32; 2000;
Average; uint32; 100;

The values have the following meaning :

  • Name The name of the digital output
  • SyncName If the pwm has to be synchronized to the 230V/110V power, one can define here the name of the digital input which provides the power sync signal
  • SwitchTime The fastest switching time [us], usually this is 10000us (230V/50Hz) or 8333us (110V/60Hz) if one is working with a solid state relais
  • SyncShift If a sync input is involved, this is the time [us] between sync and output set, 2000us is usually a good choice
  • Average Smoothen the the power output over this number of cylces (0 if not used)

Temperature control

The temperature controller bases on a standard PID controller with some temperature related optimizations, like

  • heat capacity support to improve as fast as possible jumps to a new commanded temperature
  • support for the Stefan Boltzmann law (see https://en.wikipedia.org/wiki/Stefan-Boltzmann_law)
  • several feed forward possibilities
  • alarm temperature support
  • asymmetric integrator limitation
  • temperature dependent PID parameter sets
  • different PID parameter sets for 'run' and 'halt'
  • ...

Properties

The properties are described in the following section. To get stared, just copy the section below to a new file, name it something like myzone.stream and put the file into the config section of your project.

// Controller creation
//
// Zone0 : Controller name
// INOSJerk : Type of ramp geni
// INOSTemp : Type of controller
// ADP_02.AI_02_00 : Name of temperature channel
// ADP_02.DO_02_00 : Name of digital output
//
Set ZONE0="Control.Entity.Zone0"
Control.Cmd.Create("Zone0","INOSJerk","INOSTemp","ADP_02.AI_02_00","ADP_02.DO_02_00");
//
// ActOutput
//
// Name of a digital output which has to be set if controller is active (empty if not used)
//
#{ZONE0}.Prop.ActOutput=""
//
// ErrorMask
//
// Possibility to mask out errors. The mask can be set either as a 32 bit value or by
// setting a single bit, e.g. #{ZONE0}.ErrorMask.FieldBus=1
//
#{ZONE0}.ErrorMask=0x00000000
//
// ErrorDis
//
// Possibility to define disabling errors, which end up in an inactivation of the
// controller. Can be set either as a 32 bit value or a single bit.
//
#{ZONE0}.ErrorDis=0xffffffff
//
// WarningMask
//
// Possibility to mask out warnings. Can be set either as a 32 bit value or a single bit.
//
#{ZONE0}.WarningMask=0x00000000
//
// CycleTime [ms]
//
// Should correspond to the cycletime of the temperature channel
//
#{ZONE0}.Prop.CycleTime=10.0
//
// EnbOutput
//
// Name of a digital output (or internal flag) which has to be cleared in case of an
// error. This 'output' also allows to group controllers. Controllers with
// the same EnbOutput name, set their 'group' error if EnbOutput is cleared by an
// other controller (empty if not used).
//
#{ZONE0}.Prop.EnbOutput=""
//
// Test values
//
#{ZONE0}.Cmd.Test.cmdVal1=100.0
#{ZONE0}.Cmd.Test.cmdVal2=150.0
//
// Ramp configuration. Setting V, A, B = 0 means, that the controller 'jumps' to the
// requested new temperature instead of ramping to it.
//
#{ZONE0}.Ramp.Cmd.cmdV=0.0
#{ZONE0}.Ramp.Cmd.cmdA=0.0
#{ZONE0}.Ramp.Cmd.cmdB=0.0
#{ZONE0}.Ramp.Cmd.cmdJ=0.0
//
// minS [°C]
//
// Minimal allowed commanded temperature.
//
#{ZONE0}.Ramp.Max.minS=40
//
// maxS [°C]
//
// Maximal allowed commanded temperature.
//
#{ZONE0}.Ramp.Max.maxS=300
//
// maxV [°C/sec]
//
// Maximal allowed commanded V. This value is also used to detect max. deltaT
// error. If the controller detects a temperature change with a V > 2*maxV, a
// 'DeltaTemp' error is initiated.
//
#{ZONE0}.Ramp.Max.maxV=40.0
//
// ErrorMask
//
// Possibility to mask out errors. Can be set either as a 32 bit value or a single bit.
//
#{ZONE0}.Control.ErrorMask=0x00000000
//
// ErrorDis
//
// Possibility to define disabling errors, which end up in an inactivation of the
// controller. Can be set either as a 32 bit value or a single bit.
//
#{ZONE0}.Control.ErrorDis=0xFFFFFFFF
//
// WarningMask
//
// Possibility to mask out warnings. Can be set either as a 32 bit value or a single bit.
//
#{ZONE0}.Control.WarningMask=0x00000000
//
// Vfilter
//
// V filter length. A value between 4 and 10 is normally a good choice.
//
#{ZONE0}.Control.Prop.Vfilter=10
//
// DelayToHalt [ms]
//
// Time in [ms] the controller waits after commanded temperature reached till it
// changes to 'settling' state.
//
#{ZONE0}.Control.Prop.DelayToHalt=1000
//
// SettleTime [ms]
//
// If the actual Serr (Scmd-Sact) is for 'SettleTime' ms smaller than 0.5*maxSerr,
// the Move to the new temperature is marked as done.
//
#{ZONE0}.Control.Prop.SettleTime=4000
//
// SettleTimeout [ms]
//
// Max. allowed settling time. If exeeded the controller initiates a 'settling'
// error.
//
#{ZONE0}.Control.Prop.SettleTimeout=120000
//
// ActivateTimeout [ms]
//
// Max. allowed time an activation can take. If exeeded the controller initiates
// a 'Activate' error. The activation is done if the above described 'settled'
// condition is ok.
//
#{ZONE0}.Control.Prop.ActivateTimeout=120000
//
// haltT [delta °C]
//
// If Scmd-Sact is < haltT, the controller goes into 'settling state'
//
#{ZONE0}.Control.Prop.haltT=2.0
//
// alarmT [°C]
//
// If Sact becomes >= alarmT, the controller initiates an 'AlmTemp' error.
//
#{ZONE0}.Control.Prop.alarmT=310.0
//
// alarmThyst [delta °C]
//
// After an alarm T error, the controller can not be reactivated until Sact
// is < alarmT-alarmThyst
//
#{ZONE0}.Control.Prop.alarmThyst=5.0
//
// breakT [°C]
//
// If Sact becomes >= breakT, the controller initiates an 'SnrBreak' (sensor
// break) error. This is only used if the according hardware doesn't support
// sensor break detection.
//
#{ZONE0}.Control.Prop.breakT=330.0
//
// maxOut [%]
//
// Max. allowed controller output.
//
#{ZONE0}.Control.Prop.maxOut=100
//
// minOut [%]
//
// Max. allowed controller output.
//
#{ZONE0}.Control.Prop.minOut=0
//
// fctOut [*]
//
// Output factor [% * factor = output)
//
#{ZONE0}.Control.Prop.fctOut=1
//
// minTint
//
// Lower PID integrator limit.
//
#{ZONE0}.Control.Prop.minTint=0
//
// maxTint
//
// Upper PID integrator limit.
//
#{ZONE0}.Control.Prop.maxTint=100
//
// maxSerr [°C]
//
// Max. allowed Scmd-Sact in state 'halt'.
//
#{ZONE0}.Control.Prop.maxSerr=2.0
//
// maxSerr [°C]
//
// Max. allowed Scmd-Sact in state 'run'.
//
#{ZONE0}.Control.Prop.maxSerrRun=30.0
//
// EnbBoltzmann [flag] (https://en.wikipedia.org/wiki/Stefan-Boltzmann_law)
//
// If set to 1 -> the controller enables the so called 'boltzmann' feedforward.
// ffBlz = d*A*T^4 (d=5.670*10^-8)
//
#{ZONE0}.Control.Flag.EnbBoltzmann=1.0
//
// Always zero PID integrator in case we're jumping to a new commanded temperature
//
#{ZONE0}.Control.Flag.IntegratorZeroAtJump=1.0
//
// blzP [%]
//
// Power at blzTmax.
//
#{ZONE0}.Control.Prop.blzP=12.0
//
// blzTmin [°C]
//
// Feedforward = 0.0 at this temperature.
//
#{ZONE0}.Control.Prop.blzTmin=20.0
//
// blzTmax [°C]
//
// Feedforward = blzP at this temperature.
//
#{ZONE0}.Control.Prop.blzTmax=300.0
//
// hcpFactor
//
// Used during 'jump' to a new commanded temperature. This factor can be evaluated
// with the tune type 'Hcp'. Tune("Type=Hcp");
//
#{ZONE0}.Control.Prop.hcpFactor=1.757
//
// Tune hcpFactor
//
// Use a 1 sec pulse of 100%
//
#{ZONE0}.Control.Tune.Pulse.Power=100
#{ZONE0}.Control.Tune.Pulse.Time=1000
#{ZONE0}.Control.Tune.Pulse.Timeout=30000
//
// OutDelta [°C], OutTimeout [ms]
//
// They are used to detect an 'output failure' in case of a jump to a new
// commanded temperature. They have the following meaning : After 'OutTimeout'
// ms a minimal temperature change of 'OutDelta' is requested, otherwise the
// controller emits an 'output failure' error.
//
#{ZONE0}.Control.Prop.OutDelta=1.0
#{ZONE0}.Control.Prop.OutTimeout=30000.0
//
// Low range pid parameters. Low range goes from #{ZONE0}.Ramp.Max.minS till
// #{ZONE0}.Control.Pid.Run.Medium
//
#{ZONE0}.Control.Pid.Halt.Low.Kp=3.0
#{ZONE0}.Control.Pid.Halt.Low.Ki=1400.0
#{ZONE0}.Control.Pid.Halt.Low.Kd=350.0
#{ZONE0}.Control.Pid.Halt.Low.ffS=0.0
#{ZONE0}.Control.Pid.Halt.Low.ffSoffset=0.0
#{ZONE0}.Control.Pid.Run.Low.Kp=0.5
#{ZONE0}.Control.Pid.Run.Low.Ki=1400.0
#{ZONE0}.Control.Pid.Run.Low.Kd=350.0
#{ZONE0}.Control.Pid.Run.Low.ffS=0.0
#{ZONE0}.Control.Pid.Run.Low.ffSoffset=0.0
#{ZONE0}.Control.Pid.Run.Low.ffV=0.0
#{ZONE0}.Control.Pid.Run.Low.ffA=0.0
#{ZONE0}.Control.Pid.Run.Low.ffB=0.0
//
// Medium range pid parameters. Medium range goes from #{ZONE0}.Control.Pid.Run.Medium till
// #{ZONE0}.Control.Pid.Run.High
//
#{ZONE0}.Control.Pid.Halt.Medium=100.0
#{ZONE0}.Control.Pid.Halt.Medium.Kp=2.5
#{ZONE0}.Control.Pid.Halt.Medium.Ki=1400.0
#{ZONE0}.Control.Pid.Halt.Medium.Kd=350.0
#{ZONE0}.Control.Pid.Halt.Medium.ffS=0.005
#{ZONE0}.Control.Pid.Halt.Medium.ffSoffset=30.0
#{ZONE0}.Control.Pid.Run.Medium=100.0
#{ZONE0}.Control.Pid.Run.Medium.Kp=2.0
#{ZONE0}.Control.Pid.Run.Medium.Ki=1400.0
#{ZONE0}.Control.Pid.Run.Medium.Kd=350.0
#{ZONE0}.Control.Pid.Run.Medium.ffS=0.0
#{ZONE0}.Control.Pid.Run.Medium.ffSoffset=0.0
#{ZONE0}.Control.Pid.Run.Medium.ffV=0.0
#{ZONE0}.Control.Pid.Run.Medium.ffA=0.0
#{ZONE0}.Control.Pid.Run.Medium.ffB=0.0
//
// High range pid parameters. High range goes from #{ZONE0}.Control.Pid.Run.High till
// #{ZONE0}.Ramp.Max.maxS
//
#{ZONE0}.Control.Pid.Halt.High=200.0
#{ZONE0}.Control.Pid.Halt.High.Kp=3.0
#{ZONE0}.Control.Pid.Halt.High.Ki=1400.0
#{ZONE0}.Control.Pid.Halt.High.Kd=350.0
#{ZONE0}.Control.Pid.Halt.High.ffS=0.001
#{ZONE0}.Control.Pid.Halt.High.ffSoffset=30.0
#{ZONE0}.Control.Pid.Run.High=200.0
#{ZONE0}.Control.Pid.Run.High.Kp=4.0
#{ZONE0}.Control.Pid.Run.High.Ki=1400.0
#{ZONE0}.Control.Pid.Run.High.Kd=350.0
#{ZONE0}.Control.Pid.Run.High.ffS=0.0
#{ZONE0}.Control.Pid.Run.High.ffSoffset=0.0
#{ZONE0}.Control.Pid.Run.High.ffV=0.0
#{ZONE0}.Control.Pid.Run.High.ffA=0.0
#{ZONE0}.Control.Pid.Run.High.ffB=0.0
//
// speed sets
//
#{ZONE0}.Param.Cmd.Create("Slow");
#{ZONE0}.Param.Entity.Slow.Ramp.cmdV=0.5;
#{ZONE0}.Param.Entity.Slow.Ramp.cmdA=10.000;
#{ZONE0}.Param.Entity.Slow.Ramp.cmdB=10.000;
#{ZONE0}.Param.Entity.Slow.Ramp.cmdJ=1000.000;
#{ZONE0}.Param.Cmd.Create("Rapid");
#{ZONE0}.Param.Entity.Rapid.Ramp.cmdV=5.0;
#{ZONE0}.Param.Entity.Rapid.Ramp.cmdA=100.000;
#{ZONE0}.Param.Entity.Rapid.Ramp.cmdB=100.000;
#{ZONE0}.Param.Entity.Rapid.Ramp.cmdJ=1000.000;
// commands starting with '!' will be executed immediately at startup
!Stream.Entity.zone0.Cmd.Run()

Errors

Long live errors

Warnings

Long live warnings

Setup step by step

This section gives a step by step intro how to setup an temperature controller.

Create the configuration

A temperature controller has a analog input which represents the temperature and either an analog or a digital output for the power stage. If the power stage is controlled over a digital output (e.g. a solid state relais), one has to configure a software pwm for this output, which allows the controller to work with values from 0..100%. See General for an example.

The controller itself is set up with a so called stream file. It is an ASCII file which simply has CallProcedure and PutVariable entries and is executed at booting of the system. An example can be found here Properties.

In short :

  • create a *.dt2 file with the pwm configuration (see also General) and put it into the config directory of your project
  • create a *.stream file with this content Properties. To get a good starting point, adjust the following values :
// allowed temperature range
Ramp.Max.minS=40
Ramp.Max.maxS=300
// disable boltzmann
Control.Flag.EnbBoltzmann=0
// disable heat capacity
Control.Prop.hcpFactor=0.0
// zero all PID factors
Control.Pid.Halt.Low.Kp=0.0
Control.Pid.Halt.Low.Ki=0.0
Control.Pid.Halt.Low.Kd=0.0
Control.Pid.Run.Low.Kp=0.0
Control.Pid.Run.Low.Ki=0.0
Control.Pid.Run.Low.Kd=0.0
Control.Pid.Halt.Medium.Kp=0.0
Control.Pid.Halt.Medium.Ki=0.0
Control.Pid.Halt.Medium.Kd=0.0
Control.Pid.Run.Medium.Kp=0.0
Control.Pid.Run.Medium.Ki=0.0
Control.Pid.Run.Medium.Kd=0.0
Control.Pid.Halt.High.Kp=0.0
Control.Pid.Halt.High.Ki=0.0
Control.Pid.Halt.High.Kd=0.0
Control.Pid.Run.High.Kp=0.0
Control.Pid.Run.High.Ki=0.0
Control.Pid.Run.High.Kd=0.0

Verify the configuration

After builing and loading the project to your target, let's verify if everything was setup ok. Open the INCO explorer and check if there is a folder

Control.Entity.Zone0 28.05 °C

where 'Zone0' is the name of your controller. If you don't see this folder :

  • check if the feature 'General options.Temperature control' in your iDev project is selected
  • check with the eventlogger if there are some error traces regarding your controller

Now lets check if the temperature changes if we output power for a short time :

  • open a Varlog and select your input and output channels to be logged (set the trigger to the output channel != 0.0). You find these channels under Image.Analog.Input/Output and/or Image.Digital.Output
  • start logger
  • write e.g. 10% to the output channel, wait some seconds and write 0.0 again

If you see a significant temperature change in your log, everything seems to be fine. If not, try agin with more power or check your config and wiring.

Measure heat capacity

To give the controller a hint regarding the relation between power and temperature delta, you have the possibility to measure the so called hcpFactor (heat capacity factor). This measurement makes only sense if yor heater is some kind of heating plate or similar. It definitely makes no sense for a heater like a hot air gun. Skip this section in this case.

The measurement is done by setting a predefined power for a selected time and measuring the temperature delta.

Measure the hcpFactor :

  • ensure the temperature of your heater lies below Ramp.Max.minS. If this is not the case, cool down the system somehow
  • set the requested pulse values.
Control.Entity.Zone0.Control.Tune.Pulse.Power = 100 // pulse power
Control.Entity.Zone0.Control.Tune.Pulse.Time = 5000 // pulse time [ms]
Control.Entity.Zone0.Control.Tune.Pulse.Timeout = 30000 // pulse timeout [ms]
  • activate the controller. The Control.Entity.Zone0.State should now show 'ready' :
Control.Entity.Zone0.Cmd.Activate()
  • start measurement and (read the next step before you start) :
Control.Entity.Zone0.Cmd.Tune("Type=Hcp")
  • during the measurement the controller state Control.Entity.Zone0.State shows 'tuning'. As soon as the state goes back to 'ready', the mesdurement is done and we suggest to immediately inactivate the controller again, to avoid any overheatings
Control.Entity.Zone0.Cmd.InActivate()
  • the measure result can be found here :
Control.Entity.Zone0.Control.Tune.Result.Hcp.Factor = 1.757
  • move the measure value to the properties section and don't forget to update your controller stream file as well
Control.Entity.Zone0.Control.Prop.hcpFactor = 1.757

PID factors

To setup PID factors, there is currently no reliable mechanism available. You have to do it by hand, but usually this is not really tricky.

  • to avoid any errors during this work, tell the controller to disable value checking and settling supervision :
Control.Entity.Zone0.Control.Flag.ChkVal = 0
Control.Entity.Zone0.Control.Prop.maxSerr = 50
Control.Entity.Zone0.Control.Prop.maxSerrRun = 100
  • we start PID adjustment in the predefined low range (#{ZONE0}.Ramp.Max.minS till #{ZONE0}.Control.Pid.Run.Medium, so clear Ki and Kd in this range (Halt and Run) and set Kp to a starting value e.g. 1
Control.Entity.Zone0.Control.Pid.Halt.Low.Kp = 1.0
Control.Entity.Zone0.Control.Pid.Halt.Low.Ki = 0.0
Control.Entity.Zone0.Control.Pid.Halt.Low.Kd = 0.0
Control.Entity.Zone0.Control.Pid.Run.Low.Kp = 1.0
Control.Entity.Zone0.Control.Pid.Run.Low.Ki = 0.0
Control.Entity.Zone0.Control.Pid.Run.Low.Kd = 0.0
  • activate the controller and move to a reasonable temperature of the low range (e.g. 10°C below #{ZONE0}.Control.Pid.Run.Medium)
Control.Entity.Zone0.Cmd.Activate()
Control.Entity.Zone0.Cmd.Move(80.00:d, "")
  • now 'play' around with Control.Entity.Zone0.Control.Pid.Halt.Low.Kp (typically by increasing Kp) till you get a clean temperature oscillation, use varlog to measure/view the oscillation
  • after successfu oscillation measurement, set the PID values in the following manner
Control.Entity.Zone0.Control.Pid.Halt.Low.Kp = 0.6 * used Kp for oscillation
Control.Entity.Zone0.Control.Pid.Halt.Low.Ki = 0.5 * measured oscillation time [ms]
Control.Entity.Zone0.Control.Pid.Halt.Low.Kd = 0.125 * measured oscillation time [ms]
Control.Entity.Zone0.Control.Pid.Run.Low.Kp = 0.6 * used Kp for oscillation
Control.Entity.Zone0.Control.Pid.Run.Low.Ki = 0.5 * measured oscillation time [ms]
Control.Entity.Zone0.Control.Pid.Run.Low.Kd = 0.125 * measured oscillation time [ms]
  • you should now have a stable controller for the low range, do the same steps for medium and high range or directly use the low parameters for the other ranges, they usually fit not to bad

Optimizations

There are several possibilities to optimize and therefore 'help' the controller to do its work.

  • heat capacity measurement (seen already here Measure heat capacity)
  • temperature dependant power feed forward
  • Boltzmann law feed forward for heating plates

To reduce the dependancy of the integrator limits

Control.Entity.Zone0.Control.Prop.minTint
Control.Entity.Zone0.Control.Prop.maxTint

you have two possibilities to work with a power feed forward.

1. Power feed forward with ffS

The ffS mechanism is configured with the two parameters (per temperature range) :

Control.Entity.Zone0.Control.Pid.Halt.Low.ffS = 0.02
Control.Entity.Zone0.Control.Pid.Halt.Low.ffSoffset = 30.0

The feed forwarded power is calculated like this

You can either let the system measure this feed forward with

Control.Entity.Zone0.Cmd.Tune("Type=ffS")

or live change the parameters during active control till the current controller integrator tends to be zero

Control.Entity.Zone0.Control.Err.errSint = 14.56

2. Power feed forward with Boltzmann law

The Stefan–Boltzmann law describes the power radiated from a black body in terms of its temperature (see https://en.wikipedia.org/wiki/Stefan-Boltzmann_law) So if your heater is e.g. similar to a heating plate, this feed forward mechanism is probably the right choice.

How to setup :

  • activate the controller and move to max. / or near the max. allowed temperature
  • check the controller output at this point
Control.Entity.Zone0.Control.Act.actOut = 14.0

In this example, we measured about 14%. Setup the following parameters to get things running

Control.Entity.Zone0.Control.Flag.EnbBoltzmann = 1.0
Control.Entity.Zone0.Control.Prop.blzTmin = 30.0
Control.Entity.Zone0.Control.Prop.blzTmax = 300.0
Control.Entity.Zone0.Control.Prop.blzP = 13.0

The parameters have the following meaning :

  • EnbBoltzmann Enable the boltzmann law
  • blzTmin Minimum temperature where the law should take effect
  • blzTmax Maximum temperature where the law should take effect
  • blzP The requested feed forward power at blzTmax (use about 10% less value than measured)

And now happy heating !