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上栽跟头的。

好,今天就到这儿。有问题随时交流。