PID微分部分及其低通滤波时域
Differential part of PID and its low pass filter- time domain
我在 MATLAB 中编写了 PID:
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
并使用随机低通滤波器作为植物对其进行了测试:
function r = getResponse(t)
r = 1 - exp(-5*t);
end
测试代码:
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')
注意参数设置为kp=0;ki=1;kd=1;
。我在这里只测试差分部分。结果大错特错:
注意 Y 轴缩放了 10^307。它变得太大,在 ~1.6 秒后 PID 值超过双精度范围,因此曲线停止。
我已经确保 P 和 I 部分都能正常工作(参见 this question I asked a while ago)。
从D分量的曲线(见下图)可以明显看出,从一开始就开始剧烈震荡;它的值在第 5 个时间戳后 0.04 秒后达到 >50k:
我几乎可以肯定我在实现低通滤波器时一定犯了错误,但我也注意到即使移除了低通滤波器,微分值的行为仍然相似。
为了进行某种参考和比较,我还使用完全相同的 PID 增益(即 kp=0;ki=1;kd=1;
)对同一系统进行了 Simulink 仿真。下面是框图(左)、输入和输出图(右上图)和 PID 值图(右下)
请注意,增益块中没有 top/lower 限制,初始 inputs/outputs 设置为零。
这些 PID 增益远未达到优化,但它们在模拟和编码 PID 中给出了完全不同的结果。
因此,最大的问题是我在这里做错了什么吗?为什么两个结果有差异?
低通滤波器的实现不正确。一个低通滤波器的差分方程如下所示:
getResponse 函数的调用可能如下所示:
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
因此 getResponse 等同于 alpha
function r = getResponse(wc,Ts)
r = 1 - exp(-wc*Ts);
end
此外,您必须修改 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
我在 MATLAB 中编写了 PID:
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
并使用随机低通滤波器作为植物对其进行了测试:
function r = getResponse(t)
r = 1 - exp(-5*t);
end
测试代码:
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')
注意参数设置为kp=0;ki=1;kd=1;
。我在这里只测试差分部分。结果大错特错:
注意 Y 轴缩放了 10^307。它变得太大,在 ~1.6 秒后 PID 值超过双精度范围,因此曲线停止。
我已经确保 P 和 I 部分都能正常工作(参见 this question I asked a while ago)。
从D分量的曲线(见下图)可以明显看出,从一开始就开始剧烈震荡;它的值在第 5 个时间戳后 0.04 秒后达到 >50k:
我几乎可以肯定我在实现低通滤波器时一定犯了错误,但我也注意到即使移除了低通滤波器,微分值的行为仍然相似。
为了进行某种参考和比较,我还使用完全相同的 PID 增益(即 kp=0;ki=1;kd=1;
)对同一系统进行了 Simulink 仿真。下面是框图(左)、输入和输出图(右上图)和 PID 值图(右下)
请注意,增益块中没有 top/lower 限制,初始 inputs/outputs 设置为零。
这些 PID 增益远未达到优化,但它们在模拟和编码 PID 中给出了完全不同的结果。
因此,最大的问题是我在这里做错了什么吗?为什么两个结果有差异?
低通滤波器的实现不正确。一个低通滤波器的差分方程如下所示:
getResponse 函数的调用可能如下所示:
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
因此 getResponse 等同于 alpha
function r = getResponse(wc,Ts)
r = 1 - exp(-wc*Ts);
end
此外,您必须修改 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