LOADING

加载过慢请开启缓存 浏览器默认开启

PID代码

2023/4/29 算法

Abstract: PID 控制的软件代码,提供 C 和 Python 的版本

两个版本的功能几乎一致。

C 语言版本

PID.c

#include <Arduino.h>
#include "PID.h"

/**
  * @brief  PID绝对值函数
  * @param  X
  * @retval X的绝对值
  */
float PID_abs(float X) {
  if (X >= 0) {
    return X;
  } else {
    return -X;
  }
}

/**
  * @brief  PID控制器初始化
  * @param  PID
  * @retval 无
  */
void PID_Init(PID_Typedef *PID) {
  PID->Kp = 0;
  PID->Ki = 0;
  PID->Kd = 0;

  PID->Pterm = 0;
  PID->Iterm = 0;
  PID->Dterm = 0;

  PID->Measure = 0;
  PID->Last_Mistake = 0;
  PID->Last_Measure = 0;
  PID->Target = 0;
  PID->Output = 0;
}

/**
  * @brief  PID限制器初始化
  * @param  PID_Limit
  * @retval 无
  */
void PID_Limit_Init(PID_LimitTypedef *PID_Limit) {
  PID_Limit->Dterm_Forward_On = 0;
  PID_Limit->Iterm_Max_On = 0;
  PID_Limit->Mistake_EnableIterm_On = 0;

  PID_Limit->Iterm_Max = 0;
  PID_Limit->Mistake_EnableIterm = 0;
  PID_Limit->Mistake_Deadband = 0;
}

/**
  * @brief  PID计算
  * @param  PID,PID_Limit
  * @retval 无
  */
void PID_Update(PID_Typedef *PID, PID_LimitTypedef *PID_Limit, float Feedback) {
  float Mistake = PID->Target - Feedback;
  if (PID_Limit->Mistake_Deadband_Enable != 0) {
    if (PID_abs(Mistake) < PID_abs(PID_Limit->Mistake_Deadband))  //误差在死区范围
    {
      Mistake = 0;
    }
  }
  PID->Last_Measure = PID->Measure;
  PID->Measure = Feedback;

  //P计算
  PID->Pterm = Mistake;

  //I计算
  if (PID_Limit->Mistake_EnableIterm_On != 0)  //启用积分分离
  {
    if (Mistake <= PID_Limit->Mistake_EnableIterm) {
      PID->Iterm += Mistake;
    }
  } else {
    PID->Iterm += Mistake;
  }
  if (PID_Limit->Iterm_Max_On != 0)  //启用积分限幅
  {
    if (PID->Iterm > PID_Limit->Iterm_Max) {
      PID->Iterm = PID_Limit->Iterm_Max;
    }
  }

  //D计算
  if (PID_Limit->Dterm_Forward_On != 0)  //启用微分先行
  {
    PID->Dterm = PID->Measure - PID->Last_Measure;
  } else {
    PID->Dterm = Mistake - PID->Last_Mistake;
  }

  PID->Last_Mistake = Mistake;  //记录误差

  PID->Output = PID->Pterm * PID->Kp + PID->Iterm * PID->Ki + PID->Dterm * PID->Kd;  //计算输出
}

PID.h

#ifndef __PID_H
typedef struct
{
    float Kp; //P参数
    float Ki; //I参数
    float Kd; //D参数
    
    float Pterm; //P计算值
    float Iterm; //I计算值
    float Dterm; //D计算值
    
    float Measure; //当前测量量
    float Last_Mistake; //上次误差
    float Last_Measure; //上次测量量
    float Target; //目标值
    float Output;
} PID_Typedef;

typedef struct
{
    uint8_t Dterm_Forward_On; //微分先行,非0表示启用
    uint8_t Iterm_Max_On; //积分限幅,非0表示启用
    uint8_t Mistake_EnableIterm_On; //积分分离,非0表示启用
    uint8_t Mistake_Deadband_Enable; //误差死区,非0表示启用
    
    float Iterm_Max; //积分最大值
    float Mistake_EnableIterm; //允许积分的误差值
    float Mistake_Deadband; //误差死区
} PID_LimitTypedef;


