常見的PID的演算法及程式碼示例

琨为玉也發表於2024-08-05

常見的PID的演算法及程式碼示例

PID(比例-積分-微分)演算法是控制系統中常用的一種反饋控制演算法,它透過計算誤差的比例、積分和微分來調整控制輸入,以達到預定的控制目標。以下是一些常見的PID演算法及程式碼示例:

一、常見的PID演算法

  1. 位置式PID演算法
    • 位置式PID演算法直接計算控制量的絕對值,每次輸出都與過去的狀態有關,因此計算量相對較大。其計算公式為:u(k)=Kpe(k)+K**ij=0k**e(j)+K**d[e(k)−e(k−1)]
    • 其中,u(k) 是第 k 次的控制量,e(k) 是第 k 次的誤差,K**pK**iK**d 分別是比例、積分、微分系數。
  2. 增量式PID演算法
    • 增量式PID演算法只計算控制量的增量,即本次控制量與上一次控制量之差,因此計算量相對較小,且更易於實現無擾動切換。其計算公式為:Δu(k)=K**p[e(k)−e(k−1)]+Kie(k)+K**d[e(k)−2e(k−1)+e(k−2)]
    • 實際應用中,控制量 u(k) 可透過累加增量 Δu(k) 來獲得。

二、程式碼示例

pid.c

#include "stm32f10x.h"
#include <math.h>
#include "sys.h"
#include "pid.h"
#define PID_LIMIT_MIN -10000		//PID輸出最低值
#define PID_LIMIT_MAX 10000	//PID輸出最大值
//注意:PID結構體必須定義為全域性變數或靜態變數,然後在函式中給KP,KI,KD賦值
/************************取樣週期未知且不變************************************/
//位置式PID
//pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
//setvalue : 設定值(期望值)
//actualvalue: 實際值
//由於全量輸出,每次輸出均與過去狀態有關,計算時要對ek累加,計算量大
float PID_location(float setvalue, float actualvalue, PID_LocTypeDef *PID)
{ 
	PID->ek =setvalue-actualvalue;
	PID->location_sum += PID->ek;                         //計算累計誤差值
	if((PID->ki!=0)&&(PID->location_sum>(PID_LIMIT_MAX/PID->ki))) PID->location_sum=PID_LIMIT_MAX/PID->ki;
	if((PID->ki!=0)&&(PID->location_sum<(PID_LIMIT_MIN/PID->ki))) PID->location_sum=PID_LIMIT_MIN/PID->ki;//積分限幅
	
  PID->out=PID->kp*PID->ek+(PID->ki*PID->location_sum)+PID->kd*(PID->ek-PID->ek1);
  PID->ek1 = PID->ek;  
	if(PID->out<PID_LIMIT_MIN)	PID->out=PID_LIMIT_MIN;
	if(PID->out>PID_LIMIT_MAX)	PID->out=PID_LIMIT_MAX;//PID->out限幅
	
	return PID->out;
}
//增量式PID
//pidout+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
//setvalue : 設定值(期望值)
//actualvalue: 實際值
float PID_increment(float setvalue, float actualvalue, PID_LocTypeDef *PID)
{                                
	PID->ek =setvalue-actualvalue;
  PID->out+=PID->kp*(PID->ek-PID->ek1)+PID->ki*PID->ek+PID->kd*(PID->ek-2*PID->ek1+PID->ek2);
//	PID->out+=PID->kp*PID->ek-PID->ki*PID->ek1+PID->kd*PID->ek2;
  PID->ek2 = PID->ek1;
  PID->ek1 = PID->ek;  
		
	if(PID->out<PID_LIMIT_MIN)	PID->out=PID_LIMIT_MIN;
	if(PID->out>PID_LIMIT_MAX)	PID->out=PID_LIMIT_MAX;//限幅
	
	return PID->out;
}
 

pid.h

#ifndef __PID_H
#define __PID_H
 
#include "stm32f10x.h"
#include <math.h>
#include "sys.h"
 
typedef struct
{
  float kp;                       //比例係數Proportional
  float ki;                       //積分系數Integral
  float kd;                       //微分系數Derivative
//	float ti;                       //積分時間常數
//  float td;                       //微分時間常數
//	float period;										//取樣週期
  float ek;                       //當前誤差
  float ek1;                      //前一次誤差e(k-1)
  float ek2;                      //再前一次誤差e(k-2)
  float location_sum;             //累計積分位置
	float out;											//PID輸出值
}PID_LocTypeDef;
float PID_location(float setvalue, float actualvalue, PID_LocTypeDef *PID);
float PID_increment(float setvalue, float actualvalue, PID_LocTypeDef *PID);
#endif

注意:上述程式碼,在實際應用中,可能需要使用作業系統的定時器或中斷來實現。

相關文章