ESP32 温度控制:手把手从接线到 PID 调稳
【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32
做一个 ESP32 温度控制的小系统,调了半小时参数,温度还是在 37 到 40°C 之间来回抖?多半是不懂 PID 整定的节奏、把代码直接扔进去硬跑的结果。下面按顺序走一遍:先读出干净的温度,再把 PID 嵌进主循环,最后接上加热和安全阀,走完这五步,炉子就能稳下来。
先搞懂三件事
PID 到底在干什么
想象高速路上的巡航跟车。P 看的是此刻的车间距:离得越近,刹车踩得越狠。I 管的是累计偏差:如果一直慢半拍,它会一点点加码补偿,直到那半拍彻底消失。D 盯的是趋势:判断间距接下来是变宽还是变窄,提前收着点。三者叠在一起,温度才能既跟得上、又不冲过头。
ESP32 在里面负责什么
分工很清晰:ADC 外设负责把传感器信号变成电压读数,LEDC 外设负责生成控制加热功率的 PWM 波形,两块都是硬件外设干活,CPU 不用死等。对应的底层实现在 cores/esp32/esp32-hal-adc.h 和 cores/esp32/esp32-hal-ledc.h,我们只用它封装好的几个函数就行。
传感器怎么选
| 方案 | 精度 | 接线 | 适用场景 |
|---|---|---|---|
| 芯片内置传感器 | ±2°C 左右,还会随芯片负载漂移 | 免接线,直接读 | 只要看芯片温度或大致环境温 |
| DS18B20 | ±0.5°C,线性度好 | 一根数据线和电源 | 测真实物体温度,要稳定复现 |
要控的对象温度,直接上 DS18B20,内置传感器留给"顺便看看"。
从零搭到跑起来:ESP32 温度控制十分钟搭好 🔧
第一步 · 接上传感器,读出第一组真实温度
先用内置传感器验证链路通不通,这段只干一件事:把温度读出来打到串口。
// 内置温度传感器,免接线,直接读芯片温度 analogSetAttenuation(ADC_11db); // 走 ADC 读的话先把量程拉满 float t = temperatureRead(); Serial.printf("当前温度 %.2f°C\n", t);注意一点:读到的"温度"其实是芯片自己的结温,不是环境温,想量物体温度得换 DS18B20 或热敏电阻,再做一次温度传感器校准,把读数拉回真实值。
第二步 · 把 PID 塞进主循环
主循环里每隔 1 秒调用一次下面的 compute,喂进设定值和本轮读数,拿回 0 到 100 的功率。
// 每 1 秒调用一次,返回加热功率 0~100% float compute(float setpoint, float now) { static float integ = 0, lastErr = 0; static unsigned long t0 = 0; float dt = (millis() - t0) / 1000.0; // 距上次的真实秒数 if (dt <= 0) dt = 0.001f; float err = setpoint - now; integ = constrain(integ + err * dt, -50.0, 50.0); // 积分限幅 float d = (err - lastErr) / dt; lastErr = err; t0 = millis(); return constrain(2.0f * err + 0.5f * integ + 1.0f * d, 0.0, 100.0); }两个关键处:dt 一定用两次调用的真实时间差,别写死 1 秒,一旦 loop 被卡住,I、D 两项会悄悄跑偏;积分加了上下限,防止温度偏高时它一路攒到顶、把输出焊死在 100%。
第三步 · 用 PWM 驱动加热丝
加热输出走 LEDC,这段配置一次,之后改功率就一行 ledcWrite。
// 加热脚接 GPIO12,通道 0,1000Hz、8 位分辨率 ledcSetup(0, 1000, 8); ledcAttachPin(12, 0); void setHeater(float pct) { // pct 是 0~100 的功率 ledcWrite(0, (int)(pct * 255.0 / 100.0)); }频率别设太高:加热丝加固态继电器是个慢对象,几百 Hz 到 1kHz 就足够平滑,再往上只会白白增加输出级的负担和噪声。另外提醒一句,ESP32 的 IO 口不能直接带加热丝,中间必须串一个固态继电器或光耦。
参数调不好怎么办:PID 温控的整定路子
Ziegler-Nichols 一步到位法
做法就是先让系统抖起来,记下两个数:把 Ki、Kd 都归零,只留 P,把 Kp 一档档往上加,直到温度开始等幅振荡停不下来——此时的增益记作 Ku,抖一圈的周期记作 Tu。然后按公式取参数:纯 PI 取 Kp = 0.45×Ku、Ti = 0.83×Tu;完整 PID 取 Kp = 0.6×Ku、Ti = 0.5×Tu、Td = 0.125×Tu。这两个数一记,ESP32 PID 参数整定就有个不会抖的底子了。
手动微调的四个常见坑
- 超调:Kp 给大了,温度一下窜过设定值再慢慢回,先把 Kp 砍掉三成再观察。
- 稳态偏:差个零点几度死活回不到设定值,这是 I 不够,Ki 小步往上加。
- 积分雪崩:积分项越攒越大,输出长期钉在 100%,记得给积分加限幅。
- 采样太慢:控制周期比对象的响应还长,D 项算出来的全是噪声,要么加快采样,要么直接放弃 D。
上线前必须加的安全阀 ⚠️
- 超温硬断电:读数超过 75°C 立刻把输出清零,不等下一轮计算。为什么:PID 是正常工作区的算法,传感器坏掉或加热失控时它的输出没有上限,必须有算法之外的硬线。
- 升温速率熔断:每分钟升幅超过 3°C 就判定异常。为什么:正常热对象的升温速率有物理上限,能飙到这么快的只有短路或散热失效,这种信号不值得再算,直接断。
- PWM 最小变更间隔:功率输出 1 秒内最多改一次。为什么:加热回路有热惯性,频繁改动会让测量值追不上实际功率,还加剧继电器触点磨损。
三条任何一条触发,统一走这个紧急出口:
// 任一保护触发时调用,立刻停机 void emergencyShutdown() { ledcWrite(0, 0); // 加热功率瞬间归零 heaterLocked = true; // 上锁,PID 之后不许再写输出 Serial.println("熔断触发,加热已停止"); }还能再往哪走
能延伸的方向很明确:温度曲线,把单一设定值换成"40°C 保温 10 分钟再升到 70°C"这种分段序列,一个设定值变量就能跑烘焙流程;SD 日志,用仓库自带的 SD 和 LittleFS 库,把"时间、设定、实测、功率"每轮写一行进 CSV,回头复盘整定曲线全靠它;WiFi 远程,按仓库的 WiFi 库把当前温度和设定值推到一个网页,出门在外手机上就能改参数、看曲线。
回到开头那个抖 3°C 的炉子:下次别急着调,先让系统抖起来记下 Ku 和 Tu,参数一算就有底,再装好三道安全阀才松手。明天就从消掉那个抖差开始动手。
【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考