尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用ESP32和PID打造会判断火候的智能烤面包机

用ESP32和PID打造会判断火候的智能烤面包机 动手之前我其实已经烤废了大概二十片面包厨房里全是糊味邻居差点以为我把房子点着了。这个项目叫 Automating the Art of Toast说白了就是做一台“自己会判断火候”的烤面包机。目标很简单不管面包是厚切还是薄切、冷藏过还是常温、今天湿度大还是湿度小只要在面板上把熟度设成 3 档它就必须给我吐出一片边缘金黄、中心刚好、不带焦苦味的吐司。如果只是买一台贵一点的吐司机完全不用这么折腾。市面上确实有带“智能”字样的产品但我拆过几台之后发现大多数所谓智能还是停留在“根据重量估算时间”的层面并没有真正感知面包表面的褐变过程。褐变反应的速度和温度是指数关系同样的时间和设定温度不同含水量、不同含糖量的面包反应快慢差得非常多。这就是为什么我决定从加热腔、传感器到控制板全部自己搭把“经验”变成“闭环反馈”。这篇内容适合正在玩 ESP32/Arduino 的创客也适合对“精确控温”有执念的厨房电器 DIY 爱好者。不需要多深的嵌入式基础能看懂串口打印和基本接线就行。我会把项目思路、硬件选型、参数计算、控制逻辑、真实调试中踩过的坑全部过一遍文末再列一份成本清单和后续扩展方向。PID 的数学推导我不会铺开讲但每个参数为什么这么设、改了会有什么后果都会说清楚。1. 项目整体设计与思路拆解1.1 为什么烤吐司需要“自动化”而不是更好用的旋钮传统吐司机用双金属片做定时本质上是一个“时间开环”控制。通电后双金属片受热弯曲弯到一定角度触发弹簧弹出判定对象是机器自己的温度不是面包表面的颜色。所以同样调到 3 档夏天的常温吐司和冬天从冰箱里拿出来的吐司结果完全不同厚切欧包和薄切白吐司一个可能还没上色另一个边缘已经发黑。自动化要解决的就是把“对面包表面状态进行实时测量并决定什么时候断电弹出”这件事接管过来。这个闭环一旦建立结果就不再依赖环境温度、面包初始温度和切片厚度因为这些东西最终都会体现在“表面颜色变化速度”这个可观测变量上。这是整个项目最关键的设计前提用反馈替代经验用测量替代猜测。如果你觉得这是小题大做我换个角度说。你每天做饭时“凭感觉放盐”“凭手感掂锅”本质都是人在充当传感器和控制器。既然工业生产线早就用色选机和红外测温仪干这事了那把一套微缩版搬到桌面其实只是顺理成章。1.2 闭环控制和传统定时烘烤的本质区别用一个最简单的模型描述烘烤过程加热丝辐射热量面包表面温度升高发生美拉德反应和焦糖化反应颜色从米白变成浅黄、金黄、浅棕、深棕直到黑色。整个过程有两个可测变量可以代表“熟度”表面温度和表面反射光。表面温度响应快但只能反映局部反射光直接对应颜色是褐变程度的直接体现但容易受环境光干扰。传统定时烘烤的模型是加热时间 常量由旋钮档位决定闭环控制的模型是实际状态 传感器实时采样值 决策 当状态达到目标值时触发断电和弹出这里面最大的难点不是“到达目标值”这个判断而是如何让加热过程稳定地朝目标值推进。如果一直全功率加热接近目标时惯性会把面包推向过焦如果功率太小又会出现“表面还白着但内部已经干透”的问题。所以控制器需要同时干预加热功率和采样频率这就是后面要讲 PID 和功率调制的用武之地。2. 核心硬件选型与细节解析2.1 温度传感器选型K 型热电偶还是 NTC先说结论我最后用了 MAX31855 K 型热电偶备选了 NTC 但没有选用。原因有两点。第一测量范围。吐司表面温度在烘烤过程中会从室温升到 140-180°C而紧贴加热丝附近的热空气温度能到 200°C 以上。NTC 的线性范围普遍在 -40°C 到 150°C产品手册虽然标 200°C但高温段漂移明显校准过几次还是会偏不适合长期使用。K 型热电偶量程随便到 1000°C用在吐司机上绰绰有余。第二响应速度。热电偶的测量端是一个很小的焊点热容小可以做到几百毫秒级别的响应NTC 很多是贴片或玻封热容大反应慢尤其在空气对流不强的加热腔里滞后的时间差会直接导致控制“过冲”。关于探测点位置我建议把热电偶探头固定在加热腔顶部反射板附近不要直接贴在加热丝上。贴在加热丝上读数会瞬间飙到 300°C意义不大放在反射板附近能比较真实地反映“面包上部热辐射强度”这个值用来做功率控制更合适。实际接线时要注意热电偶正负极不能接反MAX31855 板子上的螺丝端子我是用压线钳压好冷压端子再锁紧的。直接夹细线很容易松脱松了以后读数会乱跳这是排查了老半天才发现的低级问题。2.2 颜色传感器 TCS34725 的应用细节颜色传感器是我这个方案里最核心的部分。TCS34725 是一个带 RGB 滤光片的数字光传感器内部有四个通道红、绿、蓝、透明透明通道可以用来做环境光补偿。使用的时候我把模块放在面包正上方约 6-8cm 的位置对准烤架中心在外面套了一段黑色热缩管做遮光罩避免环境光直接打进传感器窗口。模块本身自带两个白色 LED但这两个 LED 亮度不高照射角度也很散。我的做法是拆掉原始 LED改用两个 5mm 高亮白光 LED 从两侧做 45° 照明并把 LED 供电和传感器供电分开走线。为什么因为同一路供电会互相干扰当 LED 开启瞬间电流突变TCS34725 的 ADC 读数会抖动尤其是在 I2C 线没有做滤波的时候读到的 RGB 值能差 30-40。分开供电后抖动基本消失。TCS34725 的量程和积分时间也需要设置。我最初用积分时间 101ms、增益 16x测试时发现面板最内侧反射光太弱信噪比低。后来把积分时间拉到 154ms增益保持 16x并把传感器离面包的距离从 10cm 缩短到 6cm数据才恢复正常。不同面包的表面反射率差很多白吐司在相同光照下 RGB 总和可能是黑麦包的 5 倍所以判断熟度不能用绝对 RGB 值要用归一化比例或者相对变化量这个在控制逻辑部分细说。2.3 加热功率控制与执行机构的选择加热丝是 220V 交流供电普通继电器开关会带来明显温度波动开一秒钟温度冲上去关一秒钟温度又掉下来。我最终用的是固态继电器型号 SSR-25DA控制端通过光耦隔离由 MCU 的 PWM 信号驱动。为什么用 SSR因为固态继电器没有机械触点开关频率可以做到几十赫兹这样我可以对 220V 交流电做“过零触发的功率调制”按周期占比控制平均功率而不是简单的一会儿开一会儿关。考虑到小功率加热丝本身热惯性不大我用的是定周期 PWM周期设 1 秒占空比从 0% 调到 100%最小调节步进 1%。执行机构这块要多说一句。开始我用的是电磁铁加弹簧的弹出方案通电时电磁铁吸合勾住烤架断电后弹簧弹起。测试中发现面包比较轻的时候没问题但厚切欧包重一些弹簧力量不够会弹不起来或者弹出不到位。后来我换成 28BYJ-48 步进电机带动一个塑料拨杆断电后让拨杆把烤架顶起来。步进电机速度确实慢但扭矩足够而且可以精确控制顶起高度不会把面包直接甩出去。步进电机驱动板用的是 ULN2003控制占用 4 个 GPIO代码不复杂成本也就十几块钱。3. 硬件搭建、接线与参数计算3.1 完整材料清单和大致成本下面是我最终定型使用的物料清单价格按照我采购时的行情写仅供参考物料型号/规格数量参考价格主控板ESP32 DevKitC V41约 25 元温度采集MAX31855 模块 K 型热电偶1 套约 35 元颜色识别TCS34725 模块改造 LED1约 18 元固态继电器SSR-25DA配散热片1约 25 元步进电机28BYJ-48 ULN2003 驱动1 套约 15 元电源模块12V 2A 适配器 AMS1117-3.3各 1约 20 元显示模块SSD1306 OLED 0.96 寸I2C1约 12 元操作面板旋转编码器 EC11 按钮各 1约 8 元加热腔旧吐司机外壳 220V 加热丝1 套回收件约 30 元其他端子、导线、热缩管、遮光罩材料若干约 20 元合计大概 200 出头。如果你是学生党想省钱MCU 换 Arduino Nano、不装 OLED 和编码器面板、加热腔直接用现成吐司机改装成本能压到 100 元以内。但我不建议省掉 TCS34725因为它是这套方案“智能”的关键省掉就退回双金属片的水平了。3.2 接线要点与分区布局MCU 用的是 ESP323.3V 逻辑电平。接线逻辑我按功能分区列出来TCS34725VIN 接 3.3VGND 接 GNDSDA 接 GPIO21SCL 接 GPIO22。LED 正极接 5V 并通过电阻限流LED 负极接一个 NPN 三极管做开关控制。MAX31855SCK 接 GPIO18CS 接 GPIO5SO 接 GPIO19VIN 接 3.3V。注意 MAX31855 的通信协议是 SPI电平是 3.3V别接 5V会烧模块。SSR 控制端控制信号从 GPIO26 输出经过 1k 电阻接到 SSR 控制端正极控制端负极接 GND。SSR 输出端串联在 220V 加热丝的回路里并且必须配散热片。我第一次没装散热片跑了十分钟就闻到糊味了。步进电机驱动IN1-IN4 接 GPIO13、12、14、27电机电源接 12V驱动板地要和 ESP32 地共地。旋转编码器CLK 接 GPIO34DT 接 GPIO35SW 接 GPIO32三个引脚都要加 10k 上拉电阻到 3.3V。关于强电安全必须多说几句。220V 部分和弱电部分要物理隔离加热丝的两根引线用耐高温硅胶线接头处用压线端子和热缩管处理裸露金属部分绝对不能放在伸手可及的地方。我在最终调试时把加热腔放在一个亚克力透明罩里每次改接线都先拔掉 220V 插头等 2 分钟让电源完全放完电再动手。这个习惯救了我好几次因为 220V 电源回路里的电容真的会存电。3.3 关键参数的计算过程第一组参数加热功率设定。我拆下来的加热丝功率是 700W按 220V 供电计算电流大约是 3.2ASSR 额定电流要留 1.5 倍以上余量所以我选了 SSR-25DA。25A 确实大材小用但余量充足、散热压力小。如果你用 1000W 的加热丝电流约 4.5A选 10A 的 SSR 也能用但必须加散热片。第二组参数PID 调参初始值。我直接用 Arduino PID 库初始值设置 P40、I5、D20温度设定 160°C。这里有个容易踩的坑PID 的输出如果直接映射成 0-255 的 PWM 占空比调节会很生硬因为加热腔热惯性很大P 稍微大一点就容易出现“冲过头”。我最后把 PID 输出做了二次映射输出 0-100 之间时给 SSR 的脉冲占空比是 10%-60% 之间超过 100 再线性拉开。这样做的原因是在接近目标温度时小占空比可以让热惯性过冲最小。实测过冲从原来的 18°C 降到了 4°C 左右。第三组参数颜色阈值标定。TCS34725 输出的是 16 位 RGB 值我每次采样后先除以透明通道值做归一化得到 r、g、b 三个比例系数。标定流程是把一片白吐司放进机器在不加热的情况下连续采集 20 次记录初始 r0、g0、b0然后把熟度设为 3 档烤好一片记录结束时的 r1、g1、b1。这样得到“从生到熟”的颜色变化向量。实际判定时我用的是综合距离熟度系数 sqrt((r - r0)^2 (g - g0)^2 (b - b0)^2) 目标熟度 sqrt((r1 - r0)^2 (g1 - g0)^2 (b1 - b0)^2) 当 熟度系数 目标熟度 * 0.85 时触发弹出这里的 0.85 是“接近目标就提前断电”的预判系数给惯性留出缓冲时间。这个系数不是拍脑袋定的是反复烤了十几片面包试出来的。定太高会烤焦定太低会偏生0.85 在多数面包上表现稳定但更换面包品牌后最好重新标定。4. 核心控制逻辑与代码实现4.1 传感器数据采集与滤波原始 ADC 数据不能直接用。TCS34725 的 RGB 读数在每次采样之间会有随机波动MAX31855 的温度读数也会有热电偶本身的噪声。温度我做了窗口大小为 5 的滑动平均滤波颜色做中值滤波排序后取中间值避免偶发干扰尖峰把控制判断带偏。核心代码逻辑如下实际发布时我会把完整工程挂到 GitHub Gist 或者网盘#include Wire.h #include Adafruit_TCS34725.h #include Adafruit_MAX31855.h #include PID_v1.h #define SSR_PIN 26 Adafruit_TCS34725 tcs Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_154MS, TCS34725_GAIN_16X); Adafruit_MAX31855 thermo(18, 5, 19); double setpoint 160; // 目标腔温 double input, output; double Kp 40, Ki 5, Kd 20; PID pid(input, output, setpoint, Kp, Ki, Kd, DIRECT); void setup() { Serial.begin(115200); pinMode(SSR_PIN, OUTPUT); tcs.begin(); pid.SetMode(AUTOMATIC); pid.SetOutputLimits(0, 255); } void loop() { double rawTemp thermo.readCelsius(); if (isnan(rawTemp)) rawTemp lastTemp; // 偶发读取失败用上一帧 lastTemp tempFilter.add(rawTemp); input lastTemp; pid.Compute(); // 占空比二次映射避免过冲 int duty mapPidOutputToDuty(output); analogWrite(SSR_PIN, duty); // ESP32 需要设置 PWM 通道 uint16_t r, g, b, c; tcs.getRawData(r, g, b, c); double rNorm c 0 ? (double)r / c : 0; double gNorm c 0 ? (double)g / c : 0; double bNorm c 0 ? (double)b / c : 0; double cookedLevel sqrt(pow(rNorm - r0, 2) pow(gNorm - g0, 2) pow(bNorm - b0, 2)); if (cookedLevel target * 0.85 || totalTime 300) { triggerPop(); } delay(50); }这个代码框架只是骨架。实际项目里比这复杂的地方在状态机比如“等待预热温度稳定”“打开加热”“观察颜色变化”“停止加热并弹出”这几个状态不能直接串行跑必须在每一帧里同时判断多个条件。我用的是一个 switch-case 状态机每个状态里执行对应的采样和控制任务状态转移条件统一放在末尾。4.2 熟度判定算法的细节我把熟度分成 1-5 档每个档位对应一组目标颜色和修正系数。比如 1 档的颜色目标大约是目标熟度的 0.55 倍2 档 0.723 档 0.854 档 0.955 档 1.10。5 档就是故意烤到深棕甚至微微冒油的状态。这些系数不是等比数列因为褐变反应速度不是线性的接近深色时每上升一点需要的温度和时间增量都更大。因此 3 档到 4 档的系数差比 1 档到 2 档小是为了避免你在 4 档时等太久。另外我加了一个“颜色变化率”作为辅助条件。如果某片面包初始颜色就很深比如黑麦欧包它从生到熟的总体色差区间比白吐司小只用绝对距离会误判。加上变化率之后逻辑变成当熟度系数超过对应档位阈值且最近 10 秒内颜色变化速率小于某个极小值时才触发弹出。后一个条件本质是“化学反应已经趋于饱和”说明褐变基本完成这时候弹出既不会太早也不会太晚。4.3 安全保护与异常处理这个必须单独写一节因为自动化的东西一旦失控比手动操作危险得多。我加了四层保护。第一层温度上限保护。腔体温度超过 230°C 时立即切断 SSR 输出。这个值比正常烘烤高约 50°C留够余地又不会误触发。第二层超时保护。一旦开始加热超过 300 秒还没触发弹出不管颜色读数如何强制断电。按 700W 功率正常烤一片吐司最多 150 秒。300 秒怎么都不可能正常触发大概率是传感器故障或者面包被卡住。第三层颜色数据异常保护。如果 TCS34725 连续 20 次读到完全相同的 RGB 值基本可以认定传感器坏掉或被挡住了此时按温度和时间计算一个保守弹出时间作为降级模式。虽然降级模式又回到开环控制但总比烧焦好。第四层开机自检。上电时检查温度读数是否为合理范围比如 -10°C 到 50°C再检查颜色传感器 I2C 是否正常应答。任何一项异常OLED 显示错误代码并拒绝启动加热。这在调试阶段救了我很多次省得每次都要拆壳检查。5. 调试实录我踩过的那些坑5.1 温度曲线和预设规则对不上首次跑通代码时我用固定 160°C 的目标温度结果烤出来的吐司颜色总是差一档设定 3 档出来的是 1.5 档的效果。排查后发现问题不是 PID 没跑好而是热电偶探头安装位置离加热丝太近测到的“腔温”比面包实际感受到的温度高很多。控制器把腔温稳定在 160°C但面包表面实际可能只有 130°C褐变反应自然慢。解决方法是调整探头安装位置并在软件里加温度补偿。我在目标温度上加一个偏移量偏移量根据探头距加热丝的距离来定。用测温枪先测几组面包表面实际温度和腔温的对应关系拟合出来一个线性补偿实际目标温度 设定温度 × 0.78 28。这个补偿系数标定好之后就不要乱动因为加热腔的物理位置变了就得重新标。5.2 颜色传感器把白吐司识别成焦面包这个问题严重到差点让我放弃颜色方案。现象是面包还没怎么上色机器就弹出来了。日志里看熟度系数涨得飞快几秒钟就超过阈值。排查后发现两个原因叠加。第一遮光罩没做彻底环境光从侧面漏进传感器窗口而且我测试的时间正好在下午阳光从窗户方向照进来RGB 里蓝色通道被拉高归一化后整体向量发生了偏移。第二传感器前面的光学窗口用了透明亚克力高温下表面凝结了一层水汽水汽造成散射让传感器读到了失真的颜色。解决把遮光罩换成黑色 ABS 3D 打印件窗口处加一层光学级玻璃替代亚克力传感器每次读取之前先读一次“基准光照”减去环境光贡献。这个方法实测有效下午直射阳光的环境下也没有再误判。5.3 继电器通断导致 ESP32 重启SSR 控制 220V 加热丝时每次切换都会产生比较大的电磁干扰尤其是大功率斩波时MCU 偶发复位OLED 闪一下黑屏。这个问题用逻辑分析仪看最直观SSR 通断瞬间5V 和 3.3V 电源轨上会出现几十毫伏到几百毫伏的毛刺。处理办法有三板斧。一是强电和弱电走线分开加热丝线尽量远离信号线。二是给 ESP32 的电源入口加一个大电容470uF 电解电容并联一个 0.1uF 陶瓷电容滤波。三是把 SSR 控制信号线改成双绞线并串一个 330Ω 电阻减小尖峰电流。改完之后我连续跑了 30 次完整烘烤流程没有一次重启。这里要提醒电源滤波电容选耐压 16V 以上不要为了省体积选 6.3V 的。5.4 步进电机弹出机构偶发卡死28BYJ-48 电机本身扭矩足够但低温环境下启动时会有丢步现象尤其冬天室温低的时候拨杆经常停在半路。排查后做了两个调整。一是在状态机里加了“弹出确认逻辑”拨杆动作后通过一个微动开关确认是否到位没到位就再补一步。二是启动时给电机一个 200ms 的加速脉冲避免冷启动直接全速导致丢步。6. 成本核算、实测效果与可扩展方向6.1 实际花销和改造周期这个项目断断续续做了三周主要时间花在等快递和反复调试。硬件成本大约 220 元加上我买废的 30 多片面包和两次烧坏的热电偶总共投入不超过 350 元。如果用现成吐司机外壳替换 DIY 加热腔改造成本还能更低。时间上如果你已经熟悉 ESP32 和基本传感器接线2-3 天就能把第一版跑起来剩下的时间都花在标定和排查问题上。6.2 实测效果我拿同一批白吐司、同品牌全麦面包、黑麦欧包各做了 5 次测试。3 档熟度下白吐司 5 次里 4 次达到了“中间软、边缘金黄、无焦斑”的预期1 次稍微偏浅是因为那次面包太薄颜色传感器捕捉到的反射面积比例不同。全麦面包表现更稳定5 次全部合格。黑麦欧包因为本身颜色深判定逻辑里的“变化率辅助条件”起了作用没有出现误判。这个可靠性和传统吐司机盲猜相比提升是肉眼可见的。6.3 后续可以扩展的方向做完这个项目我最大的收获不是“烤好了几片吐司”而是把“基于反馈的自动控制”这套方法论应用到了更多厨房场景。同一个平台换一组传感器配置就可以做低温慢煮、发酵箱、果干烘干机。我后来把代码抽成了一个简单的“任务模板”加热功率、目标温度、目标颜色、超时时间都做成可配置项。下次做别的东西改配置就行不用重写逻辑。如果你也想复刻我的建议是先把 3.3 节的颜色标定流程仔细做一遍不要跳过。这个项目所有智能都建立在可靠的“熟度感知”上感知不准后面控制逻辑再精巧都是白搭。最后再分享一个小技巧给 TCS34725 的遮光罩内部涂上一层黑色哑光漆可以有效减少内壁反光对颜色读数的污染。这个问题我在第二代版本里才发现涂完以后颜色数据的方差明显下降熟度判定也稳定了很多。这种细节不实际拆开机器看一眼是无论如何也想不到的。
返回列表