Від телеметрії до рішень: як я будую керування домашньою сонячною станцією
Сто років тут нічого не писав, тож спробую вас зацікавити темою advanced automation.
Останні 1,5 місяця у вільний від дитини і роботи (читайте — вночі) час я розробляю систему управління електроспоживанням будинку. Основна проблема, яку я хочу вирішити: перекласти оптимізацію споживання на рушій прийняття рішень, який буде автоматично вирішувати, коли, як і з якого джерела (сонце, батарея, мережа) заряджати авто, коли гріти воду і скільки можна використати енергії на нагрів, буде приймати до уваги потенційні відключення, зважувати ризики.
І все це має бути побудовано на базі певних стратегій, наприклад, максимізація self-consumption (тобто не брати з мережі), оптимізація витрат (брати з мережі, але коли дешево і нема інших джерел). При використанні даних це дозволяє приймати кращі рішення в моменті, робити постійний evaluation рішень, планувати енерговитрати та оптимізовувати ціну таких витрат.
Це досить комплексна система. Щоб розказати все, буде серія статей, якщо ця вам сподобається.
Давайте почнемо з того, що у нас є:
1. Сонячна станція, що складається з трьох інверторів Victron MultiRS Solar обʼєднаних в трифазну систему.
2. 40кВт акумуляторів Pytes 48100R (8 шт. по 5 кВт).
3. Три поля сонячних панелей: південний схід (12 шт.), південний захід (10 шт.) та північний захід (10 шт.). Загальна потужність поля — близько 14 кВт. Очевидно, що моментальна потужність менша, адже поля рознесені в різні напрями.
4. В системі є трифазна зарядна станція для машини від Victron. Розміщена на inverter ac-out (тобто за інвертором).
5. Будинок опалюється тепловим насосом повітря-вода Viessmann Vitocal
6. Є багато лічильників енергії, в основному це Shelly-пристрої. Також стоїть ввідний лічильник Victron на вході інверторів.
Тепер давайте трошки про систему.

Основна мова розробки — C#, .net 10. Хоститься локально на сервері в Docker-контейнері, розділена на три окремі сервіси. Система використовує:
- Influx для зберігання телеметрії в час;
- Postgress для збереження даних прогнозів, налаштувань системи, інтеграції;
- Seq як простий сервіс логів.
Edge.Web
Це простий сервіс .NET MVC, який візуалізує дані, дозволяє налаштовувати систему, відстежувати її дії в часі, трейсити прийняті рішення та вибирати режими роботи. Він також надає широку аналітику в часі та стан системи в режимі реального часу.
Edge.Host
Хост-сервіс телеметрії та рішень. Це головний runtime-процес системи. Саме він збирає телеметрію з енергосистеми, підтримує актуальний стан установки, запускає фонові інтеграції та виконує оперативну логіку керування.
1. Збір даних у реальному часі з Victron реалізовано через підключення до MQTT-сервера на Victron Cerbo GX (це такий управляючий пристрій у Victron). Edge.Host підписується на ключову телеметрію: генерацію від сонця, споживання, роботу батареї, мережу та EV-стан. Ці повідомлення він не просто приймає, а нормалізує в єдину модель стану системи.
2. Для історії та аналітики Edge.Host агрегує телеметрію в похвилинні buckets і зберігає її в InfluxDB. За рахунок цього ми маємо не лише «поточне значення зараз», а й детальну похвилинну картину по основних енергетичних потоках: сонце, мережа, споживання, батарея, зарядка авто.
3. Поверх цього Edge.Host тримає live-зріз системи і кожні 30 секунд запускає decision loop. На цьому циклі сервіс оцінює поточний стан установки, підтягує прогнози й налаштування та вирішує, як система має поводитися далі, оновлює та підтримує State Machine всієї установки
4. Окремо Edge.Host піднімає оперативні HTTP endpoints: для live-телеметрії, статусу системи, ручного refresh прогнозів, перегляду decision traces і технічної діагностики. Тобто це не просто ingestion-сервіс, а центральний orchestration layer.
Host інтегрує наступну телеметрію:
- MQTT life телеметрію з Cerbo GX.
- Телеметрію з підключених пристроїв Shelly (тут pull кожні 5 сек, інтервал налаштовується для кожного пристрою).
- Телеметрію з підключених інтеграцій, наприклад для теплового насосу через Viessmann API.
Host управляє сонячною станцією через Modbus TCP, через нього також читає актуальний стан пристроїв та перевіряє виконання команд.
Edge.Forecast.Worker
Є центральним вузлом прогнозування на наступні періоди. Якщо Edge.Host відповідає за live-стан системи та оперативні рішення, то worker відповідає за регулярну побудову прогнозів і підготовку плану на добу вперед (насправді до кінця поточного дня + наступний день).
1. У ньому живе production-інференс
2. На кожному циклі worker бере історичну та актуальну телеметрію й будує
3. Далі Edge.Forecasts.Worker зберігає ці прогнози в PostgreSQL як окремі версіонізовані snapshots. Коли є повний і валідний набір погодинних прогнозів, він на їх основі публікує operational schedule, який вже може використовуватися далі в системі.

