跳转至

PID 控制算法

在现实中对事物的控制不能达到完全精确. 如果使用开环的控制系统将导致误差不断积累, 最终达到某个阈值, 失去对事物的控制. 利用 PID 来构建一个闭环控制系统, 可以自动调节输出接近设定的目标值.

该算法由三部分组成, 分别是比例 (Proportional) 单元, 积分 (Integral) 单元和微分 (Derivative) 单元.

PID

注解:

  • \(K_p\): 比例调节参数.
  • \(K_i\): 积分调节参数.
  • \(K_d\): 微分调节参数.
  • \(e(t)\): 当前误差.

稳态误差 (Steady-state errors)

系统从一个稳态过渡到新的稳态, 或系统受扰动作用又重新平衡后, 系统出现的偏差.

参数

  • P: 比例单元.

    结果为误差与 \(K_p\) 的乘积. 误差越大调节力度越大, 快速减小误差; 误差越小调节力度越小.

  • I: 积分单元.

    结果为积分与 \(K_i\) 的乘积. 积分为误差与时间的乘积之和, 因此考虑到了不同时间段的误差. 若误差长期没有得到修正, 存在稳态误差, 积分会不断增加, 最终会使误差会不断接近零.

  • D: 微分单元.

    若误差缩小微分单元为负, 时间变化量越小, 整体的绝对值越大. 因此可以减少超调和振荡, 提高系统的响速度和稳定性.

通过下图可以直观的看出三种参数对整体调节效果的影响:

PID 动画

I 的值近似等于曲线与 \(y = 1\) 之间构成的面积.

调节参数

三个公式都包含各自的调节参数, 这三个调节参数分别控制三个部分计算的结果对控制的影响大小, 应根据实际应用将它们调整到最适合的值.
调试 PID 控制参数主要方式有:

实现

float kP = ..., kI = ..., kD = ...; // 这三个参数的值由用户指定
float target = ...;                 // 期望达到的值

struct PIDState
{
  float kP, kI, kD;
  float integral = 0.f;
  float prev_error;
};

float pid(float target, float actual, float dt, PIDState& state)
{
  const float error = target - actual;
  state.prev_error  = error;

  const float p   = state.kP * error;
  state.integral += state.kI * error * dt;
  const float d   = state.kD * (error - state.prev_error) / dt;

  return p + state.integral + d;
}

auto prev_timepoint = get_current_timepoint();
while(true)
{
  const auto timepoint = get_current_timepoint();
  const auto dt        = timepoint - prev_timepoint;
  prev_timepoint       = timepoint;

  const float actual = read(...); // 获取实际数据
  const float output = pid(target, actual, dt);
  write(output); // 进行调整
}

参考

评论