Skip to content
William Wood edited this page Apr 19, 2024 · 9 revisions

PID and Temperature Controllers

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.

Contents

Basics of Inputs, Outputs and Loops

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 loop

The 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);

Loop Set-Points and Quantities

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();

Loop PID Values and Manual Output

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);

PID Zoning

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);

Ramping

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);

Temperature Controllers

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 = true

Python

# 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)
Clone this wiki locally