1理论推导
1.1PID
式中:
- ——控制器的输出;
- ——控制器的输入(常常是设定值与被控量之差);
- Kp——控制器的比例放大系数;
- Ti——控制器的积分时间;
- Td——控制器的微分时间;
1.2位置式PID
设为第k次采样时刻控制器的输出值,可得离散的PID算式,又称位置式PID算式:
- e(k) : 用户设定的值(目标值) - 控制对象的当前的状态值
- Kp : e(k)
- Ki : ∑e(i) 误差的累加
- Kd : e(k) - e(k-1) 这次误差-上次误差
1.3增量式PID
由离散的PID算式可得:
其中:
- e(k) : 用户设定的值(目标值) - 控制对象的当前的状态值
- Kp : e(k)-e(k-1) 这次误差-上次误差
- Ki : e(k) 误差
- Kd : e(k) - 2e(k-1)+e(k-2) 这次误差-2*上次误差+上上次误差
1.4位置式与增量式PID控制器优缺点
1.4.1位置式PID控制器
优点:
-
高精度:适用于需要高精度控制的系统。
-
稳定性好:位置式控制通常能提供良好的稳定性。
缺点:
-
计算量大:因为需要对误差进行累加,计算复杂且资源需求较高。
-
故障影响大:如果计算机出现故障,控制器输出的大幅度变化会引起执行机构位置的大幅度变化。
1.4.2增量式PID控制器
优点:
-
响应速度快:计算过程简单,仅与最近3次的采样值有关。
-
故障影响小:每次只输出控制增量,发生故障时影响较小,对生产过程影响不大。
缺点:
-
精度较低:相较位置式PID,可能无法达到同样的高精度。
-
稳定性差:在某些情况下稳定性可能不如位置式PID。
2编程实现
2.1PID.h
#ifndef PID_H
#define PID_H
#define LimitMax(input, max) \
{ \
if (input > max) \
{ \
input = max; \
} \
else if (input < -max) \
{ \
input = -max; \
} \
}
struct PID_FUNCTION
{
int mode;
//PID 三参数
float Kp;
float Ki;
float Kd;
float max_out; //最大输出
float max_iout; //最大积分输出
float deadband; //死区
float set;
float fdb;
float out;
float Pout;
float Iout;
float Dout;
float Dbuf[3]; //微分项 0最新 1上一次 2上上次
float error[3]; //误差项 0最新 1上一次 2上上次
};
enum PID_MODE
{
PID_POSITION,
PID_DELTA
};
float PID_calc(PID_FUNCTION *pid, float ref, float set);
void PID_init(PID_FUNCTION *pid, int mode, const float PID[3], float max_out, float max_iout, float deadband);
void PID_clear(PID_FUNCTION *pid);
#endif //PID_H
2.2PID.cpp
#include "PID.h"
#include "math.h"
void PID_init(PID_FUNCTION *pid, int mode, const float PID[3], float max_out, float max_iout, float deadband)
{
if (pid == nullptr || PID == nullptr)
{
return;
}
pid->mode = mode;
pid->Kp = PID[0];
pid->Ki = PID[1];
pid->Kd = PID[2];
pid->max_out = max_out;
pid->max_iout = max_iout;
pid->deadband = deadband;
pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
pid->error[0] = pid->error[1] = pid->error[2] = pid->Pout = pid->Iout = pid->Dout = pid->out = 0.0f;
}
float PID_calc(PID_FUNCTION *pid, float ref, float set)
{
pid->error[2] = pid->error[1];
pid->error[1] = pid->error[0];
pid->set = set;
pid->fdb = ref;
pid->error[0] = set - ref;
if (fabsf(pid->error[0]) <= pid->deadband)
pid->error[0] = 0.0f;
if (pid->mode == PID_POSITION)
{
pid->Pout = pid->Kp * pid->error[0];
pid->Iout += pid->Ki * pid->error[0];
pid->Dbuf[2] = pid->Dbuf[1];
pid->Dbuf[1] = pid->Dbuf[0];
pid->Dbuf[0] = (pid->error[0] - pid->error[1]);
pid->Dout = pid->Kd * pid->Dbuf[0];
LimitMax(pid->Iout, pid->max_iout);
pid->out = pid->Pout + pid->Iout + pid->Dout;
LimitMax(pid->out, pid->max_out);
}
else if (pid->mode == PID_DELTA)
{
pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]);
pid->Iout = pid->Ki * pid->error[0];
pid->Dbuf[2] = pid->Dbuf[1];
pid->Dbuf[1] = pid->Dbuf[0];
pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]);
pid->Dout = pid->Kd * pid->Dbuf[0];
pid->out += pid->Pout + pid->Iout + pid->Dout;
LimitMax(pid->out, pid->max_out);
}
return pid->out;
}
void PID_clear(PID_FUNCTION *pid)
{
if (pid == nullptr)
{
return;
}
pid->error[0] = pid->error[1] = pid->error[2] = 0.0f;
pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
pid->out = pid->Pout = pid->Iout = pid->Dout = 0.0f;
pid->fdb = pid->set = 0.0f;
}
2.3使用范例
PID_FUNCTION PID_Y;
static float pid_y[3] = {1.2, 0.0, 1}; //kp、ki、kd参数
PID_init(&PID_Y, PID_POSITION, pid_y, 2.5, 0, 0.02);//放到函数里初始化
vy = PID_calc(&PID_Y,now_y, target_y);//放到函数里计算