Python全栈项目--智能家居安全监控系统

项目简介

在物联网时代,智能家居安全监控系统已成为现代家庭不可或缺的一部分。本文将详细介绍如何使用Python构建一个功能完善的全栈智能家居安全监控系统,该系统集成了视频监控、入侵检测、智能报警等多种功能,为家庭安全提供全方位保护。

技术栈选型

后端技术

  • Web框架: Flask/Django - 提供RESTful API接口
  • 数据库: PostgreSQL - 存储用户信息、设备数据、报警记录
  • 缓存: Redis - 缓存实时数据,提高响应速度
  • 异步任务: Celery - 处理视频分析、报警推送等耗时任务
  • 消息队列: RabbitMQ - 任务调度和设备通信

前端技术

  • 框架: Vue.js 3 - 构建响应式用户界面
  • UI组件库: Element Plus - 快速搭建美观界面
  • 实时通信: WebSocket - 实现实时视频流和报警推送
  • 图表可视化: ECharts - 展示安全数据统计

AI与计算机视觉

  • OpenCV: 视频流处理和图像分析
  • TensorFlow/PyTorch: 人脸识别、物体检测
  • YOLO: 实时目标检测算法
  • dlib: 人脸特征提取

硬件接入

  • 树莓派: 边缘计算设备,运行监控程序
  • 摄像头: USB摄像头或IP摄像头
  • 传感器: 门窗传感器、红外传感器、烟雾报警器

系统架构设计

复制代码
┌─────────────────────────────────────────────────────────┐
│                      前端展示层                          │
│   (Vue.js + WebSocket + 视频播放器)                      │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                    API网关层                             │
│        (Nginx + Flask/Django RESTful API)               │
└─────────────────────────────────────────────────────────┘
                          ↓
┌──────────────┬──────────────┬──────────────┬───────────┐
│  用户管理    │  设备管理    │  视频分析    │  报警模块 │
│   服务       │   服务       │   服务       │   服务    │
└──────────────┴──────────────┴──────────────┴───────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              数据存储层                                  │
│   PostgreSQL + Redis + 文件存储(视频/图片)              │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│              硬件设备层                                  │
│   树莓派 + 摄像头 + 各类传感器                           │
└─────────────────────────────────────────────────────────┘

核心功能模块

1. 用户认证与权限管理

复制代码
# 使用JWT实现用户认证
from flask_jwt_extended import JWTManager, create_access_token, jwt_required
from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(255), nullable=False)
    role = db.Column(db.String(20), default='user')  # admin, user, guest
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

@app.route('/api/login', methods=['POST'])
def login():
    data = request.get_json()
    user = User.query.filter_by(username=data['username']).first()
    
    if user and user.check_password(data['password']):
        access_token = create_access_token(identity=user.id)
        return jsonify(access_token=access_token), 200
    
    return jsonify({"msg": "用户名或密码错误"}), 401

2. 实时视频监控

复制代码
import cv2
from flask import Response
import threading

class VideoCamera:
    def __init__(self, camera_id=0):
        self.video = cv2.VideoCapture(camera_id)
        self.lock = threading.Lock()
        
    def __del__(self):
        self.video.release()
    
    def get_frame(self):
        with self.lock:
            success, image = self.video.read()
            if not success:
                return None
            
            # 编码为JPEG格式
            ret, jpeg = cv2.imencode('.jpg', image)
            return jpeg.tobytes()

