PID算法的理论分析

PID算法基本原理

  PID算法是控制行业最经典、最简单、而又最能体现反馈控制思想的算法。PID算法的执行流程是非常简单的,即利用反馈来检测偏差信号,并通过偏差信号来控制被控量。而控制器本身就是比例、积分、微分三个环节的加和。其功能框图如下:
在这里插入图片描述

  根据上图我们考虑在某个特定的时刻t,此时输入量为e(t),输出量为u(t),于是PID的基本控制规律就可以表示为如下公式:
u ( t ) = K p e ( t ) + K i ∫ 0 t e ( τ ) d τ + K d u(t)=K_pe(t)+K_i\int_{0}^{t}e(τ)dτ+K_d u(t)=Kpe(t)+Ki0te(τ)dτ+Kd d e ( t ) d t de(t)\over dt dtde(t)

K p K_p Kp是比例增益; K i K_i Ki是积分增益; K d K_d Kd是微分增益;

PID算法的离散化

  要在计算机上实现就必须将其离散化,接下来我们就说一说PID算法的离散化问题。在实现离散化之前,我们需要对比例、积分、微分的特性做一个简单的说明。
比例:就是用来对系统的偏差进行反应,所以只要存在偏差,比例就会起作用。因此比例作用相当于某一时刻的偏差(err)与比例系数 K p K_p Kp的乘积。
积分:主要是用来消除静差,所谓静差就是指系统稳定后输入输出之间依然存在的差值,而积分就是通过偏差的累计来抵消系统的静差。积分则是误差在一定时间内的和,满足以下公式;

K i ∫ 0 t e ( τ ) d τ K_i \int_0^t e(τ)dτ Ki0te(τ)dτ

微分:则是对偏差的变化趋势做出反应,根据偏差的变化趋势实现超前调节,提高反应速度。微分则是误差变化曲线某处的导数,或者说是某一点的斜率,因此这里需要引入微分;

K d d e ( t ) d t K_d \frac {de(t)}{dt} Kddtde(t)

  在数字系统中进行PID算法控制,需要对上述算法进行离散化;假设系统采样时间为 △ t △t t则将输入 e ( t ) e(t) e(t)序列化得到:
在这里插入图片描述
将输出 u ( t ) u(t) u(t)序列化得到:
在这里插入图片描述

比例项 : K p e ( t ) K_p e(t) Kpe(t) 离散化 K p e k K_p e_k Kpek

积分项: K i ∫ 0 t k e ( τ ) d τ K_i \int_0^{t_{k}} e(τ)dτ Ki0tke(τ)dτ 离散化 K i ∑ i = 1 k e ( i ) △ t K_i\sum_{i=1}^k e(i) △t Kii=1ke(i)t

微分项: K d K_d Kd d e ( t k ) d t {de(t_k)}\over{dt} dtde(tk)离散化 K d K_d Kd e ( k ) − e ( k − 1 ) △ t e(k)-e(k-1)\over△t te(k)e(k1)

所以最终可以得到式①,也就是网上所说的位置式PID

u ( k ) = K p e k + K i ∑ i = 1 k e ( i ) △ t + K d u(k)=K_pe_k+K_i\sum_{i=1}^{k}e(i)△t+K_d u(k)=Kpek+Kii=1ke(i)t+Kd e ( k ) − e ( k − 1 ) △ t e(k)-e(k-1)\over△t te(k)e(k1)
将式①再做一下简化:
△ u ( k ) = u ( k ) − u ( k − 1 ) △u(k)=u(k)-u(k-1) u(k)=u(k)u(k1)
最终得到增量式PID的离散公式如下:
△ u ( k ) = K p ( e ( k ) − e ( k − 1 ) ) + K i e ( k ) + K d ( e ( k ) − 2 e ( k − 1 ) + e ( k − 2 ) ) △u(k)=K_p(e(k)-e(k-1))+K_ie(k)+Kd(e(k)-2e(k-1)+e(k-2)) u(k)=Kp(e(k)e(k1))+Kie(k)+Kd(e(k)2e(k1)+e(k2))

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 × derivative //计算得到PID输出
    previous_error := error //保存当前偏差为下一次采样时所需要的历史偏差
    wait(dt) //等待下一次采用
    goto loop

PID算法C++实现

pid.cpp

