Python全栈--智能家庭能源管理系统

项目概述

智能家庭能源管理系统(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:

总结与展望

项目亮点

  1. 完整的全栈方案: 从嵌入式硬件到云端服务,实现端到端的能源管理
  2. 实时数据处理: 基于MQTT的低延迟数据传输,WebSocket实时推送
  3. 智能AI预测: 机器学习算法预测能耗趋势,提供节能建议
  4. 可扩展架构: 微服务设计,支持水平扩展和功能扩展
  5. 用户友好界面: 响应式设计,多终端适配,数据可视化

技术优势

  • Python生态: 利用Python丰富的库和框架,快速开发
  • 物联网集成: MQTT协议保证设备通信的稳定性和效率
  • 时序数据库: InfluxDB专门优化时间序列数据存储和查询
  • 容器化部署: Docker简化部署流程,提高可移植性

未来规划

  • [ ] 接入更多品牌的智能家电设备
  • [ ] 开发语音控制功能(集成小爱/天猫精灵)
  • [ ] 引入深度学习模型,提升预测精度
  • [ ] 实现多用户社区功能,能源使用排行榜
  • [ ] 支持光伏发电系统接入,实现能源自给自足
  • [ ] 区块链技术应用于家庭间电力交易

参考资料

项目代码

下载链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值