def gen_frames(camera_id):
    camera = VideoCamera(camera_id)
    while True:
        frame = camera.get_frame()
        if frame:
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/api/video_feed/<int:camera_id>')
@jwt_required()
def video_feed(camera_id):
    return Response(gen_frames(camera_id),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

3. 人脸识别与访客管理

复制代码
import face_recognition
import numpy as np
from datetime import datetime

class FaceRecognitionSystem:
    def __init__(self):
        self.known_faces = []
        self.known_names = []
        self.load_known_faces()
    
    def load_known_faces(self):
        """从数据库加载已知人脸"""
        users = FaceData.query.all()
        for user in users:
            face_encoding = np.frombuffer(user.encoding, dtype=np.float64)
            self.known_faces.append(face_encoding)
            self.known_names.append(user.name)
    
    def recognize_face(self, frame):
        """识别图像中的人脸"""
        # 检测人脸位置
        face_locations = face_recognition.face_locations(frame)
        face_encodings = face_recognition.face_encodings(frame, face_locations)
        
        results = []
        for face_encoding, face_location in zip(face_encodings, face_locations):
            # 与已知人脸比对
            matches = face_recognition.compare_faces(
                self.known_faces, face_encoding, tolerance=0.6
            )
            name = "陌生人"
            
            if True in matches:
                match_index = matches.index(True)
                name = self.known_names[match_index]
            
            results.append({
                'name': name,
                'location': face_location,
                'timestamp': datetime.now()
            })
            
            # 陌生人则触发报警
            if name == "陌生人":
                self.trigger_alert(frame, face_location)
        
        return results
    
    def trigger_alert(self, frame, face_location):
        """触发陌生人警报"""
        top, right, bottom, left = face_location
        face_image = frame[top:bottom, left:right]
        
        # 保存陌生人照片
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f'stranger_{timestamp}.jpg'
        cv2.imwrite(f'static/alerts/{filename}', face_image)
        
        # 创建报警记录
        alert = Alert(
            type='stranger_detected',
            image_path=filename,
            timestamp=datetime.now()
        )
        db.session.add(alert)
        db.session.commit()
        
        # 推送实时通知
        socketio.emit('alert', {
            'type': 'stranger',
            'message': '检测到陌生人',
            'image': filename
        })

4. 智能入侵检测

复制代码
from ultralytics import YOLO
import torch

class IntrusionDetector:
    def __init__(self):
        # 加载YOLO模型
        self.model = YOLO('yolov8n.pt')
        self.alert_classes = ['person']  # 需要报警的类别
        
    def detect_intrusion(self, frame, safe_zone_hours=(22, 6)):
        """检测入侵行为"""
        current_hour = datetime.now().hour
        
        # 在安全时间段内进行检测
        if safe_zone_hours[0] <= current_hour or current_hour < safe_zone_hours[1]:
            results = self.model(frame)
            
            for result in results:
                boxes = result.boxes
                for box in boxes:
                    class_id = int(box.cls[0])
                    class_name = self.model.names[class_id]
                    confidence = float(box.conf[0])
                    
                    if class_name in self.alert_classes and confidence > 0.5:
                        self.create_intrusion_alert(frame, box, class_name, confidence)
                        return True
        
        return False
    
    def create_intrusion_alert(self, frame, box, class_name, confidence):
        """创建入侵警报"""
        x1, y1, x2, y2 = map(int, box.xyxy[0])
        
        # 在图像上标注
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2)
        cv2.putText(frame, f'{class_name}: {confidence:.2f}', 
                    (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 
                    0.5, (0, 0, 255), 2)
        
        # 保存报警图片
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        filename = f'intrusion_{timestamp}.jpg'
        cv2.imwrite(f'static/alerts/{filename}', frame)
        
        # 发送报警
        self.send_alert_notification(filename, class_name, confidence)

5. 多传感器联动

复制代码
import RPi.GPIO as GPIO
from celery import Celery

# 传感器配置
DOOR_SENSOR_PIN = 17
WINDOW_SENSOR_PIN = 18
PIR_SENSOR_PIN = 27
SMOKE_SENSOR_PIN = 22

class SensorManager:
    def __init__(self):
        GPIO.setmode(GPIO.BCM)
        self.setup_sensors()
        
    def setup_sensors(self):
        """初始化传感器GPIO"""
        GPIO.setup(DOOR_SENSOR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(WINDOW_SENSOR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(PIR_SENSOR_PIN, GPIO.IN)
        GPIO.setup(SMOKE_SENSOR_PIN, GPIO.IN)
        
        # 添加事件检测
        GPIO.add_event_detect(DOOR_SENSOR_PIN, GPIO.BOTH, 
                            callback=self.door_callback, bouncetime=300)
        GPIO.add_event_detect(PIR_SENSOR_PIN, GPIO.RISING, 
                            callback=self.motion_callback, bouncetime=2000)
    
    def door_callback(self, channel):
        """门窗传感器回调"""
        state = "打开" if GPIO.input(channel) == GPIO.HIGH else "关闭"
        sensor_type = "门" if channel == DOOR_SENSOR_PIN else "窗"
        
        # 记录事件
        event = SensorEvent(
            sensor_type=sensor_type,
            state=state,
            timestamp=datetime.now()
        )
        db.session.add(event)
        db.session.commit()
        
        # 如果系统处于布防状态,触发报警
        if state == "打开" and self.is_armed():
            self.trigger_sensor_alert(sensor_type, state)
    
    def motion_callback(self, channel):
        """红外传感器回调"""
        if self.is_armed():
            self.trigger_sensor_alert("红外传感器", "检测到移动")
    
    def is_armed(self):
        """检查系统是否处于布防状态"""
        system_status = SystemStatus.query.first()
        return system_status and system_status.armed
    
    def trigger_sensor_alert(self, sensor_type, state):
        """触发传感器报警"""
        alert = Alert(
            type='sensor_alert',
            message=f'{sensor_type}{state}',
            timestamp=datetime.now()
        )
        db.session.add(alert)
        db.session.commit()
        
        # WebSocket推送
        socketio.emit('sensor_alert', {
            'sensor': sensor_type,
            'state': state,
            'time': datetime.now().isoformat()
        })

6. 报警通知系统

复制代码
from twilio.rest import Client
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

class AlertNotificationSystem:
    def __init__(self):
        self.twilio_client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
        self.smtp_config = {
            'server': 'smtp.gmail.com',
            'port': 587,
            'username': EMAIL_USERNAME,
            'password': EMAIL_PASSWORD
        }
    
    def send_sms_alert(self, phone_number, message):
        """发送短信报警"""
        try:
            message = self.twilio_client.messages.create(
                body=message,
                from_=TWILIO_PHONE_NUMBER,
                to=phone_number
            )
            return True
        except Exception as e:
            print(f"短信发送失败: {e}")
            return False
    
    def send_email_alert(self, to_email, subject, body, image_path=None):
        """发送邮件报警"""
        msg = MIMEMultipart()
        msg['From'] = self.smtp_config['username']
        msg['To'] = to_email
        msg['Subject'] = subject
        
        # 添加文字内容
        msg.attach(MIMEText(body, 'html'))
        
        # 添加报警图片
        if image_path:
            with open(image_path, 'rb') as f:
                img = MIMEImage(f.read())
                img.add_header('Content-Disposition', 'attachment', 
                             filename=os.path.basename(image_path))
                msg.attach(img)
        
        try:
            server = smtplib.SMTP(self.smtp_config['server'], 
                                self.smtp_config['port'])
            server.starttls()
            server.login(self.smtp_config['username'], 
                        self.smtp_config['password'])
            server.send_message(msg)
            server.quit()
            return True
        except Exception as e:
            print(f"邮件发送失败: {e}")
            return False
    
    def send_push_notification(self, user_id, title, message, data=None):
        """发送APP推送通知"""
        # 使用Firebase Cloud Messaging或其他推送服务
        notification_data = {
            'title': title,
            'body': message,
            'data': data or {}
        }
        
        # 通过WebSocket实时推送
        socketio.emit('push_notification', notification_data, 
                     room=f'user_{user_id}')

7. 数据分析与可视化

复制代码
from datetime import datetime, timedelta
from sqlalchemy import func

class SecurityAnalytics:
    @staticmethod
    def get_alert_statistics(days=30):
        """获取报警统计数据"""
        start_date = datetime.now() - timedelta(days=days)
        
        # 按类型统计报警数量
        alert_counts = db.session.query(
            Alert.type, func.count(Alert.id)
        ).filter(
            Alert.timestamp >= start_date
        ).group_by(Alert.type).all()
        
        # 按日期统计
        daily_alerts = db.session.query(
            func.date(Alert.timestamp), func.count(Alert.id)
        ).filter(
            Alert.timestamp >= start_date
        ).group_by(func.date(Alert.timestamp)).all()
        
        # 按小时统计(找出高发时段)
        hourly_distribution = db.session.query(
            func.extract('hour', Alert.timestamp), 
            func.count(Alert.id)
        ).filter(
            Alert.timestamp >= start_date
        ).group_by(func.extract('hour', Alert.timestamp)).all()
        
        return {
            'by_type': dict(alert_counts),
            'by_day': dict(daily_alerts),
            'by_hour': dict(hourly_distribution)
        }
    
    @staticmethod
    def get_visitor_statistics(days=30):
        """获取访客统计"""
        start_date = datetime.now() - timedelta(days=days)
        
        visitors = db.session.query(
            FaceLog.name, func.count(FaceLog.id)
        ).filter(
            FaceLog.timestamp >= start_date
        ).group_by(FaceLog.name).all()
        
        return dict(visitors)

@app.route('/api/analytics/dashboard')
@jwt_required()
def get_dashboard_data():
    """获取仪表盘数据"""
    analytics = SecurityAnalytics()
    
    return jsonify({
        'alerts': analytics.get_alert_statistics(30),
        'visitors': analytics.get_visitor_statistics(30),
        'system_status': {
            'cameras_online': Camera.query.filter_by(status='online').count(),
            'sensors_active': Sensor.query.filter_by(active=True).count(),
            'total_alerts_today': Alert.query.filter(
                func.date(Alert.timestamp) == datetime.now().date()
            ).count()
        }
    })

前端实现

Vue.js 主界面

复制代码
// Dashboard.vue
<template>
  <div class="dashboard">
    <el-row :gutter="20">
      <!-- 系统状态卡片 -->
      <el-col :span="6" v-for="stat in systemStats" :key="stat.title">
        <el-card class="stat-card">
          <div class="stat-content">
            <i :class="stat.icon"></i>
            <div class="stat-info">
              <h3>{{ stat.value }}</h3>
              <p>{{ stat.title }}</p>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <!-- 实时视频监控 -->
    <el-row :gutter="20" class="video-section">
      <el-col :span="12" v-for="camera in cameras" :key="camera.id">
        <el-card>
          <template #header>
            <div class="card-header">
              <span>{{ camera.name }}</span>
              <el-tag :type="camera.status === 'online' ? 'success' : 'danger'">
                {{ camera.status }}
              </el-tag>
            </div>
          </template>
          <img :src="`/api/video_feed/${camera.id}`" class="video-stream">
        </el-card>
      </el-col>
    </el-row>

    <!-- 报警记录 -->
    <el-card class="alerts-section">
      <template #header>
        <span>最近报警</span>
      </template>
      <el-table :data="recentAlerts" style="width: 100%">
        <el-table-column prop="type" label="类型" width="150">
          <template #default="scope">
            <el-tag :type="getAlertTypeColor(scope.row.type)">
              {{ scope.row.type }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="message" label="信息"></el-table-column>
        <el-table-column prop="timestamp" label="时间" width="180"></el-table-column>
        <el-table-column label="操作" width="120">
          <template #default="scope">
            <el-button size="small" @click="viewAlertDetails(scope.row)">
              查看
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 数据统计图表 -->
    <el-row :gutter="20">
      <el-col :span="12">
        <el-card>
          <template #header>报警趋势</template>
          <div ref="alertChart" style="height: 300px"></div>
        </el-card>
      </el-col>
      <el-col :span="12">
        <el-card>
          <template #header>访客统计</template>
          <div ref="visitorChart" style="height: 300px"></div>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { io } from 'socket.io-client'
import * as echarts from 'echarts'
import axios from 'axios'

const systemStats = ref([
  { title: '在线摄像头', value: 0, icon: 'el-icon-video-camera' },
  { title: '活动传感器', value: 0, icon: 'el-icon-monitor' },
  { title: '今日报警', value: 0, icon: 'el-icon-warning' },
  { title: '已识别访客', value: 0, icon: 'el-icon-user' }
])

const cameras = ref([])
const recentAlerts = ref([])
let socket = null

onMounted(async () => {
  // 初始化WebSocket连接
  socket = io('http://localhost:5000', {
    auth: {
      token: localStorage.getItem('token')
    }
  })

  // 监听实时报警
  socket.on('alert', (data) => {
    recentAlerts.value.unshift(data)
    ElNotification({
      title: '安全警报',
      message: data.message,
      type: 'warning',
      duration: 0
    })
  })

  // 加载数据
  await loadDashboardData()
  initCharts()
})

const loadDashboardData = async () => {
  try {
    const response = await axios.get('/api/analytics/dashboard')
    const data = response.data
    
    systemStats.value[0].value = data.system_status.cameras_online
    systemStats.value[1].value = data.system_status.sensors_active
    systemStats.value[2].value = data.system_status.total_alerts_today
    
    // 加载摄像头列表和报警记录
    const camerasResp = await axios.get('/api/cameras')
    cameras.value = camerasResp.data
    
    const alertsResp = await axios.get('/api/alerts/recent')
    recentAlerts.value = alertsResp.data
  } catch (error) {
    console.error('加载数据失败:', error)
  }
}

const initCharts = () => {
  // 初始化报警趋势图
  const alertChart = echarts.init(this.$refs.alertChart)
  alertChart.setOption({
    xAxis: { type: 'category', data: [] },
    yAxis: { type: 'value' },
    series: [{ type: 'line', data: [] }]
  })
  
  // 初始化访客统计图
  const visitorChart = echarts.init(this.$refs.visitorChart)
  visitorChart.setOption({
    series: [{
      type: 'pie',
      data: []
    }]
  })
}

onUnmounted(() => {
  if (socket) {
    socket.disconnect()
  }
})
</script>

部署方案

Docker容器化部署

复制代码
# Dockerfile
FROM python:3.11-slim

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    libgl1-mesa-glx \
    libglib2.0-0 \
    libsm6 \
    libxext6 \
    libxrender-dev \
    libgomp1 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# 安装Python依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 5000

# 启动应用
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "--worker-class", "eventlet", "app:app"]

# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/smart_home
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    volumes:
      - ./static:/app/static

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: smart_home
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  celery:
    build: .
    command: celery -A celery_app worker --loglevel=info
    depends_on:
      - redis
      - db

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./static:/usr/share/nginx/html/static
    depends_on:
      - web

volumes:
  postgres_data:

系统优化与最佳实践

1. 性能优化

  • 使用Redis缓存频繁访问的数据
  • 视频流使用H.264编码压缩
  • 异步处理AI分析任务,避免阻塞主线程
  • 数据库连接池优化

2. 安全加固

  • 使用HTTPS加密传输
  • JWT Token定期刷新机制
  • API接口限流防止滥用
  • 视频流访问权限验证
  • 敏感数据加密存储

3. 可扩展性

  • 微服务架构设计,各模块独立部署
  • 支持多摄像头横向扩展
  • 插件化传感器接入
  • 消息队列解耦系统组件

总结与展望

本项目实现了一个功能完整的Python全栈智能家居安全监控系统,涵盖了从硬件接入、后端服务、前端展示到智能分析的完整技术栈。系统具有以下特点:

核心优势:

  • 全栈Python开发,技术栈统一
  • AI驱动的智能分析能力
  • 实时响应和多渠道报警
  • 模块化设计,易于扩展
  • 完整的数据分析和可视化

未来改进方向:

  1. 集成更多AI模型,如行为分析、异常检测
  2. 支持边缘计算,降低云端压力
  3. 增加语音控制功能
  4. 开发移动端APP
  5. 接入更多智能家居设备
  6. 添加AI训练数据自动标注功能

通过本项目的学习和实践,你将掌握Python全栈开发、计算机视觉、物联网设备接入、实时通信等多项关键技术,为开发更复杂的智能系统打下坚实基础。

项目代码:

下载链接

相关推荐
YJlio13 小时前
1.7 通过 Sysinternals Live 在线运行工具:不下载也能用的“云端工具箱”
c语言·网络·python·数码相机·ios·django·iphone
l1t14 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
山塘小鱼儿15 小时前
本地Ollama+Agent+LangGraph+LangSmith运行
python·langchain·ollama·langgraph·langsimth
码说AI15 小时前
python快速绘制走势图对比曲线
开发语言·python
wait_luky15 小时前
python作业3
开发语言·python
Python大数据分析@17 小时前
tkinter可以做出多复杂的界面?
python·microsoft
大黄说说17 小时前
新手选语言不再纠结:Java、Python、Go、JavaScript 四大热门语言全景对比与学习路线建议
java·python·golang
小小张说故事17 小时前
SQLAlchemy 技术入门指南
后端·python
我是章汕呐18 小时前
拆解Libvio.link爬虫:从动态页面到反爬对抗的实战解析
爬虫·python