赞
踩
最近小张同学在做项目的时候发现PI闭环的FPGA学习资料很少,秉持着“既然没有轮子,那么自己就造一个的原则”,于是乎自己写了个PI的Verilog程序。
FPGA中实现PI闭环与DSP、STM32、arm中都不一样,由于FPGA中没有数学库,需要从底层开始解决运算中产生的小数、除法等问题。因此FPGA中实现PI闭环相比来说有一点点难度。
首先,位置式PI和增量式PI的选择问题,由于位置式PI的实现原理包含历史误差的累计,并且启动瞬间或状态突然切换瞬间会有突变现象,突变现象会影响电机的动态性能,同时启动电流会很大,而这会对电机工作状态产生影响,因此采用增量式PI控制更优。当然,我觉得用位置式pi也能适用于电机的pi闭环。因为本质上来说,这两种pi实现机理并无差异,最终的控制效果也应该差异不大。
而增量式PI控制通常包括并联式PI控制和串联式PI控制,并联式PI控制可以实现 K p K_p Kp和 K i K_i Ki 的解耦,方便参数调试,所以采用并联式增量PI控制器。
上图是增量并联并联式PI框图,含义为参考值与实际值比较得到的差值分为两部分,一部分通过P的直接增益,另一部分通过积分的积分增益,合并输出。
下图是传递函数,只调节Kp相,可以调节幅值,对应的Ki值需要与之对应,可以调节控制系统的零点,Kp幅值增加对应的零点频率会下降,零点频率实际上是Kp和Ki的函数。
典型pid系统的输出信号表达式如(1)所示
u
k
=
K
p
(
(
e
t
)
+
1
T
i
∫
0
t
e
(
t
)
d
(
t
)
+
T
d
d
e
(
k
)
e
(
k
)
)
u_k = K_p((e_t)+\frac{1}{T_i}\int ^t_0 e(t){\rm d}(t)+T_d\frac{de(k)}{e(k)})
uk=Kp((et)+Ti1∫0te(t)d(t)+Tde(k)de(k))
采用PI控制,即无微分项,则
u
k
=
K
p
(
(
e
t
)
+
1
T
i
∫
0
t
e
(
t
)
d
(
t
)
)
u_k = K_p((e_t)+\frac{1}{T_i}\int ^t_0 e(t){\rm d}(t))
uk=Kp((et)+Ti1∫0te(t)d(t))
在数字电路中,并无法实现连续时间的积分和微分,因此数字pid中,只能以固定采样周期来进行离散化,从而实现近似的连续时间的积分和微分,利用这个思想,将式(2)进行离散化,
可得位置式PID的离散化如下式
u
k
=
K
p
(
e
k
)
+
K
i
∑
(
e
k
)
+
K
d
(
e
(
k
)
−
e
(
k
−
1
)
)
u_k = K_p(e_k)+K_i\sum(e_k)+K_d(e(k)-e(k-1))
uk=Kp(ek)+Ki∑(ek)+Kd(e(k)−e(k−1))
PI中令位置式
K
d
K_d
Kd = 0 即可得 位置式PI表达式
u
k
=
K
p
(
e
k
)
+
K
i
∑
(
e
k
)
u_k = K_p(e_k)+K_i\sum(e_k)
uk=Kp(ek)+Ki∑(ek)
硬件中可以对误差的累计
S
u
m
(
k
)
=
S
u
m
(
k
−
1
)
+
(
k
)
。
Sum(k)=Sum(k-1)+(k)。
Sum(k)=Sum(k−1)+(k)。
增量为:
Δ
u
(
k
)
=
u
(
k
)
−
u
(
k
−
1
)
=
k
p
[
e
(
k
)
−
e
(
k
−
1
)
]
+
k
i
e
(
k
)
+
k
d
[
e
(
k
)
−
2
e
(
k
−
1
)
+
e
(
k
−
2
)
]
\Delta u(k) = u(k)-u(k-1) =k_p[e(k)-e(k-1)]+k_i\,\,e(k)+k_d\,[e(k)-2e(k-1)+e(k-2)]
Δu(k)=u(k)−u(k−1)=kp[e(k)−e(k−1)]+kie(k)+kd[e(k)−2e(k−1)+e(k−2)]
令位置式
K
d
K_d
Kd = 0 即可得
Δ
u
(
k
)
=
u
(
k
)
−
u
(
k
−
1
)
=
k
p
[
e
(
k
)
−
e
(
k
−
1
)
]
+
k
i
e
(
k
)
\Delta u(k) = u(k)-u(k-1) =k_p[e(k)-e(k-1)]+k_i\,\,e(k)
Δu(k)=u(k)−u(k−1)=kp[e(k)−e(k−1)]+kie(k)
增量式PID的输出为
u
(
k
)
=
u
(
k
−
1
)
+
Δ
u
(
k
)
u(k)=u(k-1)+\Delta u(k)
u(k)=u(k−1)+Δu(k)
然而很多场合下需要的往往不只增量,还有上一拍的输出值,于是可知增量式PI调节器算法为
u
(
k
)
=
u
(
k
−
1
)
+
Δ
u
(
k
)
=
u
(
k
−
2
)
+
Δ
u
(
k
−
1
)
+
Δ
u
(
k
)
=
u
(
0
)
+
u
(
1
)
+
.
.
.
.
.
+
u
(
k
−
1
)
u(k)=u(k-1)+\Delta u(k)=u(k-2)+\Delta u(k-1)+\Delta u(k)=u(0)+u(1)+.....+u(k-1)
u(k)=u(k−1)+Δu(k)=u(k−2)+Δu(k−1)+Δu(k)=u(0)+u(1)+.....+u(k−1)
由于
u
(
0
)
=
0
u(0)=0
u(0)=0在具体编程操作中,对每一拍的
Δ
u
(
k
)
\Delta u(k)
Δu(k)进行累积,即为PI调节器的输出;同样地,为了避免超过允许值,仅需对输出限幅即可。
事实上,由增量式PI
u
(
k
)
=
u
(
k
−
1
)
+
Δ
u
(
k
)
=
u
(
k
−
1
)
+
K
P
∗
(
e
(
k
)
−
e
(
k
−
1
)
)
+
K
i
T
s
e
(
k
)
u(k)=u(k-1)+\Delta u(k)=u(k-1)+K_P*(e(k)-e(k-1))+K_iT_se(k)
u(k)=u(k−1)+Δu(k)=u(k−1)+KP∗(e(k)−e(k−1))+KiTse(k)
可得
u
(
k
−
1
)
=
u
(
k
−
2
)
+
Δ
u
(
k
−
1
)
=
u
(
k
−
2
)
+
K
P
∗
(
e
(
k
−
1
)
−
e
(
k
−
2
)
)
+
K
i
T
s
e
(
k
−
1
)
u(k-1)=u(k-2)+\Delta u(k-1)=u(k-2)+K_P*(e(k-1)-e(k-2))+K_iT_se(k-1)
u(k−1)=u(k−2)+Δu(k−1)=u(k−2)+KP∗(e(k−1)−e(k−2))+KiTse(k−1)
代入上式即可约去
e
(
k
−
1
)
e(k-1)
e(k−1)项,不断迭代,由于
e
(
0
)
=
0
e(0)=0
e(0)=0,可发现其最终结果与位置式PI的表达式一致,也即两种PI算法完全相同(未超出限幅值的前提下)
因此,可以理解为无论用增量叠加的方式来计算位置式PI,还是直接计算,结果都是相同的。两者唯一的区别就是位置式PI需要同时设置积分限幅和输出限幅,而增量式PI只需输出限幅。 增量式的好处就是启动时和状态突然发生变化时不会产生突变,其控制效果与位置式PI基本相同。
如何实现一个积分器(I)?
创建一个加法器是最简单的形式,PI环节如上述框图描述所示。Z-1代表延迟块,T代表采样周期。
O
u
t
(
x
)
=
O
u
t
(
x
−
1
)
+
I
n
(
x
)
∗
T
Out(x) = Out(x-1) + In(x)*T
Out(x)=Out(x−1)+In(x)∗T
新输出值 = 旧输出值 + 输入*采样周期
//C语言实现
Out += In*T;
话不多说,直接贴代码
代码简单易懂 相信各位聪明的小伙伴一看就能看懂
`timescale 1 ns/1 ns
//增量式 PI
//仿真验证通过版本
// 至于k_p k_i
module pid_controller_delta #(
parameter logic [23:0] k_p=24'd30, //kP
parameter logic [23:0] k_i=24'd2 //Ki
)(
input wire signed [15:0] i_real,//实际电流值
input wire signed [15:0] i_aim, //输入给定电流值
input wire clk,
input wire rstn,
input wire pi_en, //park变换是否完成的信号,高定平即完成park变换
output wire signed [15:0] u_out, //输出电压值 u_alpha U_beta
output reg o_en //PI 结束信号
);
reg signed [31:0] error_1,error_2,delta_error;//误差值 error_1为上一时刻的误差值,error_2为当前的误差值
reg signed [31:0] multipy_p,multipy_i,multipy_i1,multipy_i2; //分别代表delta_u两部分的乘积
reg signed [31:0] u_out_temp,delta_u; //寄存上一时刻的输出值,delta_u代表输出的增量
reg en_s1,en_s2,en_s3,en_s4,en_s5;
assign u_out = u_out_temp[15:0];
function automatic logic signed [31:0] protect_add(input logic signed [31:0] a, input logic signed [31:0] b);
automatic logic signed [32:0] y;
y = $signed({a[31],a})+$signed({b[31],b}); //积分限幅
//积分限幅
if( y > $signed(33'h7fffffff) )//
return $signed(32'h7fffffff);
else if( y < -$signed(32'h7fffffff) )
return -$signed(32'h7fffffff);
else
return $signed(y[31:0]);
endfunction
function automatic logic signed [31:0] protect_mul(input logic signed [31:0] a, input logic signed [24:0] b);
automatic logic signed [57:0] y;
y = a * b; //积分限幅
//积分限幅
if( y > $signed(57'h7fffffff) )//
return $signed(32'h7fffffff);
else if( y < -$signed(57'h7fffffff) )
return -$signed(32'h7fffffff);
else
return $signed(y[31:0]);
endfunction
function automatic logic signed [31:0] protect_subtract(input logic signed [31:0] a, input logic signed [31:0] b);
automatic logic signed [32:0] y;
y = $signed({a[31],a}) - $signed({b[31],b});
//
if( y > $signed(33'h7fffffff) )
return $signed(32'h7fffffff);
else if ( y < -$signed(32'h7fffffff) )
return -$signed(32'h7fffffff);
else
return $signed(y[31:0]);
endfunction
// plpeline 1 计算e(k)
always@(posedge clk or negedge rstn)
if(~rstn) begin
en_s1 <= 1'b0;
error_1 <= 0;
end
else begin
en_s1 <= pi_en;
if(pi_en) begin
error_1 <= $signed({{16{i_aim[15]}},i_aim}) - $signed({{16{i_real[15]}},i_real}) ;
end
end
//pipeline 2 计算e(k-1)
always@(posedge clk or negedge rstn)
if(~rstn) begin
en_s2 <= 1'b0;
error_2 <= 0;
end
else begin
en_s2 <= en_s1;
if(en_s1) begin
error_2 <= error_1; //e(k-1)
multipy_i <= protect_mul(error_1,k_i);
end
end
always@(posedge clk or negedge rstn)
if(~rstn) begin
en_s3 <= 1'b0;
multipy_i1 <= 0;
delta_error <= 0;
end
else begin
en_s3 <= en_s2;
if(en_s2) begin
multipy_i1 <= multipy_i;
delta_error <= protect_subtract(error_1,error_2);
end
end
always@(posedge clk or negedge rstn)
if(~rstn) begin
en_s4 <= 1'b0;
multipy_i2 <= 0;
multipy_p <=0;
end
else begin
en_s4 <= en_s3;
if(en_s3) begin
multipy_i2 <= multipy_i1;
multipy_p <= protect_mul(delta_error,k_p);
end
end
always@(posedge clk or negedge rstn)
if(~rstn) begin
en_s5 <= 1'b0;
delta_u <= 0;
end
else begin
en_s5 <= en_s4;
if(en_s4) begin
delta_u <= protect_add(multipy_i2,multipy_p);
end
end
always@(posedge clk or negedge rstn)
if(~rstn) begin
o_en <= 1'b0;
u_out_temp <= 0;
end
else begin
o_en <= en_s5;
if(en_s5) begin
u_out_temp = protect_add(u_out_temp,delta_u);
end
end
endmodule
i_aim 给定值是350 实际值是调节后的曲线,从图中可以看出,PI调节可以快速达到闭环,并进入稳态。
如果感觉博主的博客写的还可以的话,欢迎一键三连,点个关注不迷路哦~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。