「太陽能調度為什麼不用 reinforcement learning?AlphaGo 都能解圍棋,這個應該很簡單啊。」
朋友看到我們 Solar Monitor 系統的 48h 規劃引擎是「暴力枚舉」(brute-force enumeration),第一反應通常是這句。
確實,「能源調度」很容易讓人聯想到深度學習 / RL — 變數多、決策連續、跨時域影響。但我們做了一年實戰,結論是純 Python 暴力枚舉 5 萬個 candidate、1 秒算完,比 ML 更實用。
這篇拆「為什麼」+「演算法怎麼長的」。
問題長什麼樣 #
我們自家機房太陽能 + 儲能系統的調度決策:
每 1 小時要決定一次: 接下來 1 小時用什麼模式供電?
inverter:純電池放電(PV 進電池 + 電池供負載)grid_A:市電線路 A 供電(PV 仍進電池、市電付負載費)grid_B:市電線路 B 供電(同上、但 B 線路費率不同)
24-48 小時的決策序列要最佳化什麼:
- 總電費最低(target)
- 電池水位不能低於安全線(constraint,避免硬體保護觸發)
- 不能無腦切換(constraint,逆變→市電中間 5 分鐘過渡)
輸入變數:
- 當前電池可用 AH
- 當前供電模式
- 未來 24-48h 每小時的 PV 預估(Solcast API)
- 未來 24-48h 每小時的負載歷史平均
- 24-48h 兩條線路的費率(含累進制 / 時段)
輸出:一個 24-48 步的決策序列。
為什麼第一念是 ML #
如果你跟我們一樣是工程師,看到上面這個 problem 直覺會想:
- 變數多 → 神經網路擅長
- 跨時域 → LSTM / Transformer
- 決策連續 → Reinforcement Learning
- 目標可量化(cost) → reward function 寫得出來
確實長得很 ML。我們最早也評估過。
但其實維度很小 #
仔細算:每小時決定 3 種 mode 之一、24-48 小時、有 transition constraint。用 combinatorics 算:
# 每小時可選 3 mode
# 24 小時、最多 4 個切換點
candidates_24h = C(23,0)·1 + C(23,1)·3 + C(23,2)·3² + C(23,3)·3³ + C(23,4)·3⁴
= 1 + 69 + 2277 + 35,937 + 167,580
≈ 200,000
# 但加上 transition constraint 過濾 → 剩 ~50,000
# 加上 "電池低於安全線" 過濾 → 剩 ~5,000 可行方案
5,000 個可行方案,每個跑一次 simulation = 24 個 hour step × 簡單的加減乘除 = 不到 100ms。
48 小時 + 最多 6 切換點,candidate 量大概 50 萬,總時間 ~1 秒。
這是個「枚舉就枚舉得完」的問題。用 ML 等於拿大砲打蚊子。
暴力枚舉演算法 #
核心邏輯(簡化版):
from itertools import combinations, product
MODES = ('inverter', 'grid_A', 'grid_B')
HARD_FLOOR_AH = 100.0 # 電池下限(高於硬體保險線 50 AH)
def plan_switching(current_ah, current_mode, capacity_ah,
pv_per_h, load_per_h, rate_a_per_h, rate_b_per_h):
best = None
candidates = 0
boundaries = list(range(1, len(pv_per_h))) # 可切換的時間點
# 枚舉「在哪幾個整點切換」
for n in range(0, MAX_SWITCHES + 1):
for switch_hours in combinations(boundaries, n):
# 對每個切點枚舉切到哪個 mode(3^n 種)
for mode_combo in product(MODES, repeat=n):
plan = build_plan(current_mode,
list(zip(switch_hours, mode_combo)))
# 過濾:不合法轉換
if not is_valid_transitions(plan, current_mode):
continue
# 模擬電池水位 + 累計電費
cost, min_ah, min_ah_hour = simulate(
plan, current_ah, capacity_ah,
pv_per_h, load_per_h,
rate_a_per_h, rate_b_per_h
)
# 過濾:電池水位掉到 HARD_FLOOR 以下
if min_ah < HARD_FLOOR_AH:
continue
candidates += 1
if best is None or cost < best['cost']:
best = {'plan': plan, 'cost': cost,
'min_ah': min_ah, 'min_ah_hour': min_ah_hour}
return best, candidates
暴力枚舉 vs ML 對比 #
實戰跑下來的差異:
| 項目 | ML / RL | 純枚舉 |
|---|---|---|
| 需要訓練資料 | 一年起跳 | 不需要 |
| 模型解釋性 | 黑箱 | 每個 candidate 都列得出來、可解釋 |
| 領域知識怎麼放進去 | 包進 reward function(難) | 直接寫成 filter / constraint(易) |
| 改規則的成本 | retrain | 改變數重跑 1 秒 |
| 邊界場景 | 訓練資料外表現不穩 | candidates=0 explicit 警告 |
| 維運成本 | 模型 retrain / monitor drift | 沒 |
| 解釋給老闆 / 客戶 | 「神經網路覺得這樣比較好」 | 「枚舉 50,000 種,這個成本最低 + 安全」 |
| 跨域 transfer | 重新訓練 | 改變數 |
特別是最後一條 — 我們的客戶(中小工廠 / 倉儲 / 商家)老闆看到 ML 第一反應是不信任。暴力枚舉的結果可以列表給他看:
我們算了 5,000 個可行方案。前 3 名:
方案 A:23:00 切 grid_A、06:00 切 inverter
- 預估成本 NT$ 23.4
- 最低電池 187 AH(明早 06:00)
- 安全度 ★★★★★
方案 B:22:00 切 grid_A、07:00 切 inverter
- 預估成本 NT$ 24.1
- 最低電池 195 AH
- 安全度 ★★★★★
方案 C:00:00 切 grid_B、05:00 切 inverter
- 預估成本 NT$ 25.8(B 線路目前便宜)
- 最低電池 165 AH
- 安全度 ★★★★
老闆看一眼就懂。「神經網路 confidence 0.87」是什麼,沒人懂。
演算法設計原則(踩過坑後立的) #
我們做了一年下來,幾條硬規則:
❌ 不寫死特定情境 #
常見錯誤:
if hour == 0 and mode == "grid_A":
switch_to_inverter()
問題:天氣 / 假日 / 季節 / 電費改了之後規則全部要重寫。
正解:把所有「情境」轉成數字輸入(PV、負載、費率),讓暴力枚舉自然找出來。
❌ 不設軟體 AH 安全閾值(同時保留物理保險) #
常見錯誤:
if soc < 20:
force_switch_to_grid() # 硬編碼,會跟演算法打架
我們碰過「設了軟體閾值 → 演算法 candidate 撞線 → 一直找不到可行方案 → 反而不如沒設」。
正解:
- 硬體 BMS 設物理保險(電壓低於 48.8V 自動斷逆變)— 這是底線
- 演算法層的
HARD_FLOOR_AH是「比物理保險高 50 AH 的 buffer」,不是強制限 - 演算法過濾掉違反 HARD_FLOOR 的 candidate,但允許 candidate 存在(不是 mask 掉決策空間)
✅ 結果回傳「還有幾個方案」(彈性指標) #
我們的 output 不只「最佳方案」,還回傳 alternative_count:
return {
'best_plan': [...],
'total_cost': 23.4,
'min_ah_estimate': 187,
'alternative_count': 42, # 過濾後還有幾個合法 candidate
}
這個數字是系統彈性指標:
- 100+ candidates = 餘裕大,多種方案差不多 → 可以放心
- < 10 candidates = 系統很緊繃,PV/負載稍微誤差就違法 → 警示
- 0 candidate = 暫時沒解,需要強制切市電或叫人來看 → 紅燈
✅ 不合法 transition 用 filter 而非 mask #
「逆變→市電中間需要 5 分鐘過渡」這種規則用 filter:
def is_valid_transitions(plan, current_mode):
# 切換後 1 小時內不允許再切(防震盪)
last_switch_hour = None
last_mode = current_mode
for h, mode in enumerate(plan):
if mode != last_mode:
if last_switch_hour is not None and h - last_switch_hour < 1:
return False
last_switch_hour = h
last_mode = mode
return True
比「在演算法搜索時就 mask 違法 transition」乾淨 — filter 容易測 / 容易擴充。
真的有需要 ML 的時候是什麼 #
不是說 ML 永遠不適用。維度爆炸 + reward 難明寫的場景 ML 才有意義:
| 場景 | 是否該用 ML | 為什麼 |
|---|---|---|
| 我們這種(3 mode × 24-48h) | ❌ | 維度太小,枚舉夠 |
| 100 個機台動態排程 | ⚠️ 可考慮 | 維度大但仍可用啟發式 / ILP |
| 多廠區 + 多儲能 + 多市電合約最佳化 | ✅ 可能 | 維度真的大 |
| AlphaGo / 自駕車 | ✅ | 維度爆炸 + 無法明列 reward |
中小企業的能源管理 95% 屬於第 1 類。枚舉就夠用。
雙引擎:暴力枚舉 + AI 簡報 #
我們系統有兩條獨立決策路徑:
[每天 07:00 / 17:00 觸發]
├──┬─→ 純 Python 引擎(暴力枚舉)
│ │ ↓ 1 秒
│ │ best_plan = {...}
│ │
│ └─→ AI 簡報引擎(Claude API)
│ ↓ 3-5 秒
│ briefing_text = "凌晨逆變到 127AH..."
│
└──→ UI 同時呈現兩個結果
└─→ 操作員 review,符合直覺才執行
Python 負責「算最佳解」、AI 負責「翻譯給人聽」 — 各司其職。
兩條引擎結果一致 → 直接執行;不一致 → 警示 + 人工介入。
詳細的雙引擎設計:裝了太陽能就沒事了?沒有 — 真正的省錢是「管理」
同套架構可用在的工業場景 #
把「太陽能」三個字抽掉,這個暴力枚舉骨幹可以套到:
- 冰水主機預冷(廠房空調夜間預冷利用離峰)
- EV 充電站排程(多車多樁的功率分配)
- 生產線負載平衡(避開電費尖峰時段啟動高耗能機台)
- 資料中心節能(離峰時段集中跑 batch job)
- 製程批次排程(不同 batch 的時間 / 資源最佳化)
只要「決策步驟 < 50、每步 mode 種類 < 10、有 cost function 可量化」,暴力枚舉就比 ML 實用。
重點 #
| 觀點 | 結論 |
|---|---|
| 中小規模能源調度 | 暴力枚舉 > ML |
| 解釋性 | 枚舉 ≫ ML(重要時刻決勝負) |
| 開發 / 維運成本 | 枚舉 ≪ ML |
| ML 真正適用 | 維度爆炸 + reward 難明寫,中小企業少有 |
| 我們的架構 | 暴力枚舉 + AI 翻譯,雙引擎並行 |
「用對的工具」比「用最炫的工具」省錢。我們做了一年實戰才確定這條路是對的。
想做類似的工業監控 / 排程 #
我們做太陽能 + 儲能 + 工業自動化監控平台,含演算法層客製。
詳見 Solar Monitor 產品頁,或聊聊:
- 評估你的場景(多少設備、多少維度、多少決策步驟)
- 規劃書 + 報價(諮詢免費)
- 完整交付(硬體 + 軟體 + AI 整合 + 演算法客製)
聯絡:0912852835 / henryccy@icloud.com / LINE @3q3tw / 線上表單
延伸閱讀: