PID - OE-FET/JISA GitHub Wiki
A common type of laboratory instrumentation is a PID controller — which tries to stably control some "input" quantity by adjusting an "output" quantity. The most common for of this is a temperature controller, where the input quantity is temperature, and the output is often something like heater power.
In JISA PID controllers are represented by the PID interface. Extending from
this interface is the TC interface, specifically for temperature controlling
PID instruments.
- Basics of Inputs, Outputs and Loops
- Loop Set-Points and Quantities
- Loop PID Values and Manual Output
- PID Zoning
- Ramping
- Temperature Controllers
Each PID instrument will have a number of three different "parts" in the form
of PID.Input, PID.Ouput, and PID.Loop objects. A PID.Input represents an
input quantity (i.e. the value we want to control), whereas a PID.Output
represents an output quantity (i.e. what we want the PID controller to change
to control the PID.Input value).
To extract these, you can use the relevant get...(...) methods like so:
PID pid = new Driver(new Address(...));
List<PID.Input> inputs = pid.getInputs();
List<PID.Output> outputs = pid.getOutputs();
List<PID.Loop> loops = pid.getLoops();Each PID.Loop can therefore have a single PID.Input object and a single
PID.Output object to configure the PID controller. To see which are currently
configured as such, you can call getInput() and getOutput() like so:
PID pid = new Driver(new Address(...));
PID.Loop loop = pid.getLoops().get(0); // Get the first loop
PID.Input input = loop.getInput(); // Get the input used for this loop
PID.Output output = loop.getOutput(); // Get the output used for this loopThe PID controller can also be configured to use a different input and/or output
by use of the set...(...) methods like so
loop.setInput(input);
loop.setOutput(output);However, care must be taken here, as different PID controllers have different
restrictions regarding which inputs and outputs can be used for which loops.
Therefore, each PID.Loop object provides getAvailable...() methods like so:
List<PID.Input> inputs = loop.getAvailableInputs();
List<PID.OUtput> outputs = loop.getAvailableOutputs();which will return lists of the various inputs and outputs that can be used for that loop. For instance, to configure the first loop to use its first available input and output:
PID pid = new Driver(new Address(...)); // Connect to instrument
// Get first loop, and first available input and output
PID.Loop loop = pid.getLoops().get(0);
PID.Input input = loop.getAvailableInputs().get(0);
PID.Output output = loop.getAvailableOutputs().get(0);
// Configure loop to use the extracted input and output
loop.setInput(input);
loop.setOutput(output);Controlling a PID control loop is therefore achieved by using its corresponding
PID.Loop object. For instance, its set-point can be controlled and checked like so:
loop.setSetPoint(100.0);
double setPoint = loop.getSetPoint();You can query its input and output values like so:
double inputValue = loop.getInput().getValue();
double outputValue = loop.getOutput().getValue();You can configure/query the PID values of a loop by use of:
// All at once
loop.setPIDValues(p, i, d);
// Separately
loop.setPValue(p);
loop.setIValue(i);
loop.setDValue(d);
// Query
double p = loop.getPValue();
double i = loop.getIValue();
double d = loop.getDValue();Whether the loop should use its PID values to control its output, or whether it just output some manually defined quantity can be chosen by using
// Sets its manual value to 10.0
loop.setManualOutputValue(10.0);
// true: use PID control, false: use manual value
loop.setPIDEnabled(true/false);You can further configure a PID loop to use a table of PID values corresponding to different regions of the set-point value. For instance, if the input was temperature then you may want something like
| Min [K] | Max [K] | P | I | D | Limit [%] |
|---|---|---|---|---|---|
| 0.0 | 100.0 | 10 | 5 | 0 | 50 |
| 100.0 | 200.0 | 20 | 8 | 1 | 100 |
| 200.0 | 300.0 | 30 | 9 | 2 | 100 |
where "Limit [%]" is the percentage limit to impose upon the PID output for a
given range. To configure this, we will need to create a PID.Zone object for
each line and pass them all to setPIDZones(...) like so:
loop.setPIDZones(
new PID.Zone(0 , 100, 10, 5, 0, 50),
new PID.Zone(100, 200, 20, 8, 1, 100),
new PID.Zone(200, 300, 30, 9, 2, 100)
);Then to enable the use of the table, use:
loop.setPIDZoningEnabled(true);To disable it, and return to the single set of PID values, use:
loop.setPIDZoningEnabled(false);Most (if not all) PID controllers offer a means of ramping their controlled quantity at a specified maximum rate (i.e. "go to this value at a constant rate of x" as opposed to "go to this value as quickly as possible"). To access this feature through the PID.Loop interface you can use the following methods:
// Methods to set
loop.setRampRate(value);
loop.setRampEnabled(true/false);
// Methods to query
double rate = loop.getRampRate();
boolean ramping = loop.isRampEnabled();For instance, to enable ramping for a temperature controller at 1.0 K/min:
loop.setRampRate(1.0);
loop.setRampEnabled(true);The ramp can then later be disabled by using:
loop.setRampEnabled(false);For PID controllers that are specifically temperature controllers, they are
represented using the TC interface. This extends from the PID interface, so
the types are interchangeable.
The purpose of the TC interface is simply to add more TC-specific method names
to the controllers, its inputs and outputs. For instance, thermometer inputs
implement the TMeter interface, so they can be treated as individual
thermometers. They also include the extra getThermometers() and getHeaters()
methods to specifically return only the inputs/outputs of that given type.
Here's some example code:
Java
// Accessing individual input/output components
TC tc = new Driver(new Address(...));
TC.Heater heater1 = tc.getHeaters().get(0);
TMeter sensor1 = tc.getThermometers().get(0);
double temperature = sensor1.getTemperature();
double power = heater1.getPower();
// Accessing first loop and first useable thermometer and heater
TC.Loop loop1 = tc.getLoops().get(0);
TC.Input thermometer = loop1.getAvailableThermometers().get(0);
TC.Output heater = loop1.getAvailableHeaters().get(0)
// Configure first loop
loop1.setInput(thermometer);
loop1.setOutput(heater);
loop1.setPIDValues(70, 30, 0);
// Go to 150 K
loop1.setTemperature(150);
loop1.setPIDEnabled(true);Kotlin
// Accessing individual input/output components
val tc = Driver(Address(...))
val heater1 = tc.heaters[0]
val sensor1 = tc.thermometers[0]
val temperature = sensor1.temperature
val power = heater1.power
// Accessing first loop and first useable thermometer and heater
val loop1 = tc.loops.first() // can use first() instead of [0]
val thermometer = loop1.availableThermometers.first()
val heater = loop1.availableHeaters.first()
// Configuring loop
loop1.input = thermometer
loop1.output = heater
loop1.P = 70.0
loop1.I = 30.0
loop1.D = 0.0
// Go to 150.0 K
loop1.temperature = 150.0
loop1.isPIDEnabled = truePython
# Accessing individual input/output components
tc = Driver(Address(...))
heater1 = tc.getHeaters()[0]
sensor1 = tc.getThermometers()[0]
temperature = sensor1.getTemperature()
power = heater1.getPower()
# Accessing first loop and first useable thermometer and heater
loop1 = tc.getLoops()[0]
thermometer = loop1.getAvailableThermometers()[0]
heater = loop1.getAvailableHeaters()[0]
# Configuring loop
loop1.setInput(thermometer)
loop1.setOutput(heater)
loop1.setPIDValues(70.0, 30.0, 0.0)
# Go to 150.0 K
loop1.setTemperature(150.0)
loop1.setPIDEnabled(True)