Reputation: 3793
I programmed PID in MATLAB:
classdef PID < handle
properties
Kp = 0
Ki = 0
Kd = 0
SetPoint = 1
Dt = 0.01
end
properties (Access = private)
IState = 0
PreErr = 0
end
methods
function obj = PID(Kp, Ki, Kd, SetPoint, Dt)
if nargin == 0
return;
end
obj.Kp = Kp;
obj.Ki = Ki;
obj.Kd = Kd;
obj.SetPoint = SetPoint;
obj.Dt = Dt;
end
function output = update(obj, measuredValue, t)
err = obj.SetPoint - measuredValue;
P = obj.getP(err);
I = obj.getI(err);
val = lowPass(obj,t);
D = obj.getD(err*val);
output = P + I + D;
end
function val = getP(obj, err)
val = obj.Kp*err;
end
function val = getI(obj, err)
obj.IState = obj.IState + err * obj.Dt;
val = obj.Ki * obj.IState;
end
function val = getD(obj, err)
val = obj.Kd * (err - obj.PreErr) / obj.Dt;
obj.PreErr = err;
end
function val = lowPass(obj,t)
N = 10;
val = 1-exp(-N*t);
end
end
end
And tested it using a random low pass filter as the plant:
function r = getResponse(t)
r = 1 - exp(-5*t);
end
The test code:
sr = 1e2; % sampling rate 100Hz
st = 10; % sampling time 10s
ss = st*sr+1; % sample size
t = 0:1/sr:st; % time
input = ones(1,ss)*100;
output = zeros(1,ss);
measured = 0;
pid = PID(0,1,1,input(1),t(2)-t(1));
for i = 2:ss
rPID(i) = pid.update(measured, t(i));
output(i) = rPID(i)*getResponse(t(i));
measured = output(i);
end
figure
plot(t,output)
hold on;
plot(t,input)
plot(t,rPID)
legend('Output','Input','PID')
Note that the parameters are set to kp=0;ki=1;kd=1;
. I'm only testing the differential part here. The result is very wrong:
Notice the Y-axis is scaled by 10^307. It gets too big that after ~1.6s the PID value exceeds the range of double precision and therefore, the curve stops.
I have ensured that both P and I parts work well enough (see this question I asked a while ago).
From the curve for the D component (see figure below), one can clearly see that it starts to oscillate heavy from the very beginning; its value reaches >50k after the 5th timestamp at 0.04s:
I'm almost certain I must have made a mistake in implementing the low pass filter, but I also noticed that even with the low pass filter removed, the differential values still behave similarly.
To have some sort of reference and comparison, I also made a Simulink simulation of the same system, using the exact same PID gains (i.e. kp=0;ki=1;kd=1;
). Below is the block diagram (left), figure for input and output (top right figure) and figure for PID values (bottom right)
Note that there is no top/lower limit in the gain blocks and the initial inputs/outputs are set to zeros.
These PID gains are nowhere near optimised but they give completely different results in the simulation and coded PID.
Therefore the big question is am I doing something wrong here? Why is there a difference between the two results?
Upvotes: 1
Views: 801
Reputation: 56
The implementation of the low pass filter is not correct. The difference equation of a low pass filter is as shown:
The call of the getResponse function could look like this:
pid = PID(0,1,1,input(1),t(2)-t(1));
for i = 2:ss
rPID(i) = pid.update(measured, t(i));
alpha = getResponse(0.25,0.01);
output(i) = rPID(i)*alpha+(1-alpha)*output(i-1);
measured = output(i);
end
Thus getResponse is equivalent to alpha
function r = getResponse(wc,Ts)
r = 1 - exp(-wc*Ts);
end
Further you have to modify the lowPass function in the PID class.
function output = update(obj, measuredValue)
err = obj.SetPoint - measuredValue;
P = obj.getP(err);
I = obj.getI(err);
val = lowPass(obj,err,0.1,0.01);
D = obj.getD(val);
output = P + I + D;
end
% ...
function val = lowPass(obj,err,wc,Ts)
alpha = getResponse(wc,Ts);
output = err*alpha+(1-alpha)*obj.output_1;
obj.output_1 = output;
val = output;
end
Upvotes: 2