void PID_Init(PID_Typedef *PID);
void PID_Limit_Init(PID_LimitTypedef *PID_Limit);
void PID_Update(PID_Typedef *PID, PID_LimitTypedef *PID_Limit, float Feedback);

#endif

用在 arduino 上需要更改 PID.h

#include <Arduino.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef __PID_H
  typedef struct
  {
    float Kp;  //P参数
    float Ki;  //I参数
    float Kd;  //D参数

    float Pterm;  //P计算值
    float Iterm;  //I计算值
    float Dterm;  //D计算值

    float Measure;       //当前测量量
    float Last_Mistake;  //上次误差
    float Last_Measure;  //上次测量量
    float Target;        //目标值
    float Output;
  } PID_Typedef;

  typedef struct
  {
    uint8_t Dterm_Forward_On;         //微分先行,非0表示启用
    uint8_t Iterm_Max_On;             //积分限幅,非0表示启用
    uint8_t Mistake_EnableIterm_On;   //积分分离,非0表示启用
    uint8_t Mistake_Deadband_Enable;  //误差死区,非0表示启用

    float Iterm_Max;            //积分最大值
    float Mistake_EnableIterm;  //允许积分的误差值
    float Mistake_Deadband;     //误差死区
  } PID_LimitTypedef;


  void PID_Init(PID_Typedef *PID);
  void PID_Limit_Init(PID_LimitTypedef *PID_Limit);
  void PID_Update(PID_Typedef *PID, PID_LimitTypedef *PID_Limit, float Feedback);
#endif

#ifdef __cplusplus
}
#endif

Python 版本

位置式

PID.py

class PosPIDClass:
    """位置式PID类"""

    def __init__(self, kp=0.1, ki=0, kd=0):
        """初始化"""
        self.__Kp = kp  # P参数
        self.__Ki = ki  # I参数
        self.__Kd = kd  # D参数

        self.__Iterm_max = None  # Iterm最大值,用于积分限幅,非int或float表示不限制
        self.__mistake_enableIterm = None  # 启用I调节的误差最大值,用于积分分离,非int或float表示不限制
        self.__Dterm_forward = False  # 微分先行,False表示不启用
        self.__mistake_deadband = None  # 误差死区

        self.__Pterm = 0  # P调节数值
        self.__Iterm = 0  # I调节数值
        self.__Dterm = 0  # D调节数值
        self.__output = 0  # 输出量

        self.__measurement = 0  # 测量量
        self.__last_mistake = 0  # 上一次的误差
        self.__last_measurement = 0  # 上一次的测量值
        self.__target = 0  # 目标值

    def __str__(self):
        return "Positional PIDClass"

    def setKp(self, kp):
        """设置P参数"""
        self.__Kp = kp

    def setKi(self, ki):
        """设置I参数"""
        self.__Ki = ki

    def setKd(self, kd):
        """设置D参数"""
        self.__Kd = kd

    def setTarget(self, target):
        """设置目标值"""
        self.__target = target

    def setLimitForIterm(self, max_value, mistake_enable_value):
        """设置I计算的限制参数"""
        self.__Iterm_max = max_value
        self.__mistake_enableIterm = mistake_enable_value

    def setDtermForward(self, state):
        """微分先行设置"""
        if isinstance(state, bool):
            self.__Dterm_forward = state

    def setMistakeDeadband(self, daedband):
        """误差死区设置"""
        self.__mistake_deadband = daedband

    def getPterm(self):
        """获取P计算值"""
        return self.__Pterm

    def getIterm(self):
        """获取I计算值"""
        return self.__Iterm

    def getDterm(self):
        """获取D计算值"""
        return self.__Dterm

    def update(self, feedback):
        """
        迭代计算
        在不考虑积分限幅、积分分离和微分先行时,公式为:
        $$U_n = K_P{\times}E_n + K_I{\times}{\sum_{i=1}^{n}E_i} + K_D{\times}(E_n-E_{n-1})$$
        """
        mistake = self.__target - feedback
        if isinstance(self.__mistake_deadband, int) or isinstance(self.__mistake_deadband, float):
            if abs(mistake) < self.__mistake_deadband:  # 误差在死区,则认为无误差
                mistake = 0
        self.__last_measurement = self.__measurement
        self.__measurement = feedback

        # P计算
        self.__Pterm = mistake

        # I计算
        if isinstance(self.__mistake_enableIterm, int) or isinstance(self.__mistake_enableIterm, float):  # 启用积分分离
            if mistake <= self.__mistake_enableIterm:  # 误差不大于限制值
                self.__Iterm += mistake
        else:
            self.__Iterm += mistake
        if isinstance(self.__Iterm_max, int) or isinstance(self.__Iterm_max, float):  # 启用积分限幅
            self.__Iterm = min(self.__Iterm_max, self.__Iterm)

        # D计算
        if self.__Dterm_forward:  # 启用微分先行
            self.__Dterm = self.__measurement - self.__last_measurement
        else:
            self.__Dterm = mistake - self.__last_mistake

        self.__last_mistake = mistake

        self.__output = self.__Pterm * self.__Kp + self.__Iterm * self.__Ki + self.__Dterm * self.__Kd

    def output(self):
        """获取最终结果"""
        return self.__output

