Всем привет. В статье на geektimes я рассказывал, как подключиться к электросчетчику Eastron SDM220-Modbus и забрать с него данные по шине RS-485. Сегодня я хочу рассказать про сбор и анализ статистических данных о потреблении электричества в доме.



В качестве домашнего хаба для сбора данных я использовал тонкий клиент Centerm GI-945. Его плюсы — x86 архитектура (atom 1,6 GHz), 5 USB, 1G Ethernet, mini-pcie (поставил в него wifi-карту). С флешки грузится Ubuntu Server 14.04.





Через USB-RS485 адаптер к «серверу» подключен электросчетчик. Опрос счетчика делает скрипт на Python, за основу взял этот пример. Для работы скрипта нужна библиотека pyModbus

Установка библиотеки
sudo add-apt-repository ppa:fkrull/deadsnakes-python2.7
sudo apt-get update
sudo apt-get upgrade

apt-get -y install python-pip
apt-get install python2.7-dev

pip install -U pymodbus

Скрипт запускается по cron раз в минуту, забирает данные (напряжение, ток, мощность и учтенная энергия) со счетчика и отправляет их на сайт ThingSpeak.

На сайте предварительно нужно зарегистрироваться, создать свой канал данных channel и назвать поля fields. Также для записи данных в канал понадобится write API key.



Код скрипта на Python
#!/usr/bin/python2
import struct
import pymodbus.client.sync
import binascii
import time
import sys
import urllib

def read_float_reg(client, basereg, unit=1):
resp = client.read_input_registers(basereg,2, unit=1)
if resp == None:
return None
# according to spec, each pair of registers returned
# encodes a IEEE754 float where the first register carries
# the most significant 16 bits, the second register carries the
# least significant 16 bits.
return struct.unpack('>f',struct.pack('>HH',*resp.registers))

def fmt_or_dummy(regfmt, val):
if val is None:
return '.'*len(regfmt[2]%(0))
return regfmt[2]%(val)

def main():
regs = [
# Symbol Reg# Format
( 'V:', 0x00, '%6.2f' ), # Voltage [V]
( 'Curr:', 0x06, '%6.2f' ), # Current [A]
( 'Pact:', 0x0c, '%6.0f' ), # Active Power («Wirkleistung») [W]
( 'Papp:', 0x12, '%6.0f' ), # Apparent Power («Scheinl.») [W]
( 'Prea:', 0x18, '%6.0f' ), # Reactive Power («Blindl.») [W]
( 'PF:', 0x1e, '%6.3f' ), # Power Factor [1]
( 'Phi:', 0x24, '%6.1f' ), # cos(Phi)? [1]
( 'Freq:', 0x46, '%6.2f' ), # Line Frequency [Hz]
( 'Wact:', 0x0156, '%6.2f' ), # Energy [kWh]
( 'Wrea:', 0x0158, '%6.2f' ), # Energy react [kvarh]
]

cl = pymodbus.client.sync.ModbusSerialClient('rtu',
port='/dev/ttyUSB0', baudrate=9600, parity='N',stopbits=1,
timeout=0.8)

values = [ read_float_reg (cl, reg[1], unit=1) for reg in regs ]
outvals = list((' '.join([fmt_or_dummy(*t) for t in zip(regs, values)])).split())
params = urllib.urlencode({'key': 'xxxxxxxxxxxxxxxx', 'field1': outvals[0], 'field2': outvals[1], 'field3': outvals[2], 'field4': outvals[8]})
f = urllib.urlopen(«api.thingspeak.com/update», data=params)
print(outvals)
sys.stdout.flush()


if __name__ == '__main__':
main()

Скрипт забирает все доступные параметры со счетчика, но на сайт передаются только основные, при желании можно передавать все.

Результат сразу будет отображаться на сайте (на скрине показана статистика за некоторое время, при первом опросе там будет только одна точка со значением)



По умолчанию графики отображают последние 60 показаний, что при опросе раз в минуту дает результат за последний час. Для каждого графика можно настроить отображение



Для напряжения настроил статистику за 6 часов с усреднением по 10 значений и сглаживанием кривой (spline).

Видно, что график потребленной энергии — это постоянно возрастающая кривая, а хотелось бы видеть расход за час, сутки и месяц, для этого на сайте есть аналитика, основанная на движке MATLAB и возможность запускать события по времени.

Для сбора почасовой статистики сделал следующее:

  1. Создал второй канал PowerStatistic;

  2. В Apps -> MATLAB Analysis создал скрипт, который берет текущее значение энергии и вычитает из него значение час назад. Результат заносится в поле канала;

  3. В Apps -> Time Control создал событие GetPowerPerHour, которое будет каждый час в 00 минут запускать матлабовский скрипт.

Скрипт PowerPerHour
% ID исходного канала, в который помещаются данные со счетчика, если канал приватный, нужно указать Read API Key
readChannelID = 154291;

% ID канала для записи обработанных данных (PowerStatistic)
writeChannelID = 157182;
writeAPIKey = 'xxxxxxxxxxxxxxxxxx';

%% Read Data %%
data1 = thingSpeakRead(readChannelID, 'Fields', 4);
data2 = thingSpeakRead(readChannelID, 'Fields', 4, 'NumMinutes', 60);
value = data1-data2(1);

%в data1 читаем последнее значение из 4-го поля (Energy)
%чтение в data2 сделано криво, но по другому я пока не придумал, читаются последние 60 значений (за час) и берется первое из них

%% Analyze Data %%
% пришлось добавить округление до двух знаков, так как несмотря на то, что в исходном массиве значения лежат округленные до двух знаков после запятой, результат вычитания почему-то выглядел так: 0.2700000000000031
analyzedData = round(value,2);

%disp можно использовать для отладки данных
%disp(analyzedData);

Если в analyzedData одно значение, а не вектор, оно будет записано в первое поле канала. Для записи в другие поля нужно добавить 'Fields',2, например.
%% Write Data %%
thingSpeakWrite(writeChannelID, analyzedData, 'WriteKey', writeAPIKey);

На сайте есть неприятный косяк, для применения скрипта есть кнопка Save&Run, которая при каждом запуске делает запись в канал, поэтому до полной отладки строку записи лучше закомментировать, однако даже когда скрипт готов, его нельзя просто сохранить без запуска, а запуск скрипта сразу занесет данные в канал. Удалить индивидуальные данные из канала тоже нельзя. Этот вопрос уже поднят на форуме проекта, но пока не исправлен.



Настройки запуска по расписанию, скрипт запускается каждый час. На то, что начало запуска в 12.00 можно не обращать внимание.

Смотрим в канале результат:



Для этого графика в настройках указан Type: column для отображения расхода электроэнергии по часам. Всплывающее окно соответствует пику потребления в 12 часов (курор на скрине не отобразился).

Аналогичным образом работает скрипт, собирающий статистику за сутки. Скрипт запускается в 00:01 каждого дня и суммирует 24 показания из почасового архива.

Скрипт PowerPerDay
readChannelID = 157182;
readAPIKey = 'zzzzzzzzzzzzzzzzzzzzz';

writeChannelID = 157182;
writeAPIKey = 'xxxxxxxxxxxxxxxxxxxx';

%% Read Data %%
% читаем последние 24 значения из первого поля канала
data = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields',1, 'NumPoints',24);

%% Analyze Data %%
%суммируем
analyzedData = sum(data);

%disp(analyzedData);

%% Write Data %%
thingSpeakWrite(writeChannelID, analyzedData, 'WriteKey', writeAPIKey, 'Fields',2);

Для ThingSpeak также есть приложения под Android. Для просмотра последних значений мне понравился Pocket IoT, а для графиков — ThingView. Есть также пара виджетов, но они какие-то кривые.

» Канал PowerMeter
» Канал PowerStatistic

На этом, пожалуй, все. Статья не претендует на tutorial, так как я только начал знакомиться с возможностями ThingSpeak, любые дополнения, замечания и правки приветствуются.
Поделиться с друзьями
-->

Комментарии (4)


  1. nevzorofff
    09.09.2016 13:05

    Расскажите в следующих частях чего интересного/неожиданного намерили.


    1. alk0v
      09.09.2016 13:08

      На отдельную часть это не тянет, но в целом статистика забавная. Видно, как работает холодильник ночью, сколько утром потребляют чайник, тостер и микроволновка. Кстати, сдавал этот счетчик на метрологическую поверку, в свой первый класс точности входит с трехкратным запасом.


  1. ternaus
    09.09.2016 17:40

    На предыдущей работе — стартапе под названием Bidgely, занимался тем, что писал алгоритмы, которые из временных рядов потребления энергии типа того, что выше вычленяли компоненты которые соответствуют стиральной машине, холодильнику, кондиционеру, насосу в бассейне и т. д. — крайне востребованный анализ в Европе, США и Австралии для тех, кто пытается понять, как сократить издержки на электричество.

    Это была очень интересная задача. Мы под неё какие-то велоспеды придумывали, но, видится мне, задача очень похожа на распознование голоса, так что я пытался раскрутить начальство начать работать в сторону рекурентных нейронных сетей. Но индусы этой идеей не прониклись, да и я ушёл в другое место.


    1. alk0v
      09.09.2016 17:57

      Есть интересный проект ecois.me, там подобная аналитика делается на основании анализа спектра сигнала в сети, тоже позволяет после некоторого обучения отличить одну нагрузку от другой. Мне, честно говоря, такие подробности не сильно нужны, достаточно посуточного учета электроэнергии и наблюдения текущих показаний.