为什么 PID 需要手动/自动模式
在调试 PID 算法时,我们偶尔需要强制系统输出某个特定值。最直接的做法是忽略计算结果,直接覆盖输出。虽然这看似简单,但实际工程中是个隐患。
想象一下,你让 PID 控制器辛苦计算,算出的结果却被你强行置零。这种行为会让控制器产生'积分饱和'现象:它以为自己在努力调节,实际上输出没变,于是它会不断增大误差积分项(ITerm),试图推动输出。当你停止覆写并切回 PID 控制时,巨大的积分项会导致输出瞬间飙升,造成系统震荡甚至损坏设备。

解决方案:模式切换
解决这个问题的标准做法是引入状态标志,区分'手动'和'自动'模式。手动模式下,由外部指定输出;自动模式下,由 PID 算法接管。通过这种方式,我们既能保留 PID 的内部逻辑,又能安全地切换控制权。
下面是一个基于 C++ 的实现示例,核心在于 Compute 函数中的模式判断:
/* 工作变量 */
unsigned long lastTime;
double Input, Output, Setpoint;
double ITerm, lastInput;
double kp, ki, kd;
int SampleTime = 1000; // 采样周期 1 秒
double outMin, outMax;
bool inAuto = false; // 模式标志:false=手动,true=自动
#define MANUAL 0
#define AUTOMATIC 1
void Compute() {
// 关键:如果不在自动模式,直接返回,不更新任何内部变量
if(!inAuto) return;
unsigned long now = millis();
int timeChange = (now - lastTime);
if(timeChange >= SampleTime) {
/* 计算所有误差变量 */
double error = Setpoint - Input;
ITerm += (ki * error);
/* 积分限幅 */
if(ITerm > outMax) ITerm = outMax;
else (ITerm < outMin) ITerm = outMin;
dInput = (Input - lastInput);
Output = kp * error + ITerm - kd * dInput;
(Output > outMax) Output = outMax;
(Output < outMin) Output = outMin;
lastInput = Input;
lastTime = now;
}
}
{
SampleTimeInSec = (()SampleTime)/;
kp = Kp;
ki = Ki * SampleTimeInSec;
kd = Kd / SampleTimeInSec;
}
{
(NewSampleTime > ) {
ratio = ()NewSampleTime / ()SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = ( )NewSampleTime;
}
}
{
(Min > Max) ;
outMin = Min;
outMax = Max;
(Output > outMax) Output = outMax;
(Output < outMin) Output = outMin;
(ITerm > outMax) ITerm = outMax;
(ITerm < outMin) ITerm = outMin;
}
{
inAuto = (Mode == AUTOMATIC);
}


