PI闭环的FPGA实现

2 篇文章 0 订阅

PI闭环的FPGA实现

1 原理分析

​ 最近小张同学在做项目的时候发现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)+Ti10te(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)+Ti10te(t)d(t))

2 PI的硬件实现

在数字电路中,并无法实现连续时间的积分和微分,因此数字pid中,只能以固定采样周期来进行离散化,从而实现近似的连续时间的积分和微分,利用这个思想,将式(2)进行离散化,

  1. 用一系列采样时间kT来代替时间t
  2. 以和式来代替积分: ∫ 0 t e ( t ) d ( t ) ) = ∑ e ( j T ) T = T ∑ e ( k ) \int ^t_0 e(t){\rm d}(t)) = \sum e(jT)T = T\sum e(k) 0te(t)d(t))=e(jT)T=Te(k)
  3. 向后差分 来代替 微分 d e ( t ) d t = e ( k ) − e ( k − 1 ) Δ t = e ( k ) − e ( k − 1 ) T s \frac{de(t)}{dt}=\frac{e(k)-e(k-1)}{\Delta t}=\frac{e(k)-e(k-1)}{T_s} dtde(t)=Δte(k)e(k1)=Tse(k)e(k1)

位置式PID

可得位置式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(k1))
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(k1)+(k)

增量式PID

增量为:
Δ 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(k1)=kp[e(k)e(k1)]+kie(k)+kd[e(k)2e(k1)+e(k2)]
令位置式 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(k1)=kp[e(k)e(k1)]+kie(k)
增量式PID的输出为
u ( k ) = u ( k − 1 ) + Δ u ( k ) u(k)=u(k-1)+\Delta u(k) u(k)=u(k1)+Δ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(k1)+Δu(k)=u(k2)+Δu(k1)+Δu(k)=u(0)+u(1)+.....+u(k1)
由于 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(k1)+Δu(k)=u(k1)+KP(e(k)e(k1))+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(k1)=u(k2)+Δu(k1)=u(k2)+KP(e(k1)e(k2))+KiTse(k1)
代入上式即可约去 e ( k − 1 ) e(k-1) e(k1)项,不断迭代,由于 e ( 0 ) = 0 e(0)=0 e(0)=0,可发现其最终结果与位置式PI的表达式一致,也即两种PI算法完全相同(未超出限幅值的前提下)
因此,可以理解为无论用增量叠加的方式来计算位置式PI,还是直接计算,结果都是相同的。两者唯一的区别就是位置式PI需要同时设置积分限幅和输出限幅,而增量式PI只需输出限幅。 增量式的好处就是启动时和状态突然发生变化时不会产生突变,其控制效果与位置式PI基本相同。

3 如何在硬件中实现积分器

如何实现一个积分器(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 Outx=Out(x1)+In(x)T
​ 新输出值 = 旧输出值 + 输入*采样周期

//C语言实现
Out += In*T;

4、FPGA的硬件实现

话不多说,直接贴代码

代码简单易懂 相信各位聪明的小伙伴一看就能看懂。

`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  

5、仿真波形

在这里插入图片描述
i_aim 给定值是350 实际值是调节后的曲线,从图中可以看出,PI调节可以快速达到闭环,并进入稳态。

如果感觉博主的博客写的还可以的话,欢迎一键三连,点个关注不迷路哦~

  • 12
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 34
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值