Abstract: PID 控制的软件代码,提供 C 和 Python 的版本
两个版本的功能几乎一致。
C 语言版本
PID.c
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| #include <Arduino.h> #include "PID.h"
float PID_abs(float X) { if (X >= 0) { return X; } else { return -X; } }
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; }
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; }
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;
PID->Pterm = Mistake;
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; } }
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
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
| #ifndef __PID_H typedef struct { float Kp; float Ki; float Kd; float Pterm; float Iterm; float Dterm; float Measure; float Last_Mistake; float Last_Measure; float Target; float Output; } PID_Typedef;
typedef struct { uint8_t Dterm_Forward_On; uint8_t Iterm_Max_On; uint8_t Mistake_EnableIterm_On; uint8_t Mistake_Deadband_Enable; 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
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
| #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
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| class PosPIDClass: """位置式PID类"""
def __init__(self, kp=0.1, ki=0, kd=0): """初始化""" self.__Kp = kp self.__Ki = ki self.__Kd = kd
self.__Iterm_max = None self.__mistake_enableIterm = None self.__Dterm_forward = False self.__mistake_deadband = None
self.__Pterm = 0 self.__Iterm = 0 self.__Dterm = 0 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
self.__Pterm = mistake
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)
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