项目概述
智能家庭能源管理系统(Smart Home Energy Management System, SHEMS)是一个集成了物联网、云计算和人工智能技术的综合性解决方案。该系统通过嵌入式设备实时采集家庭能源消耗数据,利用Python全栈技术实现数据分析、可视化展示和智能控制,帮助用户优化能源使用、降低电费开支并减少碳足迹。
核心功能
- 🔌 实时能耗监测: 通过智能插座和传感器实时监控各类家电能耗
- 📊 数据可视化分析: 多维度展示能耗数据,生成详细的能源使用报告
- 🤖 智能预测与建议: 基于历史数据预测能耗趋势,提供节能建议
- ⚡ 自动化控制: 根据预设规则或AI决策自动控制家电开关
- 💰 费用计算与优化: 实时计算电费,根据峰谷电价优化用电策略
- 📱 多平台访问: 支持Web、移动端和微信小程序访问
系统架构设计
整体架构
┌─────────────────────────────────────────────────────────┐
│ 前端展示层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Web前端 │ │ 移动App │ │ 微信小程序 │ │
│ │ Vue.js │ │ Flutter │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
↕ RESTful API / WebSocket
┌─────────────────────────────────────────────────────────┐
│ 后端服务层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Django / FastAPI 应用服务器 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │用户管理 │ │设备管理 │ │数据分析 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │AI预测 │ │定时任务 │ │告警服务 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
↕ MQTT / HTTP
┌─────────────────────────────────────────────────────────┐
│ 物联网中间件层 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ MQTT Broker │ │ Redis │ │
│ │ (Mosquitto) │ │ (消息队列) │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
↕ MQTT
┌─────────────────────────────────────────────────────────┐
│ 嵌入式设备层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │智能插座 │ │环境传感器 │ │电表模块 │ │
│ │ESP32 │ │ESP8266 │ │STM32 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────┐
│ 家庭电器设备 │
│ 空调 · 冰箱 · 洗衣机 · 热水器 · 照明 · 其他 │
└─────────────────────────────────────────────────────────┘
技术栈选型
前端技术:
- Vue 3 + TypeScript + Vite
- Element Plus / Ant Design Vue
- ECharts / D3.js(数据可视化)
- Axios(HTTP客户端)
- WebSocket(实时通信)
后端技术:
- Django 4.x / FastAPI(Web框架)
- Django REST Framework(API开发)
- Celery(异步任务队列)
- scikit-learn / TensorFlow(机器学习)
- pandas / numpy(数据分析)
数据库:
- PostgreSQL(关系型数据库)
- InfluxDB(时序数据库)
- Redis(缓存和消息队列)
嵌入式技术:
- ESP32 / ESP8266(WiFi模块)
- MicroPython / C++(嵌入式开发)
- MQTT协议(设备通信)
- FreeRTOS(实时操作系统)
嵌入式功能设计
硬件架构
1. 智能电能监测模块
核心组件:
- 主控芯片: ESP32-WROOM-32
- 双核32位处理器,主频240MHz
- 集成WiFi和蓝牙
- 丰富的GPIO接口
- 电能计量芯片: ADE7953
- 双通道有功/无功电能测量
- 电压/电流/功率采样
- SPI/I2C通信接口
- 电流传感器: SCT-013-030(0-30A)
- 非侵入式交流电流互感器
- 分体式设计,便于安装
- 电压采样: AC-AC变压器
- 降压采样220V交流电
- 隔离保护电路
电路设计原理:
220V AC Input
│
├──→ [电压互感器] ──→ [整流滤波] ──→ [分压电路] ──→ ESP32 ADC
│
└──→ [电流互感器] ──→ [负载采样] ──→ [放大电路] ──→ ADE7953 ──→ ESP32
│
└──→ 家电负载
2. 环境监测模块
传感器配置:
- DHT22: 温湿度传感器
- BH1750: 光照强度传感器
- MQ-135: 空气质量传感器(可选)
- DS18B20: 温度传感器
3. 智能控制模块
继电器控制电路:
- 光耦隔离设计
- 10A/250VAC继电器
- 过流过压保护
- 手动/自动切换开关
嵌入式软件设计
MicroPython固件架构
# main.py - ESP32主程序入口
import network
import time
from umqtt.simple import MQTTClient
import machine
from machine import Pin, ADC, I2C
import ujson
# ==================== 配置参数 ====================
WIFI_SSID = "YOUR_WIFI_SSID"
WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"
MQTT_BROKER = "192.168.1.100"
MQTT_PORT = 1883
DEVICE_ID = "smart_meter_001"
MQTT_TOPIC_PUB = f"energy/{DEVICE_ID}/data"
MQTT_TOPIC_SUB = f"energy/{DEVICE_ID}/control"
# ==================== 硬件初始化 ====================
class EnergyMonitor:
def __init__(self):
# I2C初始化(用于ADE7953通信)
self.i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
# ADC初始化(电压采样)
self.adc_voltage = ADC(Pin(34))
self.adc_voltage.atten(ADC.ATTN_11DB) # 0-3.3V量程
# 继电器控制引脚
self.relay = Pin(25, Pin.OUT)
self.relay.value(0) # 初始关闭
# 状态LED
self.led = Pin(2, Pin.OUT)
# 校准参数
self.voltage_cal = 234.26 # 电压校准系数
self.current_cal = 30.0 # 电流校准系数
def read_voltage(self):
"""读取电压值"""
adc_value = self.adc_voltage.read()
voltage = (adc_value / 4095.0) * 3.3 * self.voltage_cal
return round(voltage, 2)
def read_current_power(self):
"""从ADE7953读取电流和功率数据"""
try:
# 读取寄存器数据(简化示例)
# 实际需要根据ADE7953数据手册实现完整协议
data = self.i2c.readfrom_mem(0x38, 0x10, 4)
current = int.from_bytes(data[0:2], 'big') / 1000.0
power = int.from_bytes(data[2:4], 'big') / 10.0
return {
'current': round(current, 3),
'power': round(power, 2),
'energy': 0.0 # 累计电量需要积分计算
}
except Exception as e:
print(f"读取数据错误: {e}")
return {'current': 0, 'power': 0, 'energy': 0}
def control_relay(self, state):
"""控制继电器开关"""
self.relay.value(1 if state else 0)
self.led.value(1 if state else 0)
return state
# ==================== WiFi连接 ====================
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('正在连接WiFi...')
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
timeout = 0
while not wlan.isconnected() and timeout < 30:
time.sleep(1)
timeout += 1
print('.', end='')
if wlan.isconnected():
print(f'\nWiFi连接成功!')
print(f'IP地址: {wlan.ifconfig()[0]}')
return True
else:
print('\nWiFi连接失败!')
return False
return True
# ==================== MQTT通信 ====================
class MQTTHandler:
def __init__(self, client_id, broker, port):
self.client = MQTTClient(client_id, broker, port)
self.monitor = None
def connect(self):
try:
self.client.set_callback(self.on_message)
self.client.connect()
self.client.subscribe(MQTT_TOPIC_SUB)
print(f"MQTT连接成功,订阅主题: {MQTT_TOPIC_SUB}")
return True
except Exception as e:
print(f"MQTT连接失败: {e}")
return False
def on_message(self, topic, msg):
"""接收控制指令"""
try:
data = ujson.loads(msg)
command = data.get('command')
if command == 'relay_on':
self.monitor.control_relay(True)
print("继电器已打开")
elif command == 'relay_off':
self.monitor.control_relay(False)
print("继电器已关闭")
elif command == 'get_status':
self.publish_data()
except Exception as e:
print(f"处理消息错误: {e}")
def publish_data(self):
"""发布能耗数据"""
if self.monitor:
voltage = self.monitor.read_voltage()
power_data = self.monitor.read_current_power()
payload = {
'device_id': DEVICE_ID,
'timestamp': time.time(),
'voltage': voltage,
'current': power_data['current'],
'power': power_data['power'],
'energy': power_data['energy'],
'relay_state': self.monitor.relay.value()
}
try:
self.client.publish(MQTT_TOPIC_PUB, ujson.dumps(payload))
print(f"数据已发送: {payload}")
except Exception as e:
print(f"发送数据失败: {e}")
def check_msg(self):
"""检查新消息"""
try:
self.client.check_msg()
except:
pass
# ==================== 主程序 ====================
def main():
print("=" * 50)
print("智能家庭能源监测系统 - 嵌入式节点")
print("=" * 50)
# 连接WiFi
if not connect_wifi():
print("WiFi连接失败,系统重启...")
time.sleep(5)
machine.reset()
# 初始化硬件
monitor = EnergyMonitor()
# 连接MQTT
mqtt_handler = MQTTHandler(DEVICE_ID, MQTT_BROKER, MQTT_PORT)
mqtt_handler.monitor = monitor
if not mqtt_handler.connect():
print("MQTT连接失败,系统重启...")
time.sleep(5)
machine.reset()
# 主循环
last_publish = 0
publish_interval = 5 # 每5秒发送一次数据
print("\n系统运行中...")
while True:
try:
# 检查MQTT消息
mqtt_handler.check_msg()
# 定时发送数据
current_time = time.time()
if current_time - last_publish >= publish_interval:
mqtt_handler.publish_data()
last_publish = current_time
time.sleep(0.1)
except KeyboardInterrupt:
print("\n系统停止")
break
except Exception as e:
print(f"运行错误: {e}")
time.sleep(1)
if __name__ == '__main__':
main()
C++固件实现(Arduino框架)
// energy_monitor.ino - 用于更高性能需求的场景
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <ArduinoJson.h>
// ==================== 配置参数 ====================
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_BROKER = "192.168.1.100";
const int MQTT_PORT = 1883;
const char* DEVICE_ID = "smart_meter_001";
// ==================== 引脚定义 ====================
#define VOLTAGE_PIN 34
#define RELAY_PIN 25
#define LED_PIN 2
#define SDA_PIN 21
#define SCL_PIN 22
// ==================== 全局对象 ====================
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// ==================== 数据结构 ====================
struct EnergyData {
float voltage;
float current;
float power;
float energy;
unsigned long timestamp;
};
EnergyData currentData;
unsigned long lastPublish = 0;
const unsigned long PUBLISH_INTERVAL = 5000; // 5秒
// ==================== WiFi连接 ====================
void connectWiFi() {
Serial.println("正在连接WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(1000);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi连接失败,重启...");
ESP.restart();
}
}
// ==================== MQTT回调 ====================
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("收到消息: ");
Serial.println(topic);
// 解析JSON
StaticJsonDocument<256> doc;
deserializeJson(doc, payload, length);
const char* command = doc["command"];
if (strcmp(command, "relay_on") == 0) {
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, HIGH);
Serial.println("继电器已打开");
} else if (strcmp(command, "relay_off") == 0) {
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
Serial.println("继电器已关闭");
}
}
// ==================== MQTT连接 ====================
void connectMQTT() {
while (!mqttClient.connected()) {
Serial.println("正在连接MQTT...");
if (mqttClient.connect(DEVICE_ID)) {
Serial.println("MQTT连接成功!");
char subTopic[64];
sprintf(subTopic, "energy/%s/control", DEVICE_ID);
mqttClient.subscribe(subTopic);
Serial.printf("订阅主题: %s\n", subTopic);
} else {
Serial.print("连接失败, rc=");
Serial.println(mqttClient.state());
delay(5000);
}
}
}
// ==================== 读取传感器数据 ====================
void readSensors() {
// 读取电压(ADC)
int adcValue = analogRead(VOLTAGE_PIN);
currentData.voltage = (adcValue / 4095.0) * 3.3 * 234.26;
// 读取电流和功率(从ADE7953芯片)
// 这里需要实现I2C通信协议
Wire.beginTransmission(0x38);
Wire.write(0x10);
Wire.endTransmission();
Wire.requestFrom(0x38, 4);
if (Wire.available() >= 4) {
byte data[4];
for (int i = 0; i < 4; i++) {
data[i] = Wire.read();
}
currentData.current = ((data[0] << 8) | data[1]) / 1000.0;
currentData.power = ((data[2] << 8) | data[3]) / 10.0;
}
currentData.timestamp = millis();
}
// ==================== 发布数据 ====================
void publishData() {
StaticJsonDocument<256> doc;
doc["device_id"] = DEVICE_ID;
doc["timestamp"] = currentData.timestamp;
doc["voltage"] = currentData.voltage;
doc["current"] = currentData.current;
doc["power"] = currentData.power;
doc["energy"] = currentData.energy;
doc["relay_state"] = digitalRead(RELAY_PIN);
char buffer[256];
serializeJson(doc, buffer);
char pubTopic[64];
sprintf(pubTopic, "energy/%s/data", DEVICE_ID);
if (mqttClient.publish(pubTopic, buffer)) {
Serial.println("数据发送成功");
} else {
Serial.println("数据发送失败");
}
}
// ==================== 初始化 ====================
void setup() {
Serial.begin(115200);
Serial.println("\n智能家庭能源监测系统");
Serial.println("===========================");
// 初始化引脚
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
// 初始化I2C
Wire.begin(SDA_PIN, SCL_PIN);
// 连接WiFi
connectWiFi();
// 配置MQTT
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
mqttClient.setCallback(mqttCallback);
// 连接MQTT
connectMQTT();
Serial.println("系统初始化完成!");
}
// ==================== 主循环 ====================
void loop() {
// 保持MQTT连接
if (!mqttClient.connected()) {
connectMQTT();
}
mqttClient.loop();
// 读取传感器
readSensors();
// 定时发送数据
unsigned long now = millis();
if (now - lastPublish >= PUBLISH_INTERVAL) {
publishData();
lastPublish = now;
}
delay(100);
}
后端服务设计
Django项目结构
energy_management/
├── config/ # 项目配置
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── apps/
│ ├── users/ # 用户管理
│ │ ├── models.py
│ │ ├── serializers.py
│ │ └── views.py
│ ├── devices/ # 设备管理
│ │ ├── models.py
│ │ ├── mqtt_client.py
│ │ └── views.py
│ ├── energy/ # 能耗数据
│ │ ├── models.py
│ │ ├── analytics.py
│ │ └── views.py
│ └── alerts/ # 告警服务
│ ├── models.py
│ └── tasks.py
├── utils/ # 工具函数
│ ├── mqtt_handler.py
│ └── ml_predictor.py
├── manage.py
└── requirements.txt
核心模型设计
# apps/devices/models.py
from django.db import models
from django.contrib.auth.models import User
class Device(models.Model):
"""设备模型"""
DEVICE_TYPES = [
('meter', '智能电表'),
('plug', '智能插座'),
('sensor', '环境传感器'),
]
device_id = models.CharField(max_length=50, unique=True, verbose_name='设备ID')
name = models.CharField(max_length=100, verbose_name='设备名称')
device_type = models.CharField(max_length=20, choices=DEVICE_TYPES, verbose_name='设备类型')
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='所有者')
location = models.CharField(max_length=100, verbose_name='安装位置')
is_online = models.BooleanField(default=False, verbose_name='在线状态')
is_active = models.BooleanField(default=True, verbose_name='启用状态')
last_seen = models.DateTimeField(null=True, blank=True, verbose_name='最后上线时间')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
verbose_name = '设备'
verbose_name_plural = verbose_name
ordering = ['-created_at']
def __str__(self):
return f"{self.name} ({self.device_id})"
# apps/energy/models.py
from django.db import models
from apps.devices.models import Device
class EnergyData(models.Model):
"""能耗数据模型"""
device = models.ForeignKey(Device, on_delete=models.CASCADE, verbose_name='设备')
timestamp = models.DateTimeField(db_index=True, verbose_name='时间戳')
voltage = models.FloatField(verbose_name='电压(V)')
current = models.FloatField(verbose_name='电流(A)')
power = models.FloatField(verbose_name='功率(W)')
energy = models.FloatField(verbose_name='电量(kWh)')
power_factor = models.FloatField(null=True, blank=True, verbose_name='功率因数')
temperature = models.FloatField(null=True, blank=True, verbose_name='温度(°C)')
humidity = models.FloatField(null=True, blank=True, verbose_name='湿度(%)')
class Meta:
verbose_name = '能耗数据'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['device', '-timestamp']),
]
def __str__(self):
return f"{self.device.name} - {self.timestamp}"
class EnergyStatistics(models.Model):
"""能耗统计模型"""
PERIOD_TYPES = [
('hour', '小时'),
('day', '天'),
('month', '月'),
]
device = models.ForeignKey(Device, on_delete=models.CASCADE, verbose_name='设备')
period_type = models.CharField(max_length=10, choices=PERIOD_TYPES, verbose_name='统计周期')
period_start = models.DateTimeField(verbose_name='开始时间')
total_energy = models.FloatField(verbose_name='总电量(kWh)')
avg_power = models.FloatField(verbose_name='平均功率(W)')
max_power = models.FloatField(verbose_name='最大功率(W)')
cost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='费用(元)')
class Meta:
verbose_name = '能耗统计'
verbose_name_plural = verbose_name
unique_together = ['device', 'period_type', 'period_start']
MQTT数据接收服务
# apps/devices/mqtt_client.py
import paho.mqtt.client as mqtt
import json
import logging
from django.utils import timezone
from apps.devices.models import Device
from apps.energy.models import EnergyData
logger = logging.getLogger(__name__)
class MQTTClient:
"""MQTT客户端,接收嵌入式设备数据"""
def __init__(self, broker_host, broker_port=1883):
self.broker_host = broker_host
self.broker_port = broker_port
self.client = mqtt.Client()
# 设置回调函数
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self.client.on_disconnect = self.on_disconnect
def on_connect(self, client, userdata, flags, rc):
"""连接成功回调"""
if rc == 0:
logger.info("MQTT连接成功")
# 订阅所有设备的数据主题
client.subscribe("energy/+/data")
client.subscribe("energy/+/status")
else:
logger.error(f"MQTT连接失败: {rc}")
def on_message(self, client, userdata, msg):
"""接收消息回调"""
try:
# 解析主题获取设备ID
topic_parts = msg.topic.split('/')
device_id = topic_parts[1]
message_type = topic_parts[2]
# 解析JSON数据
data = json.loads(msg.payload.decode())
if message_type == 'data':
self.handle_energy_data(device_id, data)
elif message_type == 'status':
self.handle_status_update(device_id, data)
except Exception as e:
logger.error(f"处理MQTT消息错误: {e}")
def handle_energy_data(self, device_id, data):
"""处理能耗数据"""
try:
device = Device.objects.get(device_id=device_id)
# 创建能耗数据记录
EnergyData.objects.create(
device=device,
timestamp=timezone.now(),
voltage=data.get('voltage', 0),
current=data.get('current', 0),
power=data.get('power', 0),
energy=data.get('energy', 0),
temperature=data.get('temperature'),
humidity=data.get('humidity')
)
# 更新设备在线状态
device.is_online = True
device.last_seen = timezone.now()
device.save()
logger.info(f"设备 {device_id} 数据已保存")
except Device.DoesNotExist:
logger.warning(f"设备不存在: {device_id}")
except Exception as e:
logger.error(f"保存能耗数据错误: {e}")
def handle_status_update(self, device_id, data):
"""处理设备状态更新"""
try:
device = Device.objects.get(device_id=device_id)
device.is_online = data.get('online', False)
device.last_seen = timezone.now()
device.save()
except Exception as e:
logger.error(f"更新设备状态错误: {e}")
def on_disconnect(self, client, userdata, rc):
"""断开连接回调"""
logger.warning(f"MQTT连接断开: {rc}")
def publish_control_command(self, device_id, command):
"""发布控制指令到设备"""
topic = f"energy/{device_id}/control"
payload = json.dumps(command)
result = self.client.publish(topic, payload, qos=1)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
logger.info(f"控制指令已发送到 {device_id}")
return True
else:
logger.error(f"发送控制指令失败")
return False
def start(self):
"""启动MQTT客户端"""
try:
self.client.connect(self.broker_host, self.broker_port, 60)
self.client.loop_start()
logger.info(f"MQTT客户端已启动: {self.broker_host}:{self.broker_port}")
except Exception as e:
logger.error(f"启动MQTT客户端失败: {e}")
def stop(self):
"""停止MQTT客户端"""
self.client.loop_stop()
self.client.disconnect()
logger.info("MQTT客户端已停止")
# 全局MQTT客户端实例
mqtt_client = MQTTClient(broker_host='localhost', broker_port=1883)
API接口实现
# apps/devices/views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.utils import timezone
from datetime import timedelta
from apps.devices.models import Device
from apps.devices.serializers import DeviceSerializer
from apps.devices.mqtt_client import mqtt_client
class DeviceViewSet(viewsets.ModelViewSet):
"""设备管理API"""
serializer_class = DeviceSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Device.objects.filter(owner=self.request.user)
@action(detail=True, methods=['post'])
def control(self, request, pk=None):
"""设备控制接口"""
device = self.get_object()
action_type = request.data.get('action')
if action_type == 'turn_on':
command = {'command': 'relay_on'}
elif action_type == 'turn_off':
command = {'command': 'relay_off'}
else:
return Response(
{'error': '无效的控制命令'},
status=status.HTTP_400_BAD_REQUEST
)
# 通过MQTT发送控制指令
success = mqtt_client.publish_control_command(device.device_id, command)
if success:
return Response({'message': '控制指令已发送'})
else:
return Response(
{'error': '发送控制指令失败'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@action(detail=True, methods=['get'])
def realtime_data(self, request, pk=None):
"""获取设备实时数据"""
device = self.get_object()
# 获取最新的10条数据
from apps.energy.models import EnergyData
latest_data = EnergyData.objects.filter(device=device).order_by('-timestamp')[:10]
from apps.energy.serializers import EnergyDataSerializer
serializer = EnergyDataSerializer(latest_data, many=True)
return Response(serializer.data)
# apps/energy/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.db.models import Sum, Avg, Max
from datetime import datetime, timedelta
from apps.energy.models import EnergyData, EnergyStatistics
from apps.devices.models import Device
class EnergyAnalyticsView(APIView):
"""能耗分析API"""
permission_classes = [IsAuthenticated]
def get(self, request):
"""获取能耗分析数据"""
device_id = request.query_params.get('device_id')
period = request.query_params.get('period', 'day') # day/week/month
if not device_id:
return Response({'error': '缺少device_id参数'}, status=400)
try:
device = Device.objects.get(device_id=device_id, owner=request.user)
except Device.DoesNotExist:
return Response({'error': '设备不存在'}, status=404)
# 计算时间范围
end_time = timezone.now()
if period == 'day':
start_time = end_time - timedelta(days=1)
elif period == 'week':
start_time = end_time - timedelta(days=7)
elif period == 'month':
start_time = end_time - timedelta(days=30)
else:
start_time = end_time - timedelta(days=1)
# 查询能耗数据
energy_data = EnergyData.objects.filter(
device=device,
timestamp__gte=start_time,
timestamp__lte=end_time
)
# 统计分析
stats = energy_data.aggregate(
total_energy=Sum('energy'),
avg_power=Avg('power'),
max_power=Max('power')
)
# 按小时分组的数据
hourly_data = []
current_time = start_time
while current_time < end_time:
next_hour = current_time + timedelta(hours=1)
hour_data = energy_data.filter(
timestamp__gte=current_time,
timestamp__lt=next_hour
).aggregate(
energy=Sum('energy'),
avg_power=Avg('power')
)
hourly_data.append({
'time': current_time.isoformat(),
'energy': hour_data['energy'] or 0,
'power': hour_data['avg_power'] or 0
})
current_time = next_hour
return Response({
'device': {
'id': device.device_id,
'name': device.name
},
'period': period,
'statistics': stats,
'hourly_data': hourly_data
})
AI预测模型
# utils/ml_predictor.py
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler
import joblib
from datetime import datetime, timedelta
class EnergyPredictor:
"""能耗预测模型"""
def __init__(self):
self.model = RandomForestRegressor(n_estimators=100, random_state=42)
self.scaler = StandardScaler()
self.is_trained = False
def prepare_features(self, data):
"""准备特征工程"""
df = pd.DataFrame(data)
# 时间特征
df['hour'] = df['timestamp'].dt.hour
df['day_of_week'] = df['timestamp'].dt.dayofweek
df['month'] = df['timestamp'].dt.month
df['is_weekend'] = df['day_of_week'].isin([5, 6]).astype(int)
# 峰谷电价时段特征
df['is_peak'] = df['hour'].apply(
lambda x: 1 if 8 <= x < 11 or 18 <= x < 23 else 0
)
df['is_valley'] = df['hour'].apply(
lambda x: 1 if 23 <= x or x < 7 else 0
)
# 温度特征(如果有)
if 'temperature' in df.columns:
df['temp_squared'] = df['temperature'] ** 2
feature_cols = ['hour', 'day_of_week', 'month', 'is_weekend',
'is_peak', 'is_valley', 'temperature']
return df[feature_cols].fillna(0)
def train(self, historical_data):
"""训练模型"""
X = self.prepare_features(historical_data)
y = historical_data['power'].values
# 标准化
X_scaled = self.scaler.fit_transform(X)
# 训练
self.model.fit(X_scaled, y)
self.is_trained = True
print("模型训练完成")
def predict(self, future_timestamps, temperature=None):
"""预测未来能耗"""
if not self.is_trained:
raise ValueError("模型尚未训练")
# 构造预测数据
future_data = pd.DataFrame({
'timestamp': future_timestamps,
'temperature': temperature or 25.0
})
X = self.prepare_features(future_data)
X_scaled = self.scaler.transform(X)
predictions = self.model.predict(X_scaled)
return predictions
def save_model(self, filepath):
"""保存模型"""
joblib.dump({
'model': self.model,
'scaler': self.scaler,
'is_trained': self.is_trained
}, filepath)
def load_model(self, filepath):
"""加载模型"""
data = joblib.load(filepath)
self.model = data['model']
self.scaler = data['scaler']
self.is_trained = data['is_trained']
前端应用设计
Vue 3 主应用结构
frontend/
├── src/
│ ├── views/
│ │ ├── Dashboard.vue # 仪表盘
│ │ ├── DeviceList.vue # 设备列表
│ │ ├── Analytics.vue # 数据分析
│ │ └── Settings.vue # 系统设置
│ ├── components/
│ │ ├── EnergyChart.vue # 能耗图表
│ │ ├── DeviceCard.vue # 设备卡片
│ │ └── RealtimeMonitor.vue # 实时监控
│ ├── api/
│ │ └── index.js # API接口
│ ├── store/
│ │ └── index.js # Vuex状态管理
│ └── App.vue
实时监控组件
<!-- components/RealtimeMonitor.vue -->
<template>
<div class="realtime-monitor">
<el-card class="monitor-card">
<template #header>
<div class="card-header">
<span>实时能耗监控</span>
<el-tag :type="deviceOnline ? 'success' : 'danger'">
{{ deviceOnline ? '在线' : '离线' }}
</el-tag>
</div>
</template>
<el-row :gutter="20">
<el-col :span="6">
<div class="stat-item">
<div class="stat-icon voltage">⚡</div>
<div class="stat-value">{{ voltage }} V</div>
<div class="stat-label">电压</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-item">
<div class="stat-icon current">🔋</div>
<div class="stat-value">{{ current }} A</div>
<div class="stat-label">电流</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-item">
<div class="stat-icon power">💡</div>
<div class="stat-value">{{ power }} W</div>
<div class="stat-label">功率</div>
</div>
</el-col>
<el-col :span="6">
<div class="stat-item">
<div class="stat-icon energy">📊</div>
<div class="stat-value">{{ energy }} kWh</div>
<div class="stat-label">累计电量</div>
</div>
</el-col>
</el-row>
<div class="chart-container">
<div ref="chartRef" style="width: 100%; height: 300px;"></div>
</div>
<div class="control-buttons">
<el-button type="success" @click="turnOn" :disabled="!deviceOnline">
开启设备
</el-button>
<el-button type="danger" @click="turnOff" :disabled="!deviceOnline">
关闭设备
</el-button>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import { ElMessage } from 'element-plus'
import { deviceApi } from '@/api'
const props = defineProps({
deviceId: {
type: String,
required: true
}
})
// 响应式数据
const voltage = ref(0)
const current = ref(0)
const power = ref(0)
const energy = ref(0)
const deviceOnline = ref(false)
// 图表
const chartRef = ref(null)
let chart = null
let ws = null
let chartData = {
time: [],
power: []
}
// 初始化图表
const initChart = () => {
chart = echarts.init(chartRef.value)
const option = {
title: {
text: '功率变化趋势',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: chartData.time
},
yAxis: {
type: 'value',
name: '功率(W)'
},
series: [{
name: '功率',
type: 'line',
smooth: true,
data: chartData.power,
areaStyle: {
color: '#409EFF',
opacity: 0.3
}
}]
}
chart.setOption(option)
}
// WebSocket连接
const connectWebSocket = () => {
ws = new WebSocket(`ws://localhost:8000/ws/device/${props.deviceId}/`)
ws.onopen = () => {
console.log('WebSocket连接成功')
deviceOnline.value = true
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
// 更新实时数据
voltage.value = data.voltage.toFixed(2)
current.value = data.current.toFixed(3)
power.value = data.power.toFixed(2)
energy.value = data.energy.toFixed(3)
// 更新图表
const now = new Date().toLocaleTimeString()
chartData.time.push(now)
chartData.power.push(data.power)
// 保持最近50个数据点
if (chartData.time.length > 50) {
chartData.time.shift()
chartData.power.shift()
}
chart.setOption({
xAxis: {
data: chartData.time
},
series: [{
data: chartData.power
}]
})
}
ws.onerror = (error) => {
console.error('WebSocket错误:', error)
deviceOnline.value = false
}
ws.onclose = () => {
console.log('WebSocket连接关闭')
deviceOnline.value = false
// 5秒后尝试重连
setTimeout(connectWebSocket, 5000)
}
}
// 设备控制
const turnOn = async () => {
try {
await deviceApi.control(props.deviceId, 'turn_on')
ElMessage.success('设备已开启')
} catch (error) {
ElMessage.error('操作失败')
}
}
const turnOff = async () => {
try {
await deviceApi.control(props.deviceId, 'turn_off')
ElMessage.success('设备已关闭')
} catch (error) {
ElMessage.error('操作失败')
}
}
onMounted(() => {
initChart()
connectWebSocket()
})
onUnmounted(() => {
if (ws) {
ws.close()
}
if (chart) {
chart.dispose()
}
})
</script>
<style scoped>
.realtime-monitor {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-item {
text-align: center;
padding: 20px;
border-radius: 8px;
background: #f5f7fa;
}
.stat-icon {
font-size: 32px;
margin-bottom: 10px;
}
.stat-value {
font-size: 24px;
font-weight: bold;
color: #409EFF;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #909399;
}
.chart-container {
margin: 30px 0;
}
.control-buttons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 20px;
}
</style>
部署方案
Docker容器化部署
# docker-compose.yml
version: '3.8'
services:
# PostgreSQL数据库
postgres:
image: postgres:14
environment:
POSTGRES_DB: energy_db
POSTGRES_USER: energy_user
POSTGRES_PASSWORD: secure_password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
# InfluxDB时序数据库
influxdb:
image: influxdb:2.7
environment:
INFLUXDB_DB: energy_metrics
INFLUXDB_ADMIN_USER: admin
INFLUXDB_ADMIN_PASSWORD: admin_password
volumes:
- influxdb_data:/var/lib/influxdb
ports:
- "8086:8086"
# Redis缓存
redis:
image: redis:7
ports:
- "6379:6379"
volumes:
- redis_data:/data
# MQTT Broker
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
- "9001:9001"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
- mosquitto_data:/mosquitto/data
# Django后端
backend:
build: ./backend
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./backend:/app
ports:
- "8000:8000"
depends_on:
- postgres
- redis
- influxdb
environment:
DATABASE_URL: postgres://energy_user:secure_password@postgres:5432/energy_db
REDIS_URL: redis://redis:6379/0
MQTT_BROKER: mosquitto
# Celery Worker
celery:
build: ./backend
command: celery -A config worker -l info
volumes:
- ./backend:/app
depends_on:
- backend
- redis
# Vue前端
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
postgres_data:
influxdb_data:
redis_data:
mosquitto_data:
总结与展望
项目亮点
- 完整的全栈方案: 从嵌入式硬件到云端服务,实现端到端的能源管理
- 实时数据处理: 基于MQTT的低延迟数据传输,WebSocket实时推送
- 智能AI预测: 机器学习算法预测能耗趋势,提供节能建议
- 可扩展架构: 微服务设计,支持水平扩展和功能扩展
- 用户友好界面: 响应式设计,多终端适配,数据可视化
技术优势
- Python生态: 利用Python丰富的库和框架,快速开发
- 物联网集成: MQTT协议保证设备通信的稳定性和效率
- 时序数据库: InfluxDB专门优化时间序列数据存储和查询
- 容器化部署: Docker简化部署流程,提高可移植性
未来规划
- [ ] 接入更多品牌的智能家电设备
- [ ] 开发语音控制功能(集成小爱/天猫精灵)
- [ ] 引入深度学习模型,提升预测精度
- [ ] 实现多用户社区功能,能源使用排行榜
- [ ] 支持光伏发电系统接入,实现能源自给自足
- [ ] 区块链技术应用于家庭间电力交易
1043

被折叠的 条评论
为什么被折叠?



