連續PID的離散化及程式碼實現

2020-08-09 10:30:06

在这里插入图片描述

1. 公式

在这里插入图片描述
比例項可以讓系統儘快達到一個小的誤差範圍,積分項對比例項作用產生的誤差進行積分,也就是放大產生的誤差,讓其更快達到目標,但這時會導致系統過沖量較大,微分項的引入會抑制過沖。

作用:

  • P:加快調節速度
  • I:減小誤差,從而消除靜差
  • D:改善系統的動態效能

進行離散化處理

假設系統採樣時間爲t△te(t)e(t)將序列化爲:
在这里插入图片描述
將輸出u(t)u(t)序列化得到:
在这里插入图片描述

  • 比例項:
    在这里插入图片描述

  • 積分項:
    在这里插入图片描述

  • 微分項:
    在这里插入图片描述
    因此,連續PID式變爲:
    在这里插入图片描述
    上式即爲位置式PID

實際工程中,常用增量式PID
在这里插入图片描述

即:
在这里插入图片描述


2. 演算法虛擬碼

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

3. 位置式PID演算法的C++實現

標頭檔案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.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
測試程式碼
#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;
}

4. 位置式PID演算法Python版

class PositionalPID:
    def __init__(self, P, I, D):
        self.Kp = P
        self.Ki = I
        self.Kd = D

        self.SystemOutput = 0.0#系統輸出值
        self.ResultValueBack = 0.0
        self.PidOutput = 0.0#PID控制器輸出
        self.PIDErrADD = 0.0
        self.ErrBack = 0.0

    #設定一階慣性環節系統  其中InertiaTime爲慣性時間常數
    def SetInertiaTime(self, InertiaTime,SampleTime):
       self.SystemOutput = (InertiaTime * self.ResultValueBack + SampleTime * self.PidOutput) / (SampleTime + InertiaTime)
       self.ResultValueBack = self.SystemOutput

    #設定PID控制器參數
    def SetStepSignal(self,StepSignal):
        Err = StepSignal - self.SystemOutput
        KpWork = self.Kp * Err
        KiWork = self.Ki * self.PIDErrADD
        KdWork = self.Kd * (Err - self.ErrBack)
        self.PidOutput = KpWork + KiWork + KdWork
        self.PIDErrADD += Err