什么是PID控制?
PID(Proportional-Integral-Derivative)控制是工业控制中最常用的控制算法,由比例、积分、微分三个环节组成。
为什么需要PID?
想象你在开车,需要保持车速100km/h:
- 看到速度是90km/h,踩油门(比例控制)
- 速度长期达不到100,继续加油(积分控制)
- 速度上升太快,提前收油门(微分控制)
这就是PID的基本思想。
PID数学原理
控制公式
$$
u(t) = K_p \cdot e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt}
$$
其中:
- $u(t)$:控制输出
- $e(t)$:误差(目标值 - 当前值)
- $K_p, K_i, K_d$:PID三个参数
离散化形式
实际编程中使用离散形式:
$$
u_k = K_p \cdot e_k + K_i \sum_{i=0}^k e_i \cdot \Delta t + K_d \frac{e_k - e_{k-1}}{\Delta t}
$$
三个环节详解
P - 比例控制
作用:根据当前误差大小,按比例产生控制作用。
1 2
| error = target - current output_p = Kp * error
|
特点:
- ✅ 响应快速
- ❌ 存在稳态误差
- ❌ Kp过大会振荡
I - 积分控制
作用:累积历史误差,消除稳态误差。
1 2
| error_sum += error * dt output_i = Ki * error_sum
|
特点:
- ✅ 消除稳态误差
- ❌ 可能导致积分饱和
- ❌ 响应变慢
D - 微分控制
作用:根据误差变化率,预测趋势,提前调整。
1 2
| error_rate = (error - last_error) / dt output_d = Kd * error_rate
|
特点:
完整PID实现
标准PID
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
| class PID: def __init__(self, Kp, Ki, Kd, setpoint=0): self.Kp = Kp self.Ki = Ki self.Kd = Kd self.setpoint = setpoint self.last_error = 0 self.integral = 0 self.last_time = time.time() def update(self, current_value): current_time = time.time() dt = current_time - self.last_time error = self.setpoint - current_value P = self.Kp * error self.integral += error * dt I = self.Ki * self.integral derivative = (error - self.last_error) / dt if dt > 0 else 0 D = self.Kd * derivative output = P + I + D self.last_error = error self.last_time = current_time return output def reset(self): self.integral = 0 self.last_error = 0
|
增量式PID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class IncrementalPID: def __init__(self, Kp, Ki, Kd): self.Kp = Kp self.Ki = Ki self.Kd = Kd self.last_error = 0 self.prev_error = 0 def update(self, error): delta_output = ( self.Kp * (error - self.last_error) + self.Ki * error + self.Kd * (error - 2 * self.last_error + self.prev_error) ) self.prev_error = self.last_error self.last_error = error return delta_output
|
PID改进技巧
1. 积分限幅
防止积分饱和:
1 2 3 4 5 6 7 8 9
| def update(self, current_value): self.integral += error * dt self.integral = max(min(self.integral, self.integral_max), -self.integral_max) I = self.Ki * self.integral
|
2. 微分滤波
降低噪声影响:
1 2 3 4 5
| alpha = 0.1 filtered_derivative = alpha * derivative + (1 - alpha) * self.last_derivative self.last_derivative = filtered_derivative D = self.Kd * filtered_derivative
|
3. 抗积分饱和
1 2 3
| if abs(error) < error_threshold: self.integral += error * dt
|
4. 输出限幅
1
| output = max(min(output, output_max), output_min)
|
5. 变速积分
误差大时减小积分,误差小时增大积分:
1 2 3 4
| coefficient = 1.0 if abs(error) > threshold: coefficient = 0.5 I = self.Ki * self.integral * coefficient
|
参数整定方法
经验法(适合入门)
先调P:
- 从小开始增大Kp
- 直到系统出现轻微振荡
- 然后减小到稳定
再调D:
最后调I:
Ziegler-Nichols法
- 设Ki=0, Kd=0
- 增大Kp直到系统临界振荡,记录Ku和Tu
- 按表格设置参数:
| 控制器类型 |
Kp |
Ki |
Kd |
| P |
0.5*Ku |
- |
- |
| PI |
0.45*Ku |
0.54*Ku/Tu |
- |
| PID |
0.6*Ku |
1.2*Ku/Tu |
0.075KuTu |
试凑法口诀
1 2 3 4 5 6 7 8 9
| 参数整定找最佳,从小到大顺序查 先是比例后积分,最后再把微分加 曲线振荡很频繁,比例度盘要放大 曲线漂浮绕大弯,比例度盘往小扳 曲线偏离回复慢,积分时间往下降 曲线波动周期长,积分时间再加长 曲线振荡频率快,先把微分降下来 动差大来波动慢,微分时间应加长 理想曲线两个波,前高后低4比1
|
实战案例
案例1:温度控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pid = PID(Kp=2.0, Ki=0.5, Kd=0.1, setpoint=200)
while True: current_temp = read_temperature() heater_power = pid.update(current_temp) heater_power = max(0, min(100, heater_power)) set_heater(heater_power) time.sleep(0.1)
|
案例2:电机速度控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class MotorPID: def __init__(self): self.pid = PID(Kp=0.8, Ki=0.2, Kd=0.05) self.pid.integral_max = 100 self.output_max = 255 def control(self, target_rpm, current_rpm): self.pid.setpoint = target_rpm pwm = self.pid.update(current_rpm) pwm = max(-self.output_max, min(self.output_max, pwm)) if pwm > 0: motor.forward(abs(pwm)) else: motor.backward(abs(pwm)) return pwm
|
案例3:机器人云台控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class GimbalController: def __init__(self): self.pitch_pid = PID(Kp=1.2, Ki=0.1, Kd=0.3) self.yaw_pid = PID(Kp=1.5, Ki=0.15, Kd=0.4) def track_target(self, target_x, target_y): center_x, center_y = 320, 240 error_x = target_x - center_x error_y = target_y - center_y yaw_output = self.yaw_pid.update(error_x) pitch_output = self.pitch_pid.update(error_y) gimbal.set_yaw(yaw_output) gimbal.set_pitch(pitch_output)
|
调试技巧
1. 可视化曲线
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
| import matplotlib.pyplot as plt
targets = [] actuals = [] outputs = []
plt.ion() fig, (ax1, ax2) = plt.subplots(2, 1)
while running: output = pid.update(current) targets.append(pid.setpoint) actuals.append(current) outputs.append(output) ax1.clear() ax1.plot(targets, 'r--', label='Target') ax1.plot(actuals, 'b-', label='Actual') ax1.legend() ax2.clear() ax2.plot(outputs, 'g-', label='Output') ax2.legend() plt.pause(0.01)
|
2. 分环节测试
1 2 3 4 5 6 7 8
| pid = PID(Kp=1.0, Ki=0, Kd=0)
pid.Ki = 0.1
pid.Kd = 0.05
|
3. 阶跃响应测试
1 2 3 4
| pid.setpoint = 100 time.sleep(5) pid.setpoint = 150
|
常见问题
1. 系统振荡
- 原因:Kp过大或Kd过小
- 解决:减小Kp,增大Kd
2. 响应慢
3. 有稳态误差
4. 超调严重
总结
PID控制简单实用,但参数整定需要经验。建议:
- 理解三个环节的作用
- 从简单系统开始练习
- 记录参数与效果的关系
- 多实践,积累经验
推荐资源: