PetBoxX 恒温区风扇控制的演变:固定 PWM、hold_ready 重置,与动态微调
本文记录 PetBoxX 孵化箱固件在"恒温维持阶段"风扇控制策略的三次演变,从最初的固定 PWM、引入 hold_ready 累计判断,到最终的按风扇资质归一化的动态微调。最后讨论哪种方案在理论和实测数据上更优。
背景:PTC + 风扇的控温物理模型
PetBoxX 使用 PTC(正温度系数)陶瓷加热片作为热源,配合顶部直流风扇做强制对流。
PTC 有一个关键特性:自限温。当 PTC 温度升高,其电阻急剧上升,发热功率自动下降。这意味着在稳定工况下,PTC 不需要外部功率控制——只要供电,它会自己找到平衡温度。
箱内实际温度由两个量共同决定:
温度上升速率 ∝ PTC 发热功率 - 风扇散热功率
- PTC 功率:由 TRIAC 控制(过零点触发延迟),近目标时已降至较低水平
- 风扇功率:由 PWM 占空比控制,影响对流强度(热量从加热片到传感器的传输速率)
因此,风扇速度决定了热量分配效率,而不只是"散热"。风速太低,热空气不能充分循环,传感器附近可能偏冷;风速太高,PTC 产生的热量被快速带走,反而降温。
方案一:固定 PWM(hold_lock = true)
实现
bool hold_ready = g_fan_hold_lock || (hold_elapsed_s >= g_fan_hold_dwell_s);
// hold_ready 时:
stage_pct_f = (float)g_fan_hold_pct; // 固定不变
将 g_fan_hold_lock = true 后,hold_ready 永远为真,风扇 PWM 锁定在由风扇资质测试(fan_qualify)确定的最优 hold_pct 值,不随温度误差变化。
测试观察
实测开启 hold_lock 后,RPM 读数仍在 1106~1135 之间来回跳动。初看像是 PWM 在变,但深入分析后确认这是FG 脉冲量化误差,而不是实际转速变化:
FG:2 脉冲/转,采样窗口约 1034ms
RPM 分辨率 = 60,000,000 / (1,034,483 × 2) ≈ 29 RPM/步
风扇实际 PWM 完全固定,观测到的 ±30 RPM 抖动来自整数计数量化,无法消除(除非拉长采样窗口或做 N 次平均)。
优点
- 实现最简单,无计算开销
- 消除了控制算法本身引入的 PWM 跳变
- 对于"找到了正确的 hold_pct"的场景,理论上最优
缺点
hold_pct是 fan_qualify 在特定工况下测定的,换了环境(室温、负载、箱体差异)后可能不再是最优值- 完全开环:温度偏移 0.3°C 后,控制器不会做任何响应(只靠 TRIAC 控制 PTC 功率)
- 依赖 TRIAC 的精度和一致性,量化粗糙(50Hz 交流,延迟步长约 200μs)
方案二:hold_ready 累计判断与重置
实现
// 每个控制周期(约 dt_s ≈ 200ms)
if (err_mag_c <= g_fan_hold_err_c) {
hold_elapsed_s += dt_s; // 误差在区间内,累计时间
} else {
hold_elapsed_s = 0.0f; // 误差超出区间,重置
}
bool hold_ready = (hold_elapsed_s >= g_fan_hold_dwell_s);
hold_ready 不是立即触发的——需要温度在 ±hold_err_c(默认 ±0.3°C)区间内持续稳定 hold_dwell_s(默认若干秒)之后,才认为进入恒温状态,切换到 hold_pct。
在 hold_ready 尚未触发时(near_wait 阶段),使用线性插值:
// 按误差比例从 0 爬升到 hold_pct
stage_pct_f = lerp_clamp(err_pos_c, 0.0f, g_fan_hold_err_c,
0.0f, (float)g_fan_hold_pct);
这样做的意图
避免箱温刚进入目标区间就立刻切到 hold 档,给系统一段"稳定确认期",防止瞬时超调误触发。
问题:smooth_floor 的干扰
引入 hold_ready 机制后,发现一个副作用:在 near_wait 阶段,smooth_floor 保护逻辑会将风扇下限抬升到 near_pct:
if (!g_fan_hold_lock && err_pos_c >= FAN_LINEAR_ERR_C) {
float smooth_floor = lerp_clamp(err_pos_c, ...);
fan_floor = fmaxf(fan_floor, smooth_floor);
}
当温度在 hold_err_c 边界附近轻微震荡时,hold_elapsed_s 反复重置,风扇在 lerp 输出 和 smooth_floor 覆盖 之间跳动,产生明显的 PWM 抖动。这反而比固定 PWM 更差。
方案三:按风扇资质归一化的动态微调(当前实现)
核心思路
进入 hold_ready 状态后,不再固定在 hold_pct,而是根据当前温度误差与资质测试余量做小幅调整:
const float ratio = 0.3f;
float t = err_c / g_fan_hold_err_c; // 归一化误差,[-1, +1]
float micro;
if (t > 0.0f) {
// 偏冷:向上借用余量,上限 = near_pct - hold_pct
float up_headroom = (float)(g_fan_near_pct - g_fan_hold_pct);
micro = fminf(t, 1.0f) * up_headroom * ratio;
} else {
// 偏热:向下让出余量,下限 = hold_pct - heat_min_pct
float dn_headroom = (float)(g_fan_hold_pct - g_fan_heat_min_pct);
micro = fmaxf(t, -1.0f) * dn_headroom * ratio;
}
stage_pct_f = g_fan_hold_pct + micro;
为什么归一化到 fan_qualify 余量
不同风扇的 hold_pct、near_pct、heat_min_pct 由 fan_qualify 测定,反映该风扇在当前箱体的实际特性。若用固定的 ±3% 微调,对于 hold_pct = 15% 的强风扇和 hold_pct = 35% 的弱风扇效果截然不同。
归一化后,ratio = 0.3 的含义是:在 hold_err_c 边界处,最多使用 30% 的余量空间,对所有风扇都有同等的相对影响力。
举例:
| 风扇档位 | hold_pct | near_pct | up_headroom | 最大向上微调 (ratio=0.3) |
|---|---|---|---|---|
| 弱风扇 | 35% | 50% | 15% | +4.5% |
| 强风扇 | 15% | 28% | 13% | +3.9% |
| 典型M箱 | 22% | 32% | 10% | +3.0% |
微调量和余量成正比,不会让弱风扇突然跳到一个可能过热的区间。
三方案对比
| 维度 | 固定 PWM | hold_ready 累计重置 | 动态微调(当前) |
|---|---|---|---|
| 实现复杂度 | 最低 | 中 | 中 |
| 控制连续性 | 完全开环 | 阶梯切换 | 连续小步调整 |
| 温度偏移响应 | 无(靠 TRIAC) | 无(hold 期间) | 有(±hold_err_c 范围内) |
| 跨风扇适配性 | 依赖 fan_qualify 精度 | 同左 | 归一化,自适应 |
| smooth_floor 冲突 | 无(hold_lock 跳过) | 有(边界抖动) | 无(hold_ready 后不触发) |
| RPM 抖动来源 | 仅 FG 量化(不可消除) | 算法跳变 + FG 量化 | 温误差变化 + FG 量化 |
理论分析:哪种最好?
PTC 系统的开环稳定性
PTC 加热片本身已经提供了一定的自稳能力:靠近目标温度时功率自动下降。在小箱体、低热容量的孵化场景中,这种自稳能力相当强。
理论上,对于一个热容量小、PTC 自限温准确的系统,固定 PWM 已经足够。 原因:
- 热时间常数长(箱体热容 > 几分钟),风扇的小幅变化对温度的影响需要等待才能显现
- TRIAC 控制的 PTC 功率本身就是主要调节手段,风扇是辅助
- 动态微调的 ±3~5% PWM 变化,在一个几分钟热时间常数的系统里,几乎是噪声量级
动态微调的实际价值
动态微调的价值不在于"控温精度提高了多少",而在于:
- 应对环境变化:室温从 20°C 突变到 30°C,固定 hold_pct 下温度会漂移;动态微调在漂移超过 hold_err_c 时开始响应
- 消除算法抖动:比 hold_ready 的阶梯切换更平滑,不会因边界震荡产生 PWM 跳变
- 无需 hold_lock:不依赖特殊的调试开关,日常运行就是动态的,但微调幅度被限制在 30% 余量以内,不激进
需要什么数据才能下结论
目前没有做过受控对比实验。要验证三方案的实际效果,需要:
- 同一台设备、同一环境,分别运行三种策略各 2 小时,记录 1s 精度温度曲线
- 计算各策略的温度标准差(σ)和最大偏差(peak-to-peak)
- 在室温变化(如开空调、关门)等扰动下重复上述测试
初步预期:
- 无扰动、稳定室温:三者 σ 相近(均 < 0.2°C),固定 PWM 最平稳
- 有环境扰动:动态微调能更快响应,σ 略小
- 风扇老化/磨损导致 hold_pct 漂移:动态微调最鲁棒
当前策略的局限
动态微调目前还有两个已知局限:
- 只在
hold_err_c区间内微调(默认 ±0.3°C),超出区间就回到 near/fast 阶段,微调不连续 - 没有积分项:对持续的温度偏置(steady-state offset),微调只能补偿到
ratio × headroom,无法完全消除
如果未来要进一步提升,下一步可以考虑在 hold 区间引入一个轻量积分累加器,对超过 30s 的持续偏置做额外补偿。但这需要更谨慎的抗积分饱和设计,不在本次讨论范围内。
小结
| 固定 PWM | 动态微调(当前) | |
|---|---|---|
| 最优场景 | 室温恒定、fan_qualify 准确、不需要自适应 | 有环境扰动、多台设备风扇特性不一致 |
| 主要风险 | 环境变化时无响应 | 过于激进的 ratio 可能加剧抖动 |
| 现状 | 可作为调试/基准模式(hold_lock=true) | 默认运行模式 |
两者并不互斥——hold_lock 开关保留了固定 PWM 的调试路径,在需要隔离变量测试时可以随时启用。