增量式

class IncPIDClass:
    """增量式PID类"""

    def __init__(self, kp=0.1, ki=0, kd=0):
        """初始化"""
        self.__Kp = kp  # P参数
        self.__Ki = ki  # I参数
        self.__Kd = kd  # D参数

        self.__mistake_enableIterm = None  # 启用I调节的误差最大值,用于积分分离,非int或float表示不限制
        self.__mistake_deadband = None  # 误差死区

        self.__Pterm = 0  # P调节数值
        self.__Iterm = 0  # I调节数值
        self.__Dterm = 0  # D调节数值
        self.__output = 0  # 输出量

        self.__mistake = 0  # 误差
        self.__last_mistake = 0  # 上一次的误差
        self.__double_last_mistake = 0  # 上上次的测量值
        self.__target = 0  # 目标值

    def __str__(self):
        return "Increasing PIDClass"

    def setKp(self, kp):
        """设置P参数"""
        self.__Kp = kp

    def setKi(self, ki):
        """设置I参数"""
        self.__Ki = ki

    def setKd(self, kd):
        """设置D参数"""
        self.__Kd = kd

    def setTarget(self, target):
        """设置目标值"""
        self.__target = target

    def setMistakeDeadband(self, daedband):
        """误差死区设置"""
        self.__mistake_deadband = daedband

    def getPterm(self):
        """获取P计算值"""
        return self.__Pterm

    def getIterm(self):
        """获取I计算值"""
        return self.__Iterm

    def getDterm(self):
        """获取D计算值"""
        return self.__Dterm

    def update(self, feedback):
        """
        迭代计算
        $$\Delta u_k = u_k - u_{k-1} =
        K_p \times (e_k-e_{k-1}) + K_i \times e_k + K_d \times (e_k - 2e_{k-1} + e_{k-2})$$
        """
        mistake = self.__target - feedback

        self.__double_last_mistake = self.__last_mistake  # 误差记录更新
        self.__last_mistake = self.__mistake
        self.__mistake = mistake

        self.__Pterm = self.__mistake - self.__last_mistake  # P计算

        if isinstance(self.__mistake_enableIterm, int) or isinstance(self.__mistake_enableIterm, float):  # 启用积分分离
            if mistake <= self.__mistake_enableIterm:  # 误差不大于限制值
                self.__Iterm = mistake  # I计算
        else:
            self.__Iterm = mistake

        self.__Dterm = self.__mistake - 2 * self.__last_mistake + self.__double_last_mistake  # D计算

        self.__output = self.__Kp * self.__Pterm + self.__Ki * self.__Iterm + self.__Kd * self.__Dterm  # 计算输出值

    def output(self):
        """获取最终结果"""
        return self.__output