Problems with using PID controller

0
I have initialised my controller in the StartUp task.After that i'm trying to call the output of the controller in the Main task.I'm now showing the variables in a watchtable, but i'm getting 'Nan' for the output of the controller. I dont understand how to properly call the method. In the Github page they say you have the variables 'Value' and 'Input' to fill in. What of those is the setpoint? And what do these two values mean for the controller?
asked
9 answers
0

Hi,


The reason you are getting NaN from controller.Next() is usually because the PID controller has not yet received a valid previous cycle / elapsed time, or one of the inputs used internally results in an invalid calculation (most commonly a divide-by-zero during the first call).

From the snippet you shared, the important part is this call:


S_outputPID := controller.Next(50, I_setpoint);

In most PID implementations the parameters passed to Next() are interpreted as:

Next(measuredValue, setpoint)

So the first parameter is the current process value, and the second parameter is the target (setpoint).

That means your call is effectively doing:

measuredValue = 50
setpoint = I_setpoint

If I_setpoint is still uninitialized or zero during the first cycle, the controller can produce NaN, especially when the derivative or integral part is calculated.

The two values mentioned in the documentation typically mean:

  • Value / measuredValue → the current measured value from the process (sensor input)
  • Input / setpoint → the desired target value the controller should reach

Example:

Measured temperature = 50°C
Target temperature   = 70°C

Then the call would be:


output := controller.Next(50, 70);

The controller internally calculates the error:

error = setpoint - measuredValue

and uses that together with K, Ti, Td to compute the PID output.

Another thing to check is the first cycle timing. Since you are using:

dynamic.elapsedTimeProvider := elapsedTimeProvider

the controller expects a valid elapsed time between calls. If Next() is executed immediately after initialization (before a proper time delta exists), the derivative calculation can produce NaN.

A common way to avoid this is to:

  • ensure the PID loop runs cyclically with a fixed interval
  • call Next() only after the first cycle delay has passed.

For example in the main task:

loop
    wait(100 ms)
    output := controller.Next(measuredValue, setpoint)
end

So the key things to verify are:

  1. measuredValue (first parameter) must be the actual process value
  2. setpoint must be initialized before calling Next()
  3. The controller should run in a cyclic loop with a valid time delta

Once those conditions are correct, the PID output should return a valid number instead of NaN.


answered
1

The issue is most likely related to how the PID Next() parameters are used.


In most PID implementations:

  • Value → current measured process value (actual value from the system)
  • Input → setpoint (the desired target value)


The controller internally calculates the error as:

error = setpoint - process_value


So your call should typically look like this:

S_outputPID := controller.Next(I_measuredValue, I_setpoint);


Make sure both the measured value and the setpoint are initialized before the first cycle. If one of them is invalid or not initialized, the PID calculation can result in NaN.


If this resolves your issue, please mark the answer as accepted so it can help others facing the same problem.


answered
1

Since both I_PV and I_setpoint already have valid values, the problem is probably not the parameters anymore.


If controller.Next(I_PV, I_setpoint) still returns NaN, and even a manually assigned S_outputPID gets overwritten back to NaN, then the NaN is most likely coming from inside the PID block itself.


The most likely cause is the time delta / elapsed time used by the controller. On the first scan, or if the elapsedTimeProvider is not returning a valid value yet, the PID can end up doing a divide-by-zero internally, especially with the I and D parts active. That would explain why you get NaN even though K, Ti, Td, PV, and setpoint all look valid.


I would check these points:

Make sure the controller is not called in the same cycle where it is initialized. Try initializing it in StartUp, then call Next(...) only from the next scan onward.


Also try disabling the integral and derivative parts temporarily and test with P-only control first. For example, set Ti and Td to 0 if the library allows that, or otherwise remove those terms temporarily. If the output stops becoming NaN, then the issue is almost certainly related to the internal time calculation of the I/D terms.


So the likely issue is not the meaning of Value and Input anymore. Your call format now looks correct. The issue is more likely that the PID block is being evaluated before it has a valid elapsed time.


answered
1

It looks like the main issue might be related to the elapsed time used by the PID controller.


From your screenshot, elapsedTimeProvider.GetElapsedSeconds() returns 0. If the PID controller receives a time delta of 0, some internal calculations (especially related to derivative or internal timing logic) can result in NaN values.


One thing you could try is checking whether the MeasuredTimeProvider is actually updating between cycles. If it always returns 0, it might not be measuring the elapsed time yet, or the instance might be recreated/reset each cycle.


Another thing worth checking is whether the same instance of elapsedTimeProvider is reused across cycles. If a new instance is created repeatedly, the elapsed time may never increase.


For testing purposes, you might also try calling controller.Next(...) only when the elapsed time is greater than 0. For example, you could add a small guard condition to see if the output stops returning NaN once a valid time delta exists.


So the parameters (I_PV and I_setpoint) appear correct based on your screenshots. The behavior you are seeing may be related to the elapsed time staying at 0, and once the controller receives a valid time delta, the NaN output might disappear.


IF elapsedTimeProvider.GetElapsedSeconds() > 0 THEN
   S_outputPID := controller.Next(I_PV, I_setpoint);
END_IF
answered
1

If I’m not missing something, this behavior may actually be expected based on the values you shared.


In your example, the setpoint is 100 and the process value (PV) is 50, so the controller error would be:

error = setpoint - PV = 100 - 50 = 50


With K = 5, the proportional part alone may already produce:

P = 5 × 50 = 250


Since Ti = 5 and Td = 5 are also active, the controller is likely not using only the proportional part. The integral and derivative terms may also be contributing to the result. That could explain why S_outputPID becomes significantly higher than 250, for example around 750.


If I’m not overlooking anything, S_outputPID also does not seem to represent a percentage automatically. It appears to be the raw PID output, and its scale depends on how the library calculates and applies the P, I, and D terms.


My understanding is that SetIntegratorMinOutput() and SetIntegratorMaxOutput() probably only limit the integral part of the controller, not the final combined PID output. So even if the integrator is limited between 0 and 100, the proportional and derivative parts may still push the total output above that range.


As a suggestion, it may be worth checking whether the library provides a separate final output min/max clamp. If not, you could consider applying your own limit to S_outputPID after the PID calculation if you want to use it as a 0–100% signal.


answered
0

I have given the I_PV (actual process value) and the I_setpoint (setpoint value) a starting value.


Then i'm trying to call the next-function as follows in the Main.

And this is what i'm getting while monitoring.

answered
0

I have set up this in my configuration, so in the global variables.



After that I'm having this set up in a StartUp task, so it only runs once before the main loop. Where the Ti and Td are both non active, so only P-action.


And after that i'm running this in the main program.



Yet still i'm having a 'Nan' output.





answered
0

I have asked the Elapsed time via 'O_GetElapsedSeconds := elapsedTimeProvider.GetElapsedSeconds()' in the Main Program and i get this.




answered
0

So the problem is indeed that the 'ElapsedTimeProvider' was 0 en stayed at the zero value. So internally the PID was dividing by zero. I fixed it with using a constantTimeProvider so that i could set the value myself. For some reason the MeasurementTimeProvider just isn't working.


I just have a follow up question. The next photo is my StartUp task. I have set the Integratorbounds of the PID between 0 en 100. So that i could see it as a percentage and could work from there.



But when i set my I_Setpoint to the value 100, the PID goes to a value of 750. What does this value mean? and what do the 'SetIntegratorMinOutput' and 'SetIntegratorMaxOutput' then mean?


The next photo is just to show what is being called in the Main program




answered