4. Окремо worker підтримує ML data pipeline: оновлює датасети для активних моделей. Кожен день вночі він доповнює датасет свіжими даними за вчора.
Якщо дуже коротко, то Edge.Forecasts.Worker це окремий forecasting engine: він бере телеметрію, проганяє її через
- Погоду тягнемо з Open Meteo.
- Через Victron VRM API тягнемо прогноз по споживанню та сонцю. Це робиться як fall-back стратегія та для порівняння різних прогнозів. Я можу вибрати, що саме використовувати в системі — ML прогноз чи прогноз, який будує сам Victron.
Прогнози оновлюються кожну годину, кожну годину перебудовується Daily Schedule, який на картинці вище. Кожне оновлення прогнозу = новий snapshot. Якщо по якійсь причині не вийшло створити новий snapshot, система використовує попередній успішний, але така проблема логується, а система піднімає рівень песимізму в прийнятті рішень.
Думаю, з оглядом сервісів ми завершили. В цій статті хочу розповісти, як саме будується
ML прогнозування
В системі тренуються три основні моделі: SOC_1h, Consumption_1h, PV_1h. З назв ясно, що вони всі погодинні. У мене була ідея будувати коротші інтервали, типу 15 хв, але на практиці виявилося не дуже практично, так як виконання прогнозу все одно можна відстежувати в реальному часі.
Для прогнозування використовую LightGBM — це відносно проста для використання
Навчання розділено від runtime, тобто я запускаю навчання вручну, нові артефакти створюються, а далі приймаю рішення на базі результатів навчання: якщо MAE (Mean Absolute Error) та RMSE (Root Mean Squared Error) покращилися, то йде в прод.
Модель SOC_1h
В цій прогнозній моделі використовуються наступні features:
month | номер місяця |
day_of_week | номер дня тижня |
hour_of_day | локальна година доби |
is_daylight | 0/1 індикатор світлої доби |
battery_soc_lag_1h | стан заряду батареї годину тому |
battery_soc_last_15m | стан заряду батареї 1 хв тому |
net_balance_next_hour | баланс енергії в наступну годину (планована генерація — плановане споживання) |
pv_to_battery_last_1h | кількість енергії що з сонця перетікла в акумулятор за останню годину |
hourly_pv_forecast_kwh | прогноз сонячної генерації на поточну годину прогнозування |
grid_to_battery_last_1h | кількість енергії, що перетікла з мережі в акумулятор за останню годину |
net_battery_energy_last_hour | баланс енергії акумулятора (заряд мінус розряд) за минулу годину |
battery_charge_energy_last_15m | кількість енергії яка пішла в акумулятор за останні 15 хвилин |
battery_discharge_energy_last_15m | кількість енергії яка пішла з акумулятора за останні 15 хвилин |
Такий feature-set дає достатньо даних для того, щоб реагувати на зміни в системі та давати виключно якісний прогноз. Наприклад, графік за один з днів:

Видно, що модель (пунктир) зреагувала на розряд батареї вночі (зарядка авто йшла частково з батарейки) і майже 1-в-1 слідує за фактичними даними. Прогноз перераховується погодинно, кожен новий прогноз — новий snapshot. Можна відстежити всі дані, які були вибрані для прогнозування, експортувати весь feature set або видалити цей прогноз (він буде регенерований автоматично, якщо Worker не знайде активний, діючий, опублікований snapshot на поточну годину).

На даний момент дата сет моделі для тренування налічує 1032 рядки, що дорівнює 42 дням історичних даних.
Модель Сonsumption_1h
В цій прогнозній моделі використовуються наступні features:
| Feature | Value |
|---|---|
month | номер місяця |
is_weekend | 0/1 індикатор вихідного дня, бо там є залежність: на вихідних споживання менше |
day_of_week | номер для тижня |
hour_of_day | локальна година доби |
consumption_lag_1h | споживання будинку за минулу годину |
consumption_lag_2h | споживання будинку позаминулу годину |
consumption_lag_24h | споживання будинку в цю саму годину 24 години тому |
ev_charge_sum_last_1h | витрата енергії на зарядку авто в минулу годину |
ev_is_charging_last_15m | 0/1 чи машина заряджалась останні 15 хв |
weather_temperature_2m_c | температура повітря в прогнозну годину |
Найбільшими драйверами в цій моделі є, звичайно, витрати на зарядку машини. Ось приклад реакції моделі на зарядку машини:

Синім — фактична витрата енергії на заряджання авто.
Так як споживання — дуже волатильне, то погрішність моделі на даний момент складає близько 7% від фактичного в середньому. Наприклад, за вчора прогноз — 69 кВт, а факт — 75 кВт. А позавчора 58 кВт vs 59 кВт. Неможливо передбачити умовне прання, посудомийку і т. д.
На даний момент датасет моделі для тренування налічує 1032 рядки, що дорівнює 42 дням історичних даних.
Модель PV_1h
В цій прогнозній моделі використовуються наступні features:
| Feature | Value |
|---|---|
month | номер місяця |
day_of_week | номер для тижня |
hour_of_day | локальна година доби |
clouds_last_hour | % хмарності за минулу годину. ці дані йдуть з прогнозу погоди |
pv_actual_last_hour | фактична генерація за минулу годину |
radiation_last_hour | solar radiation за минулу годину |
pv_forecast_last_hour | ML прогноз по генерації за минулу годину |
visibility_next_hour_m | «видимість», тобто прозорість атмосфери |
weather_code_next_hour | погодний код на наступну годину, який надає провайдер погоди |
clouds_forecasted_next_hour | % хмарності на наступну годину |
radiation_forecasted_next_hour | solar radiation на наступну годину |
relative_humidity_next_hour_pct | відносна вологість в наступну годину |
direct_normal_irradiance_last_hour | кількість прямої сонячної енергії в Вт/м2 за минулу годину |
direct_normal_irradiance_next_hour | кількість прямої сонячної енергії в Вт/м2 на наступну годину |
precipitation_probability_next_hour | ймовірність опадів на наступну годину |
Всі сонячні та погодні дані, крім факту і прогнозу генерації, беруться з прогнозу Open Meteo. Worker погодинно оновлює прогноз свіжими даними, які далі використовуються в прогнозуванні.
Це — найбільший датасет: у ньому 9768 рядків, тобто 407 днів даних. Звідки стільки даних, якщо система працює лише 1,5 місяця? Я витягнув їх з VRM API, погодинну історію за останній рік і доповнив своїми даними. Віктрон зберігає дані по установці і є можливість витягнути їх, це дозволило доповнити історичний горизонт.
PV forecast не суперточний в моменті, тому що сонячна генерація залежить від дуже багатьох факторів, які неможливо передбачити. Але дуже точний на рівні дня, враховуючи, що десь він себе переоцінив, а десь навпаки — був сильно песимістичним.

Зазвичай прогноз промахується в меншу сторону, тобто генерації трохи більше. Наприклад, вчора прогноз — 52 кВт, факт — 59 кВт, позавчора (був складний день по сонцю) прогноз — 27 кВт, факт — 32 кВт.
Замість висновку. І що далі
Сподіваюсь, тему я зачепив цікаву. Все в одну статтю не вкладеш, тому далі думаю будуть наступні статті:
- Оркестрування прийняття рішень.
- Інтеграція різних пристроїв.
Ну, як мінімум, це. Дякую всім за читання і буду радий відповісти на питання (але не обіцяю, що швидко відповідатиму).
4 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарівЦікаво продовження про оркестрування та інтеграції.
скоро буде
Файна стаття, Євгене! Приємно бачити твою статтю тут у нас)
Гарно!