#ifndef _PID_SOURCE_
#define _PID_SOURCE_

#include <iostream>
#include <cmath>
#include "pid.h"

using namespace std;

class PIDImpl
{
    public:
        PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki );
        ~PIDImpl();
        double calculate( double setpoint, double pv );

    private:
        double _dt;
        double _max;
        double _min;
        double _Kp;
        double _Kd;
        double _Ki;
        double _pre_error;
        double _integral;
};


PID::PID( double dt, double max, double min, double Kp, double Kd, double Ki )
{
    pimpl = new PIDImpl(dt,max,min,Kp,Kd,Ki);
}
double PID::calculate( double setpoint, double pv )
{
    return pimpl->calculate(setpoint,pv);
}
PID::~PID() 
{
    delete pimpl;
}


/**
 * Implementation
 */
PIDImpl::PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ) :
    _dt(dt),
    _max(max),
    _min(min),
    _Kp(Kp),
    _Kd(Kd),
    _Ki(Ki),
    _pre_error(0),
    _integral(0)
{
}

double PIDImpl::calculate( double setpoint, double pv )
{
    
    // Calculate error
    double error = setpoint - pv;

    // Proportional term
    double Pout = _Kp * error;

    // Integral term
    _integral += error * _dt;
    double Iout = _Ki * _integral;

    // Derivative term
    double derivative = (error - _pre_error) / _dt;
    double Dout = _Kd * derivative;

    // Calculate total output
    double output = Pout + Iout + Dout;

    // Restrict to max/min
    if( output > _max )
        output = _max;
    else if( output < _min )
        output = _min;

    // Save error to previous error
    _pre_error = error;

    return output;
}

PIDImpl::~PIDImpl()
{
}

#endif

pid.h

#ifndef _PID_H_
#define _PID_H_

class PIDImpl;
class PID
{
    public:
        // Kp -  proportional gain
        // Ki -  Integral gain
        // Kd -  derivative gain
        // dt -  loop interval time
        // max - maximum value of manipulated variable
        // min - minimum value of manipulated variable
        PID( double dt, double max, double min, double Kp, double Kd, double Ki );

        // Returns the manipulated variable given a setpoint and current process value
        double calculate( double setpoint, double pv );
        ~PID();

    private:
        PIDImpl *pimpl;
};

#endif

pid_example.cpp

#include "pid.h"
#include <stdio.h>
int main() {

    PID pid = PID(0.1, 100, -100, 0.1, 0.01, 0.5);

    double val = 20;
    for (int i = 0; i < 100; i++) {
        double inc = pid.calculate(0, val);
        printf("val:% 7.3f inc:% 7.3f\n", val, inc);
        val += inc;
    }
    return 0;
}

Python代码

import matplotlib.pyplot as plt
class PID():
    def __init__(self,dt,max,min,Kp,Kd,Ki):
        self.dt=dt  #采样周期
        self.max=max #最大调整值
        self.min = min #最小调整值
        self.Kp = Kp #比例系数
        self.Kd = Kd #积分系数
        self.Ki =Ki #微分系数
        self.integral=0  #积分和
        self.previous_error=0 #上一次偏差
        self.error=0

    def calculate(self,setpoint,measured_value):
        self.error =setpoint-measured_value # 计算得到偏差
        P_out=self.Kp*self.error  #计算出比例值

        self.integral=self.integral+self.error*self.dt #计算得到积分累加和
        I_out=self.Ki*self.integral #计算出积分值


        derivative = (self.error-self.previous_error)/self.dt
        D_out = self.Kd*derivative #计算出微分值

        self.previous_error=self.error #保存上一次偏差

        output=(P_out+I_out+D_out)
        if output>self.max:
            output =self.max
        if output<self.min:
            output = self.min
        return output

list_x=list()
list_y=list()
testPID =PID(0.1, 100, -100, 0.1, 0.01, 0.5);
val=20
for point in range(0,200):
    inc =testPID.calculate(0,val)
    val += inc
    list_x.append(point)
    list_y.append(inc)

plt.scatter(list_x,list_y,alpha=0.6,edgecolors='white')
# plt.plot(list_x,list_y)
plt.legend()
plt.show()

仿真结果

输入:PID(0.1, 100, -100, 0.1, 0.01, 0.5)
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值