• FPGA学习之实现PID算法


    1 废话篇

    1.1 理论学习

    PID控制算法的学习,本次介绍位置式和增量式PID控制算法的原理和Matlab的仿真分析

    1.1.1 模拟PID控制算法

    在工程中,比较用的多的就是比例、积分、微分控制,简称PID控制。G(s) 为被控对象的系统传递函数。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    PID控制算法分为三种,分别是P调节,PI调节和PID调节算法。

    P调节算法:比例控制是一种最简单的控制方式。其控制器的输出和输入误差信号成比例光系。偏差一旦产生。控制器立即就发生作用即调节控制输出,使被控量朝着减小误差的方向变化,偏差减小的速度取决于比例系数Kp,Kp越大偏差减小的越快,但是容易引起振荡,尤其是在迟滞环节比较大的情况下,Kp减小,发生振荡的可能性减小。但是调节的速度变慢。但单纯的比例控制存在稳态误差不能消除的缺点,这里就需要积分控制。P调节算法的控制规律和阶跃响应如下图所示
    在这里插入图片描述

    PI调节算法
    在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入问太后存在稳态误差,则称这个控制系统是有稳态误差的或者简称有差系统。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增大而加大,它推动控制器的输出增大使稳态误差进一步减少,直至等于零。因此,比例+积分控制器,可以使系统进入稳态后无稳态误差,实质上就是对偏差累积进行控制,直至偏差为零。积分控制作用始终施加指向给定值的作用力,有利于消除静差,其效果不仅与偏差大小有关,而且还与偏差持续的时间有关。PI调节算法的控制规律和阶跃响应如下图所示:
    在这里插入图片描述
    PID调节算法:
    在微分控制中,控制器的输出和输入误差信号的微分(即误差变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因使由于存在有较大惯性组件(环节)或有滞后组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的方法是使抑制误差的作用的变化“超前”,即在误差接近0时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是 微分项,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或者滞后的被控对象,微分比较有效果
    在这里插入图片描述

    1.1.2 离散化

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    1.1.3 伪算法

    这里看到网上一个位置式PID实现的伪算法:

    previous_error := 0 // 上一次偏差
    integral := 0;   // 积分和
    
    // 循环
    // 采样周期为dt
    loop:
    	// setpoint 设定值
    	// measured value 反馈值
    	 error := setpoint - measured_value;    		 // 计算得到偏差
    	 integral := integral + error * dt;     		 // 计算得到积分累加和
    	 derivative := (error - previous_error ) / dt;   // 计算得到的微分
    	 output := kp*error + ki*integral + kd*derivate;  // 计算得到PID的输出
    	 previous_error := error;                         // 保存当前偏差为下一次采样时所需要的历史偏差
    	 wait(dt);
    	 goto loop;
    	 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.1.4 matlab算法位置式

    对应的matlab仿真:

    % 位置式PID算法仿真
    clear;
    clc;
    %% 参数定义
    Ts = 1e-3;% 采样时间     
    e_sum = 0;% 多次误差和
    % PID参数(可根据实际情况调节)
    kp = 0.32;% 比例 
    ki = 0.15;% 积分
    kd = 0.12;% 微分
    %% 建立被控系统
    % 假设被控对象的系统传递函数为0.88/(0.05s + 1.5)
    s_sys = tf(0.88,[0.05 1.5]); % 根据传递函数建立被控系统的模型
    z_sys = c2d(s_sys,Ts,'z');    % 拉氏变换-->z变换
    [m,n] = tfdata(z_sys,'v');
    %% 开始PID控制
    T = 2000;% 设置仿真运行时间
    r = 800;% 期望输出值
    % 预先分配内存
    u = zeros(1,T);% PID输出初始值
    y = zeros(1,T);% 被控系统响应输出
    e = zeros(1,T);% 误差信号
    for k=2:1:T
        y(k) = -n(2)*y(k-1) + m(1)*u(k) + m(2)*u(k-1);% 计算被控系统输出
        e(k) = r - y(k);   % 计算误差
        u(k) = kp*e(k) + ki*e_sum + kd*(e(k)-e(k-1)); %根据误差调整PID控制量输出
        e_sum = e_sum+e(k);% 计算多次误差和
    end
    % 绘制过渡过程的曲线
    t = 1:1:T;
    figure('Color','White');
    plot(t,y,'r-','LineWidth',1.2);
    title('pid-pos')
    xlabel('t');
    ylabel('y');
    grid on;
    set(gca,'FontSize',12,'LineWidth',1.2,'Fontname', 'Times New Roman','FontWeight','Bold')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    1.1.5 matlab 算法增量式

    在这里插入图片描述

    % 位置式PID算法仿真
    clear;
    clc;
    %% 参数定义
    Ts = 1e-3;% 采样时间     
    % PID参数(可根据实际情况调节)
    kp = 0.32;% 比例 
    ki = 0.15;% 积分
    kd = 0.12;% 微分
    %% 建立被控系统
    % 假设被控对象的系统传递函数为0.88/(0.05s + 1.5)
    s_sys = tf(0.88,[0.05 1.5]); % 根据传递函数建立被控系统的模型
    z_sys = c2d(s_sys,Ts,'z');    % 拉氏变换-->z变换
    [m,n] = tfdata(z_sys,'v');
    %% 开始PID控制
    T = 2000;% 设置仿真运行时间
    r = 800;% 期望输出值
    % 预先分配内存
    u = zeros(1,T);% PID输出初始值
    y = zeros(1,T);% 被控系统响应输出
    e = zeros(1,T);% 误差信号
    d_u = zeros(1,T);% PID输出增量
    for k=3:1:T
        y(k) = -n(2)*y(k-1) + m(1)*u(k) + m(2)*u(k-1);% 计算被控系统输出
        e(k) = r - y(k);   % 计算误差
        d_u(k) = kp*(e(k)-e(k-1))+ki*e(k)+kd*((e(k)-e(k-1))-(e(k-1)-e(k-2)));% 根据误差获取PID增量
        u(k) = u(k-1) + d_u(k);% 根据PID增量计算PID控制输出
    end
    % 绘制过渡过程的曲线
    t = 1:1:T;
    figure('Color','White');
    plot(t,y,'r-','LineWidth',1.2);
    title('pid-incre')
    xlabel('t');
    ylabel('y');
    grid on;
    set(gca,'FontSize',12,'LineWidth',1.2,'Fontname', 'Times New Roman','FontWeight','Bold')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述

    1.1.5 verilog 实现PID

    error.v

    module error(
    
        input clk,
        input rst_n,
        input signed [9:0] target,
        input signed [9:0] y,
        
        output signed [9:0] ek0,
        output reg signed[9:0] ek1;
        output reg signed [9:0] ek2;
    );
    
    assign ek0 = target - y;   // 计算e(k)
    
    always @(posedge clk or negedge rst_n) begin
        
        if(!rst_n) begin
            ek1 <= 10'd0;
            ek2 <= 10'd0;
        end
        else begin
            ek1 <= ek0;                 // 延时一个时钟周期 得到e(k-1)
            ek2 <= ek1;                 // 再延时一个时钟周期 得到e(k-2)
        end 
    end
    
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    pid_value.v

    ///
    // Company: 
    //
    // File: pid_value.v
    // File history:
    //      : : 
    //      : : 
    //      : : 
    //
    // Description: 
    //
    // 
    //
    // Targeted device:   
    // Author: 
    //
    /// 
    
    //`timescale  / 
    
    module pid_value( 
    
        input clk,                          // 时钟信号
        input rst_n,                        // 复位信号,低电平有效
        input signed [14:0] d_uk,           // pid 增量
    
        output reg signed [14:0] uk0        // pid 输出值
    
    );
    
    reg signed [14:0] uk1 = 15'd0;          // 上一时刻u(k-1) 的值
    
    always @(d_uk) begin
        uk0 = uk1 + d_uk;                   // 计算pid 输出值
        uk1 = uk0;                          // 寄存上一时刻 u(k-1) 的值
    end
    
    //
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    incre_value.v

    ///
    // Company: 
    //
    // File: incre_value.v
    // File history:
    //      : : 
    //      : : 
    //      : : 
    //
    // Description: 
    //
    // 
    //
    // Targeted device:   
    // Author: 
    //
    /// 
    
    //`timescale  / 
    
    module incre_value( 
    
        input signed [9:0] ek0,
        input signed [9:0] ek1,
        input signed [9:0] ek2,
    
        input [3:0] kp,
        input [3:0] ki,
        input [3:0] kd,
    
        output signed [14:0] d_uk
    
    );
    
    
    assign  d_uk = kp*(ek0 -ek1) + ki*ek0 + kd*((ek0 - ek1)-(ek1 - ek2)); // 计算pid增量
    
    //
    
    endmodule
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    demo_top.v

    ///
    // Company: 
    //
    // File: demo_top.v
    // File history:
    //      : : 
    //      : : 
    //      : : 
    //
    // Description: 
    //
    // 
    //
    // Targeted device:   
    // Author: 
    //
    /// 
    
    //`timescale  / 
    
    module demo_top (
    
        input clk,                          // 时钟信号
        input rst_n,                        // 复位信号
        input signed [9:0] target,          // 目标值
        input signed [9:0] y,               // 实际输出值
    
        input [3:0] kp,
        input [3:0] ki,
        input [3:0] kd,
        
        output signed [14:0] uk0            // pid 输出值
    );
    
    
    wire signed [9:0] ek0;
    wire signed [9:0] ek1;
    wire signed [9:0] ek2;
    
    error error_inst(
        .clk(clk),
        .rst_n(rst_n),
        .y(y),
        .ek0(ek0),
        .ek1(ek1),
        .ek2(ek2)
    );
    
    wire signed [14:0] d_uk; // pid 增量
    incre_value incre_value_inst(
    
        .ek0(ek0),
        .ek1(ek1),
        .ek2(ek2),
        .kp(kp),
        .ki(ki),
        .kd(kd),
        .d_uk(d_uk)
    
    );
    
    pid_value pid_value_inst(
    
        .clk(clk),
        .rst_n(rst_n),
        .d_uk(d_uk),
        .uk0(uk0)
    );
    
    endmodule
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    tb_demo_top.v

    ///
    // Company: 
    //
    // File: tb_demo_top.v
    // File history:
    //      : : 
    //      : : 
    //      : : 
    //
    // Description: 
    //
    // 
    //
    // Targeted device:   
    // Author: 
    //
    /// 
    
    //`timescale  / 
    
    module tb_demo_top();
    
    reg clk;
    reg rst_n;
    reg signed [9:0] target ;  // 目标值
    reg signed [9:0] y; // 实际值
    
    reg [3:0] kp;    // 比例系数
    reg [3:0] ki;    // 积分系数
    reg [3:0] kd;    // 微分系数
    
    wire signed [14:0] uk0;
    
    reg [10:0] i;
    reg [8:0] mytxt[0:1997];
    
    initial begin
        $readmemh(,mytxt);
        clk = 1'b0;
        rst_n = 1'b1;
        #5 rst_n = 1'b0;
        #5 rst_n = 1'b1;
        target = 10'd350;
        kp = 4'd10;
        ki = 4'd9;
        kd = 4'd8;
    
        for(i = 0; i<11'd1997; i=i+1)
            begin
                y = mytxt[i];
                #10;
            end
    end
    
     
    always #5 clk = ~clk;
    
    demo_top demo_top_tb(.clk(clk),
    .rst_n(rst_n),
    .target(target),
    .y(y),
    .kp(kp),
    .ki(ki),
    .kd(kd),
    .uk0(uk0)
    ); 
    
    //
    
    endmodule
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
  • 相关阅读:
    Matlab图像处理-HSI模型
    LaTeX Algorithm相关写法
    如何实现WinApp的UI自动化测试?自动化工具如何选择人?
    SpringBoot+VUE3前后端分离-【支付宝支付】
    ssm的Demo
    大模型的无限上下文与数据集组合艺术
    JAVA 100道题(26)
    如何解决跨域问题?
    【云原生 | 24】Docker运行数据库实战之MySQL
    websocket接口测试
  • 原文地址:https://blog.csdn.net/qq_30093417/article/details/127725429