อนุมานจังหวะ defrost ห้องเย็นจากความชันอุณหภูมิ ไม่ง้อเซนเซอร์ความชื้น
ไม่มีเซนเซอร์ความชื้นสักตัวในห้อง แต่กราฟอุณหภูมิบอกได้หมดว่าคอยล์เริ่มตันด้วยน้ำแข็ง เพราะ frost ที่หนาขึ้นทำให้ห้องเย็นช้าลงตามฟิสิกส์ ไม่ใช่เรื่องบังเอิญ ถ้าอ่านความชันเป็น ก็สั่ง defrost ตอนที่ควรได้ ไม่ต้องตั้ง timer เดาไว้ล่วงหน้า
พอดีมีห้องเย็นตัวหนึ่งที่ใส่วุ้นเส้นเปียกเข้าไปเพื่อรีดน้ำออกจากเส้น (freeze-dehydration) ต้องคุมอุณหภูมิราว -11 ถึง -14°C แต่ทั้งห้องไม่มีเซนเซอร์ความชื้นสักตัว มีแค่ 4 จุดวัดอุณหภูมิกับกระแสไฟของคอมเพรสเซอร์/พัดลม/ฮีตเตอร์ defrost ที่อ่านผ่าน RS485 ทุกไม่กี่วินาที คำถามคือจะรู้ได้ยังไงว่าคอยล์เริ่มตันด้วยน้ำแข็งแล้วควรสั่ง defrost ผมเลยไปไล่ดูว่าความชันของกราฟอุณหภูมิมันบอกอะไรได้บ้าง กลายเป็นว่าบอกได้เกือบหมด
ชื่อโรงงานและเลข register ในบทความเป็นตัวอย่าง ส่วนสมการ ค่าคงที่ และโค้ดคือของจริงที่คำนวณตามได้
frost เกาะคอยล์ ทำไมกราฟอุณหภูมิถึงราบลง
หัวใจคือสมการสมดุลพลังงานของห้องแบบก้อนรวม (lumped-capacitance) มองห้อง+ของ+โครงเป็นมวลความร้อนก้อนเดียว:
# สมดุลพลังงานของห้อง (lumped-capacitance)
# (m*cp)_eff * dT/dt = Q_load - Q_evap
#
# (m*cp)_eff : thermal mass ของอากาศ + วุ้นเส้น + โครง (J/K)
# Q_load : โหลดเข้า (แทรกซึม + ของที่ใส่ + พัดลม + ความร้อน defrost ตกค้าง)
# Q_evap : ความเย็นที่คอยล์ดึงออก (W)
#
# ตอนคอมเดินดึงอุณหภูมิลง จัดรูปได้เป็น
# dT/dt = -(Q_evap - Q_load) / (m*cp)_eff
# แปลว่า |dT/dt| (ความชันการลด) แปรผันตรงกับ Q_evap
ทีนี้ Q_evap ที่คอยล์เขียนได้เป็น Q_evap = U * A * ΔT_lm โดย U คือ overall heat-transfer coefficient, A คือพื้นที่แลกเปลี่ยนความร้อน, ΔT_lm คือผลต่างอุณหภูมิเฉลี่ยแบบ log-mean ระหว่างน้ำยากับอากาศ
frost ที่เกาะคอยล์ทำร้าย Q_evap พร้อมกันสองทาง คือ frost เป็นฉนวน (k ราว 0.1 W/m·K) ไปเพิ่มความต้านทานความร้อน ทำให้ U ลด และตัวมันเองไปอุดช่องลมระหว่างครีบ ลมผ่านคอยล์ได้น้อยลง A กับ h_air ลด ตามไปด้วย ผลรวมคือ U·A ตกลงเรื่อยๆ ตามเวลา Q_evap เลยลด และเมื่อ Q_evap ลด ความชันการลดอุณหภูมิของห้องก็ราบลงตามสมการข้างบน นี่แหละที่สังเกตได้ว่า "ห้องลดช้าลง = น้ำเกาะคอยล์"
อ้างอิงตัวเลขที่มีคนวัดไว้ frost หนาเกิน 5mm เริ่มเสียพลังงาน 5-30%, หนาราว 25mm capacity ตกได้ถึง ~32% เพราะฉะนั้นความชันที่ราบลง ไม่ใช่แค่สัญญาณคร่าวๆ มันคือตัวแทน (proxy) ของ U·A ที่ลดลงตรงๆ
จับ frost ด้วย time-constant τ ไม่ใช่ slope ดิบ
ปัญหาของการเอา slope ดิบมาเทียบกันคือ ตอนห้องเข้าใกล้ setpoint slope จะราบลงเองตามฟิสิกส์ (exponential approach) ไม่ใช่เพราะ frost ผมเลยไม่วัด slope เป็นตัวเลขเดียว แต่ fit ทั้งเส้น pull-down เข้ากับสมการ Newton cooling แล้วดึงค่า time-constant τ ออกมา ซึ่ง τ = (m·cp)_eff / (U·A) — frost ทำให้ U·A ลด τ เลยโตขึ้น ตรงไปตรงมา
import numpy as np
from scipy.optimize import curve_fit
def newton_cooling(t_min, T_inf, T0, tau):
# T(t) = T_inf + (T0 - T_inf) * exp(-t/tau)
return T_inf + (T0 - T_inf) * np.exp(-t_min / tau)
def fit_tau(t_min, temp_c):
"""fit หนึ่งรอบ pull-down -> คืน tau (นาที). tau ยิ่งมาก = คอยล์ยิ่งตัน"""
p0 = (float(np.min(temp_c)), float(temp_c[0]), 20.0) # เดา T_inf, T0, tau เริ่มต้น
(T_inf, T0, tau), _ = curve_fit(newton_cooling, t_min, temp_c, p0=p0, maxfev=10000)
return tau
# ตัวอย่างจากรอบจริง: คอยล์เพิ่งล้าง fit ได้ tau ~ 18 นาที
# อีกไม่กี่รอบต่อมาน้ำเกาะจน fit ได้ tau ~ 26 นาที = แย่ลง +44%
ก่อนจะ fit ต้องแยก "รอบ pull-down" ออกมาให้สะอาดก่อน คือเอาเฉพาะช่วงที่คอมเดินต่อเนื่องและไม่ได้ defrost อยู่ ไม่งั้นค่า τ เพี้ยนหมด:
import pandas as pd
def pulldown_cycles(df, comp_thr=5.0):
"""
df: คอลัมน์ ts(unix), temp(°C กลางห้อง), i_comp(A), i_defrost(A)
คืน list ของ DataFrame แต่ละรอบ pull-down (comp ON, defrost OFF)
"""
on = (df["i_comp"] > comp_thr) & (df["i_defrost"] < 1.0) # กรอง comp ON + defrost OFF
grp = (on != on.shift()).cumsum() # นับ segment ต่อเนื่อง
cycles = []
for _, seg in df[on].groupby(grp[on]):
if len(seg) < 30: # รอบสั้นเกินไป ตัดทิ้ง
continue
seg = seg.copy()
seg["t_min"] = (seg["ts"] - seg["ts"].iloc[0]) / 60.0
cycles.append(seg)
return cycles
ตอนห้องเข้าใกล้ setpoint slope จะราบลงเองตามฟิสิกส์ ไม่ใช่ frost เสมอ ผมเลยไม่เอา slope ดิบมาเทียบตรงๆ แต่ใช้ τ ทั้งเส้น หรือถ้าจะเทียบ slope ก็ต้องจับเวลาช่วงเดียวกันทุกครั้ง เช่น -5 → -10°C ใช้กี่นาที และต้องกรองเฉพาะ comp ON กับ defrost OFF ถ้าเผลอเอาช่วงคอมหยุดเข้ามาปน ค่าจะพังทันที
กฎ demand-defrost: τ แย่กว่า baseline เกินเกณฑ์ = สั่งละลาย
พอมี τ ต่อรอบแล้ว ที่เหลือคือเทียบกับ baseline ของคอยล์ที่เพิ่งล้างเสร็จ ถ้า τ โตเกินเกณฑ์ (ผมเริ่มที่ 30%) แปลว่า frost สะสมพอที่จะคุ้มค่าละลายแล้ว logic ทั้งหมดสั้นแค่นี้:
def decide_defrost(tau_now, tau_baseline, threshold=0.30):
"""คืน (should_defrost, degrade) — degrade = τ แย่ลงกี่ %"""
degrade = (tau_now - tau_baseline) / tau_baseline
return degrade >= threshold, degrade
# ต่อกับ pipeline จริง
cycles = pulldown_cycles(df)
tau_series = [fit_tau(c["t_min"].to_numpy(), c["temp"].to_numpy()) for c in cycles]
tau_baseline = tau_series[0] # รอบแรกหลังล้างคอยล์ = คอยล์สะอาด
tau_now = tau_series[-1]
go, degrade = decide_defrost(tau_now, tau_baseline)
print(f"tau baseline={tau_baseline:.1f} min now={tau_now:.1f} min "
f"degrade={degrade*100:.0f}% -> {'DEFROST' if go else 'hold'}")
ข้อดีที่เห็นชัดคือมันปรับตามของเอง ช่วงแรกที่วุ้นเส้นยังเปียกมาก น้ำระเหยไปเกาะคอยล์เร็ว τ แย่เร็ว ระบบก็ defrost ถี่เองเพื่อระบายน้ำออกไว พอเส้นแห้งขึ้น frost ช้าลง τ ก็ค่อยๆ แย่ช้าลง defrost ห่างออกเอง ไม่ต้องมานั่งจูน timer ตามฤดูหรือตามน้ำหนักของที่ใส่
ตั้งเวลาตายตัว เดาไว้ล่วงหน้า
ช่วงของเปียกมาก frost เกาะเร็ว แต่ timer ยังไม่ถึงเวลา คอยล์ตันไปแล้ว รีดน้ำช้า กินไฟเพิ่ม พอเส้นแห้ง frost ช้าลง timer กลับสั่ง defrost ถี่เกินจำเป็น เสียทั้งค่าฮีตเตอร์และภาระ re-cooling ฟรีๆ
สั่งตาม τ ที่วัดได้จริง
τ แย่ถึงเกณฑ์เมื่อไหร่ค่อยละลาย ปรับความถี่ตามน้ำที่ออกจากเส้นเอง เปียกมาก defrost ถี่ แห้งแล้วห่างออก ไม่ต้องแก้ตัวเลข timer เลย
COP กับค่าไฟ ทำไม frost แพงกว่าที่คิด
frost ไม่ได้แค่ทำให้เย็นช้า มันทำให้กินไฟต่อความเย็นแพงขึ้นด้วย เพราะพอ Q_evap ตก คอมต้องเดินนานขึ้นเพื่อดึงความร้อนก้อนเดิม ตรงนี้ต้องรู้ COP จริงของระบบก่อน อย่าเดา R404A ที่อุณหภูมิ low-temp แบบนี้ COP จริงจะอยู่ราว 0.4-0.6 ของ Carnot เท่านั้น ผมคำนวณจาก enthalpy จริงด้วย CoolProp ที่ Te/Tc ที่วัดได้:
import math
from CoolProp.CoolProp import PropsSI
def cop_r404a(Te_C, Tc_C, superheat=5.0, subcool=3.0, eta_isen=0.65):
Te, Tc = Te_C + 273.15, Tc_C + 273.15
p_low = PropsSI('P', 'T', Te, 'Q', 1, 'R404A') # ความดันด้าน evaporator
p_high = PropsSI('P', 'T', Tc, 'Q', 0, 'R404A') # ความดันด้าน condenser
# state 1: ทางออก evaporator (superheated)
h1 = PropsSI('H', 'T', Te + superheat, 'P', p_low, 'R404A')
s1 = PropsSI('S', 'T', Te + superheat, 'P', p_low, 'R404A')
# state 2: อัดแบบ isentropic ไป p_high แล้วหารด้วยประสิทธิภาพคอมจริง
h2s = PropsSI('H', 'P', p_high, 'S', s1, 'R404A')
h2 = h1 + (h2s - h1) / eta_isen
# state 3: ทางออก condenser (subcooled), state 4: หลัง expansion (h4 = h3)
h3 = PropsSI('H', 'T', Tc - subcool, 'P', p_high, 'R404A')
h4 = h3
cop = (h1 - h4) / (h2 - h1) # COP = Q_evap / W_comp
cop_carnot = Te / (Tc - Te) # เพดาน Carnot
return cop, cop_carnot
cop, ceil = cop_r404a(Te_C=-25.0, Tc_C=40.0)
print(f"COP(R404A) = {cop:.2f} Carnot ceiling = {ceil:.2f} ratio = {cop/ceil:.2f}")
# ~ COP 1.7, Carnot 3.8, ratio ~0.45 <- ตรงกับที่คาดไว้สำหรับ low-temp R404A
จุดที่ต้องจำคือ Te ยิ่งต่ำ (ห้องเย็นจัด) และ Tc ยิ่งสูง (วันร้อน คอนเดนเซอร์ระบายไม่ทัน) COP ยิ่งตก ยิ่งกินไฟต่อความเย็นหนึ่งหน่วยแพงขึ้น ส่วนกำลังไฟฟ้าที่วัดจากกระแสจริงต่อ 3 เฟส คิดง่ายๆ ด้วย P = √3·V·I·PF:
def power_3ph_kw(current_a, v_line=380.0, pf=0.85):
return math.sqrt(3) * v_line * current_a * pf / 1000.0
p_comp = power_3ph_kw(33.0) # กระแสคอมจาก register
p_defrost = power_3ph_kw(28.0, pf=1.0) # ฮีตเตอร์เป็น resistive -> PF = 1
print(f"comp ~ {p_comp:.1f} kW defrost heater ~ {p_defrost:.1f} kW")
รีดน้ำออกกี่กิโลต่อรอบ defrost
พอ defrost ละลาย frost น้ำก็ไหลออกทางท่อ drain ออกนอกระบบ นี่แหละคือขั้นที่เก็บเกี่ยวน้ำออกจากห้องจริงๆ (frost ที่เกาะคอยล์ก็คือน้ำที่ออกจากเส้นไปเกาะจุดเย็นสุด) ถ้าอยากรู้คร่าวๆ ว่ารีดน้ำออกกี่กิโลต่อรอบ ใช้พลังงาน defrost หารด้วยความร้อนแฝงการหลอมเหลว โดยหักส่วนที่ไปอุ่นโลหะ/อากาศทิ้ง:
h_sf = 334.0 # kJ/kg latent heat of fusion (ละลายน้ำแข็ง->น้ำ)
h_sg = 2838.0 # kJ/kg latent heat of sublimation (น้ำแข็ง->ไอ)
def water_harvested_kg(p_heater_kw, t_defrost_s, melt_fraction=0.6):
"""melt_fraction = สัดส่วนพลังงานฮีตเตอร์ที่ใช้ละลายจริง (ที่เหลืออุ่นโลหะ/ห้อง)"""
e_kj = p_heater_kw * t_defrost_s # kW * s = kJ
e_melt = e_kj * melt_fraction
return e_melt / h_sf
m = water_harvested_kg(p_heater_kw=18.4, t_defrost_s=600)
print(f"น้ำที่รีดออกราว {m:.1f} kg/รอบ defrost")
ค่านี้เป็นแค่ประมาณการ ทางที่แม่นกว่าคือชั่งน้ำหนักวุ้นเส้นก่อน/หลัง หรือวัดน้ำที่ drain ตรงๆ เพราะไม่มีเซนเซอร์ความชื้น อันนั้นคือ ground truth ที่ดีสุด ส่วนพลังงานที่ต้องป้อนเพื่อรีดน้ำออกจากเส้นเองนั้นสูงมาก น้ำ 1 kg ที่ระเหิด (sublimate) ออกไปต้องใช้ราว 2838 kJ ซึ่งคอยล์ต้องรับภาระนี้แล้วคายกลับมาเป็น frost อีกที เข้าใจตรงนี้แล้วจะเห็นว่าทำไม frost บนคอยล์ถึงทำให้อัตราการรีดน้ำช้าลง เพราะพอผิวคอยล์ถูก frost หุ้มจนอุ่นขึ้น ความดันไอที่ผิวก็สูงขึ้น แรงขับที่ดึงน้ำจากเส้น (ผลต่างความดันไอ) เลยลดลง อีกเหตุผลที่ต้อง defrost เพื่อคงอัตราแห้ง
defrost ถี่ไปคอยล์สะอาดตลอด Q สูง แต่เปลืองค่าฮีตเตอร์ + re-cooling + เวลาคอมหยุด · defrost ห่างไป frost หนา Q ตก เย็นช้า รีดน้ำช้า จุดเหมาะคือ demand-defrost ตาม τ ไม่ใช่ timer ตายตัว เป้าที่ผมเล็งคือ kWh ต่อ kg-น้ำ ต่ำสุด ไม่ใช่คอยล์สวยที่สุด
ต่อ pipeline เข้า cron
ที่เหลือคือทำให้มันเดินเอง ผมแยกเป็นสองขา ขาแรกตัวอ่านค่าจาก controller ผ่าน RS485 (Modbus RTU) แล้วต่อท้ายไฟล์ csv ขาสองตัวตัดสินใจที่รันเป็นระยะแล้วสั่ง defrost ผ่าน relay:
- poller อ่าน register (temp กลางห้อง, กระแสคอม, กระแสฮีตเตอร์ defrost) ทุก 1-5 วินาที เขียนลง csv
- ตัวตัดสินใจอ่าน csv ย้อนหลัง ตัดรอบ pull-down กรอง comp ON + defrost OFF
- fit τ ทีละรอบ เทียบ baseline ถ้าแย่เกินเกณฑ์สั่ง defrost แล้วรีเซ็ต baseline ใหม่หลังละลายเสร็จ
#!/usr/bin/env bash
# coldroom-poll.sh — อ่าน RS485 แล้วต่อท้าย csv (ตัวอย่าง register/พอร์ต)
PORT=/dev/ttyUSB0 # USB-RS485 adapter
UNIT=1 # Modbus unit id
OUT=/var/log/coldroom/telemetry.csv
mkdir -p "$(dirname "$OUT")"
while true; do
ts=$(date +%s)
# reg4=temp*10(°C), reg8=กระแสคอม*10(A), reg11=กระแสฮีตเตอร์*10(A)
read t i_comp i_def < <(python3 /opt/coldroom/read_regs.py "$PORT" "$UNIT" 4 8 11)
echo "$ts,$t,$i_comp,$i_def" >> "$OUT"
sleep 5
done
ตัวตัดสินใจไม่ต้องรันค้าง ให้ cron เรียกทุก 5 นาทีพอ อ่าน csv ย้อนหลังพอให้ครอบคลุมรอบ pull-down ล่าสุด:
# /etc/cron.d/coldroom-defrost
*/5 * * * * coldroom /opt/coldroom/venv/bin/python /opt/coldroom/defrost_decide.py >> /var/log/coldroom/defrost.log 2>&1
เดินมาถึงตรงนี้ผมได้ตัวเลขที่จับต้องได้แทนการเดา τ ตัวเดียวเล่าได้ทั้งว่าคอยล์ตันแค่ไหน ควรละลายตอนไหน และรีดน้ำออกได้เท่าไหร่ต่อรอบ ทั้งหมดจากอุณหภูมิกับกระแสไฟที่มีอยู่แล้ว ไม่ต้องซื้อเซนเซอร์ความชื้นเพิ่มเลย ใครจะเอาโครงนี้ไปปรับ เกณฑ์ 30% กับ melt_fraction ควรจูนตามเครื่องของตัวเองนะครับ ค่าที่ผมใส่เป็นจุดตั้งต้น ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย