目录
1 pid的基本原理
2 FPGA实现
3 联合仿真
1 pid的基本原理
PID控制器(比例-积分-微分控制器),由比例单元(Proportional)、积分单元(Integral)和微分单元(Derivative)组成。可以透过调整这三个单元的增益K_{p},K_{i}和K_{d}来调定其特性。PID控制器主要适用于基本上线性,且动态特性不随时间变化的系统。
有些应用只需要PID控制器的部分单元,可以将不需要单元的参数设为零即可。因此PID控制器可以变成PI控制器、PD控制器、P控制器或I控制器。其中又以PI控制器比较常用,因为D控制器对回授噪声十分敏感,而若没有I控制器的话,系统不会回到参考值,会存在一个误差量。
若定义u(t)为控制输出,PID算法可以用下式表示:
Kp——比例系数
Ti——积分时间常数
Td——微分时间常数
e(t)——偏差
u(t)——控制量
在采样周期足够小时,可以作如下近似:
用这种近似方法,可以得到两种形式数字PID控制算法 :
位置式PID算法
可以看出数字调节的输出u(k)跟过去的所有偏差信号有关,计算机需要对e(i)进行累加,运算量太大,一般不用
增量式PID算法
Δ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)+Δu(k)=u(k−1)+Kp∗(e(k)−e(k−1))+Kie(k)
2 FPGA实现
状态机代码:
//
//
// u(k)=u(k−1)+Δu(k)=u(k−1)+KP∗(e(k)−e(k−1))+Kie(k)
//
module pi #(
parameter [23:0] k_p = 24'd30, //比例参数,避免小数
parameter [23:0] k_i = 24'd2 //积分参数
) (
input wire signed [15:0] i_real, //输出
input wire signed [15:0] i_aim, //目标
input wire clk, //系统时钟
input wire rstn, //复位
input wire pi_en, //pi控制器开关
output wire signed [15:0] u_out, //pi控制器输出
output reg o_en //pi控制器输出有效标志
);
reg signed [31:0] error_1, error_2, delta_error; //ek ek-1 e(k)-e(k-1)
reg signed [31:0] mult_kp, mult_i, mult_i1, mult_i2; //kp*(e(k)-e(k-1)) ki*ek 寄存积分误差
reg signed [31:0] u_out_temp, delta_u; //u(k-1)+deltau
assign u_out = u_out_temp[15:0];
//误差限幅 加法 乘法 减法
function reg signed [31:0] protect_add(input signed [31:0] a, input signed [31:0] b);
begin
protect_add = $signed({a[31], a}) + $signed({b[31], b});
if (protect_add > $signed(33'h7fffffff)) //
protect_add = $signed(32'h7fffffff);
else if (protect_add < -$signed(32'h7fffffff))
protect_add = -$signed(32'h7fffffff);
else protect_add = protect_add;
end
endfunction
function reg signed [31:0] protect_mul(input signed [31:0] a, input signed [24:0] b);
begin
protect_mul = a * b;
if (protect_mul > $signed(57'h7fffffff)) //
protect_mul = $signed(32'h7fffffff);
else if (protect_mul < -$signed(57'h7fffffff))
protect_mul = -$signed(32'h7fffffff);
else protect_mul = protect_mul;
end
endfunction
function reg signed [31:0] protect_subtract(input signed [31:0] a, input signed [31:0] b);
begin
protect_subtract = $signed({a[31], a}) - $signed({b[31], b});
if (protect_subtract > $signed(33'h7fffffff))
protect_subtract = $signed(32'h7fffffff);
else if (protect_subtract < -$signed(32'h7fffffff))
protect_subtract = -$signed(32'h7fffffff);
else protect_subtract = protect_subtract;
end
endfunction
reg [2:0] state ;
parameter IDLE = 3'd0;
parameter Ki_ek = 3'd1;
parameter Delta_ek = 3'd2;
parameter Kp_delta_ek = 3'd3;
parameter Delta_u = 3'd4;
parameter U_out = 3'd5;
//e(k)
always @(posedge clk or negedge rstn)
if (~rstn) begin
state <= IDLE;
error_1 <= 0;
error_2 <= 0;
delta_error <= 0;
mult_kp <= 0;
mult_i <=0;
mult_i1 <=0;
mult_i2 <=0;
delta_u<=0;
u_out_temp<=0;
end
else begin
case (state)
IDLE: if (pi_en) begin //ek
error_1<= protect_subtract(i_aim,i_real);
o_en<=0;
state <=Ki_ek;
end
else begin
error_1<=0;
state<=IDLE;
end
Ki_ek: begin
error_2<=error_1;//ek-1
mult_i<=protect_mul(error_1,k_i);//ki*ek
state<=Delta_ek;
end
Delta_ek: begin
mult_i1<=mult_i;//delay_ki*ek
delta_error<=protect_subtract(error_1,error_2);//ek-ek-1
state<=Kp_delta_ek;
end
Kp_delta_ek: begin
mult_i2<=mult_i1;//delay_ki*ek again
mult_kp<=protect_mul(delta_error,k_p);//kp*(ek-ek-1)
state<=Delta_u;
end
Delta_u: begin
delta_u <= protect_add(mult_i2, mult_kp);//kp*(ek-ek-1)+ki*ek
state<=U_out;
end
U_out: begin
u_out_temp = protect_add(u_out_temp, delta_u);//u(k)=u(k−1)+Δu(k)
state<=IDLE;
o_en<=1'b1;
end
default :
state<=IDLE;
endcase
end
endmodule
状态机:
流水线代码:
//
//
// u(k)=u(k−1)+Δu(k)=u(k−1)+KP∗(e(k)−e(k−1))+Kie(k)
//
module pi #(
parameter [23:0] k_p = 24'd30, //比例参数,避免小数
parameter [23:0] k_i = 24'd2 //积分参数
) (
input wire signed [15:0] i_real, //输出
input wire signed [15:0] i_aim, //目标
input wire clk, //系统时钟
input wire rstn, //复位
input wire pi_en, //pi控制器开关
output wire signed [15:0] u_out, //pi控制器输出
output reg o_en //pi控制器输出有效标志
);
reg signed [31:0] error_1, error_2, delta_error; //ek ek-1 e(k)-e(k-1)
reg signed [31:0] mult_kp, mult_i, mult_i1, mult_i2; //kp*(e(k)-e(k-1)) ki*ek 寄存积分误差
reg signed [31:0] u_out_temp, delta_u; //u(k-1)+deltau
reg en_s1, en_s2, en_s3, en_s4, en_s5; //流水线标志
assign u_out = u_out_temp[15:0];
//误差限幅 加法 乘法 减法
function reg signed [31:0] protect_add(input signed [31:0] a, input signed [31:0] b);
begin
protect_add = $signed({a[31], a}) + $signed({b[31], b});
if (protect_add > $signed(33'h7fffffff)) //
protect_add = $signed(32'h7fffffff);
else if (protect_add < -$signed(32'h7fffffff))
protect_add = -$signed(32'h7fffffff);
else protect_add = protect_add;
end
endfunction
function reg signed [31:0] protect_mul(input signed [31:0] a, input signed [24:0] b);
begin
protect_mul = a * b;
if (protect_mul > $signed(57'h7fffffff)) //
protect_mul = $signed(32'h7fffffff);
else if (protect_mul < -$signed(57'h7fffffff))
protect_mul = -$signed(32'h7fffffff);
else protect_mul = protect_mul;
end
endfunction
function reg signed [31:0] protect_subtract(input signed [31:0] a, input signed [31:0] b);
begin
protect_subtract = $signed({a[31], a}) - $signed({b[31], b});
if (protect_subtract > $signed(33'h7fffffff))
protect_subtract = $signed(32'h7fffffff);
else if (protect_subtract < -$signed(32'h7fffffff))
protect_subtract = -$signed(32'h7fffffff);
else protect_subtract = protect_subtract;
end
endfunction
//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 <= protect_subtract(i_aim,i_real);
end
end
//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)
mult_i <= protect_mul(error_1, k_i); //ki*ek
end
end
//比例误差的计算 减法一个周期 乘法一个周期
//所以需要将积分误差延时两个周期
//e(k)-e(k-1)
always @(posedge clk or negedge rstn)
if (~rstn) begin
en_s3 <= 1'b0;
mult_i1 <= 0;
delta_error <= 0;
end else begin
en_s3 <= en_s2;
if (en_s2) begin
mult_i1 <= mult_i;
delta_error <= protect_subtract(error_1, error_2);
end
end
// kp*(e(k)-e(k-1))
always @(posedge clk or negedge rstn)
if (~rstn) begin
en_s4 <= 1'b0;
mult_i2 <= 0;
mult_kp <= 0;
end else begin
en_s4 <= en_s3;
if (en_s3) begin
mult_i2 <= mult_i1;
mult_kp <= protect_mul(delta_error, k_p);
end
end
//delta_u
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(mult_i2, mult_kp);
end
end
// u_out
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
流水线的速度比状态机快很多,但是资源也比状态机消耗得多。
3 联合仿真
testbench
`timescale 1ns / 1ps
module tb_pi();
//输入
reg [15:0] i_real;
reg [15:0] i_aim;
reg sys_clk;
reg rstn;
reg pi_en;
//输出
wire [15:0] u_out;
wire o_en ;
//初始值
initial
begin
sys_clk = 1'b1;
rstn <= 1'b0;
i_real<=16'd0;
i_aim<=16'd350;
pi_en<=1'b1;
#20
rstn <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//将pi控制器的输出减少2^6倍
always #30 i_real =i_real+u_out>> 6;
//例化
pi u_pi
(
. i_real (i_real) ,
. i_aim (i_aim),
. clk (sys_clk ),
. rstn (rstn ),
. pi_en (1'b1),
. u_out (u_out),
. o_en (o_en)
);
endmodule
3、仿真结果
这里上升的比较缓慢,可以将u_out与i_real,之间的线性关系即本文中的2^6 适当调整。
参考:文章来源:https://www.toymoban.com/news/detail-779024.html
PI闭环的FPGA实现_爱折腾的张Sir的博客-CSDN博客深入浅出PID控制算法(二)————PID算法离散化和增量式PID算法原理及Matlab实现_万般滋味皆生活的博客-CSDN博客_离散pid公式文章来源地址https://www.toymoban.com/news/detail-779024.html
到了这里,关于FPGA实现PI控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!