2、连续域与离散域:模拟PID与数字PID的区别,采样时间的重要性
好,咱们接着聊。上一章我们把PID的三大参数——比例、积分、微分——掰开揉碎讲了一遍。这一章,我想聊聊一个更底层的问题:模拟PID和数字PID,到底差在哪?
你可能觉得,不就是把模拟电路换成单片机代码嘛,能有多大区别?
嗯,我当年刚入行时也这么想。直到有一次,我在一个温控项目里,直接把模拟PID的公式照搬到STM32上,结果系统抖得像筛糠一样。折腾了两天,最后发现——问题出在采样时间上。
这一章,我就把这段经历里的教训,掰开揉碎讲给你听。
2.1 模拟PID:连续域里的“理想国”
先说说模拟PID。它用运算放大器、电阻、电容搭出来,信号是连续的电压或电流。
说白了,模拟PID是实时、无延迟的。输入一变,输出立刻跟着变。微分项看的是“瞬间变化率”,积分项是“无间断累加”。
它的数学表达式长这样:
u(t) = Kp * e(t) + Ki * ∫e(t)dt + Kd * de(t)/dt
你看,这里用的是微分方程,时间是连续的。dt可以无限小,理论上精度可以做到极高。
但现实世界没有理想国。模拟PID有几个硬伤:
- 参数难调:换个电阻就得重新算,调试全靠示波器加手拧电位器
- 温漂严重:温度一变,运放参数就飘,PID参数也跟着变
- 功能单一:想加个抗积分饱和?得额外搭一堆电路
所以,现在除了极少数高频场合(比如开关电源的电流环),大部分场景都被数字PID取代了。
2.2 数字PID:离散域里的“现实派”
数字PID就不一样了。它跑在MCU或DSP里,信号是离散的采样点。
你想想看,MCU不可能连续不断地计算。它只能每隔一段时间(比如1ms)读一次反馈值,算一次输出。这个“每隔一段时间”,就是采样时间Ts。
数字PID的表达式变成了这样:
u(k) = Kp * e(k) + Ki * Σe(i)*Ts + Kd * (e(k) - e(k-1)) / Ts
注意看,积分变成了累加求和,微分变成了差分。这就是离散化的核心。
我在项目中遇到过最典型的坑是:采样时间选错了,整个系统就废了。
核心区别一句话总结:
模拟PID是“无时无刻都在算”,数字PID是“每隔Ts算一次”。
这个Ts,决定了数字PID的“分辨率”和“实时性”。
2.3 采样时间:数字PID的“命门”
采样时间Ts有多重要?我这么说吧——它直接决定了你的PID能不能用。
为什么?因为数字PID的积分和微分,都依赖于Ts的准确性。
咱们拆开来看:
2.3.1 积分项:Ts影响累加精度
模拟积分是连续累加,数字积分是矩形法近似。Ts越小,近似越准。
如果你Ts设得太大,积分项会变得很粗糙。比如温度控制,采样周期1秒和10秒,积分效果天差地别。
2.3.2 微分项:Ts放大噪声
这个更关键。数字微分的公式是:
微分项 = Kd * (e(k) - e(k-1)) / Ts
你看,Ts在分母上。Ts越小,微分项对误差变化越敏感。但问题是——传感器噪声也会被放大。
我曾经在一个电机速度控制项目里,把采样时间设成了0.1ms。结果微分项疯狂跳动,电机嗡嗡响。后来把Ts改成1ms,再配合一阶低通滤波,才稳定下来。
避坑指南:
我曾经因为采样时间不固定,导致积分项越积越偏。后来养成一个习惯:用定时器中断固定采样周期,绝不用主循环里的延时。
记住:采样时间必须恒定,否则PID参数全白调。
2.4 如何选择采样时间?
这个问题没有标准答案,但我可以给你几条实战经验:
| 应用场景 | 推荐采样时间 | 说明 |
|---|---|---|
| 温度控制(慢速) | 0.5s ~ 5s | 热惯性大,采样太快反而浪费算力 |
| 电机速度(中速) | 1ms ~ 10ms | 机械时间常数在几十毫秒量级 |
| 电流环(高速) | 10μs ~ 100μs | 需要专用DSP或FPGA,MCU一般扛不住 |
| 无人机姿态(中高速) | 1ms ~ 4ms | IMU更新率通常1kHz左右 |
我个人习惯是:先按系统带宽的1/10到1/20来选Ts。比如系统响应频率是10Hz,那采样时间就取0.05s到0.1s。然后根据实际效果微调。
小技巧:
如果你不确定Ts选多少,可以先用一个较大的值(比如10ms),然后逐步减小。当系统性能不再明显改善时,就说明Ts已经够小了。
别一上来就追求极致的小Ts,算力、噪声、成本都要权衡。
2.5 从模拟到数字:一个简单的转换示例
最后,我给你看一个实际转换的例子。假设我们有一个模拟PID:
u(t) = 2 * e(t) + 0.5 * ∫e(t)dt + 0.1 * de(t)/dt
现在要转成数字PID,采样时间Ts = 0.01s。
第一步:计算离散化参数
Kp = 2
Ki = 0.5 * Ts = 0.5 * 0.01 = 0.005
Kd = 0.1 / Ts = 0.1 / 0.01 = 10
第二步:写出位置式PID代码
float pid_update(float setpoint, float feedback) {
float error = setpoint - feedback;
static float integral = 0;
static float last_error = 0;
integral += error * Ts; // 注意这里乘了Ts
float derivative = (error - last_error) / Ts;
last_error = error;
return Kp * error + Ki * integral + Kd * derivative;
}
你看,代码很简单。但这里有个细节:Ki和Kd的值,是依赖于Ts的。如果你换了采样时间,必须重新计算Ki和Kd。
嗯,这一点很多人会忽略。他们直接从模拟PID抄参数,结果系统完全不对。
2.6 本章小结
这一章我们聊了:
- 模拟PID是连续域,数字PID是离散域
- 采样时间Ts是数字PID的核心参数
- Ts影响积分精度和微分噪声
- 选择Ts要权衡系统带宽、算力和噪声
- 离散化时,Ki和Kd必须根据Ts重新计算
下一章,我们会深入讲位置式PID和增量式PID的区别。这两种实现方式,在实际项目里各有各的坑。到时候我再给你讲讲我当年是怎么在增量式PID上栽跟头的。
好,今天就到这儿。有问题随时交流。