基于Flask+Vue.js的智能社区垃圾分类管理系统 - 三创赛参赛项目全栈开发指南

项目概述与创新点

项目背景

随着我国垃圾分类政策的全面推进,社区垃圾分类管理面临诸多挑战:居民分类知识不足、投放监督难、管理效率低下等。本项目旨在打造一个智能化、可视化、互动性强的社区垃圾分类管理系统,结合AI图像识别技术,提升垃圾分类的准确性和管理效率。

创新亮点

  1. AI智能识别:集成深度学习模型,实现垃圾图片自动分类

  2. 积分激励机制:建立居民垃圾分类积分体系,提高参与度

  3. 可视化数据大屏:实时展示社区垃圾分类数据与趋势

  4. 移动端便捷管理:支持小程序快速查询与上报

技术栈设计

后端技术栈

  • Web框架:Flask + Flask-RESTful

  • 数据库:PostgreSQL + Redis

  • AI模型:PyTorch + ResNet预训练模型

  • 任务队列:Celery + Redis

  • API文档:Swagger/OpenAPI

前端技术栈

  • 主框架:Vue.js 3 + Vue Router + Pinia

  • UI组件库:Element Plus

  • 可视化:ECharts

  • 移动端:UniApp(兼容小程序)

部署与运维

  • 容器化:Docker + Docker Compose

  • CI/CD:GitHub Actions

  • 监控:Prometheus + Grafana

项目架构设计

text

复制代码
garbage-classification-system/
├── backend/                 # Flask后端
│   ├── app/
│   │   ├── api/            # API蓝图
│   │   ├── models/         # 数据模型
│   │   ├── services/       # 业务逻辑
│   │   ├── utils/          # 工具函数
│   │   └── ai_model/       # AI模型相关
│   ├── migrations/         # 数据库迁移
│   ├── tests/              # 单元测试
│   └── requirements.txt
├── frontend/               # Vue前端
│   ├── src/
│   │   ├── views/         # 页面组件
│   │   ├── components/    # 通用组件
│   │   ├── store/         # 状态管理
│   │   ├── router/        # 路由配置
│   │   └── api/           # API调用
│   └── package.json
├── mobile/                 # 小程序端
├── docker-compose.yml
├── README.md
└── .gitignore

核心功能模块实现

1. 后端核心代码实现

app/init.py - Flask应用工厂

python

复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import JWTManager
from config import Config

db = SQLAlchemy()
jwt = JWTManager()

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(config_class)
    
    # 初始化扩展
    CORS(app)
    db.init_app(app)
    jwt.init_app(app)
    
    # 注册蓝图
    from app.api import bp as api_bp
    app.register_blueprint(api_bp, url_prefix='/api/v1')
    
    # 创建数据库表
    with app.app_context():
        db.create_all()
    
    return app

app/models/user.py - 用户模型

python

复制代码
from app import db
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model):
    __tablename__ = 'users'
    
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(120), unique=True, index=True)
    password_hash = db.Column(db.String(128))
    phone = db.Column(db.String(20))
    address = db.Column(db.String(200))
    points = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    
    # 关系
    garbage_records = db.relationship('GarbageRecord', backref='user', lazy='dynamic')
    
    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)
    
    def to_dict(self):
        return {
            'id': self.id,
            'username': self.username,
            'email': self.email,
            'points': self.points,
            'created_at': self.created_at.isoformat()
        }

app/services/ai_classifier.py - AI垃圾分类器

python

复制代码
import torch
import torch.nn as nn
from PIL import Image
import torchvision.transforms as transforms
from torchvision.models import resnet50

class GarbageClassifier:
    def __init__(self, model_path='models/garbage_resnet50.pth'):
        self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        self.model = self._load_model(model_path)
        self.classes = ['可回收物', '有害垃圾', '厨余垃圾', '其他垃圾']
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ])
    
    def _load_model(self, model_path):
        """加载预训练模型"""
        model = resnet50(pretrained=False)
        num_ftrs = model.fc.in_features
        model.fc = nn.Linear(num_ftrs, 4)  # 4分类
        
        if torch.cuda.is_available():
            model.load_state_dict(torch.load(model_path))
        else:
            model.load_state_dict(torch.load(model_path, map_location='cpu'))
        
        model.eval()
        return model.to(self.device)
    
    def predict(self, image_path):
        """预测图片中的垃圾类型"""
        image = Image.open(image_path).convert('RGB')
        image_tensor = self.transform(image).unsqueeze(0).to(self.device)
        
        with torch.no_grad():
            outputs = self.model(image_tensor)
            probabilities = torch.nn.functional.softmax(outputs, dim=1)
            _, predicted = torch.max(outputs, 1)
            
        return {
            'category': self.classes[predicted.item()],
            'confidence': probabilities[0][predicted.item()].item(),
            'probabilities': {
                cls: prob.item() for cls, prob in zip(self.classes, probabilities[0])
            }
        }

2. 前端核心组件实现

src/views/Dashboard.vue - 数据仪表盘

vue

复制代码
<template>
  <div class="dashboard-container">
    <!-- 顶部统计卡片 -->
    <el-row :gutter="20">
      <el-col :xs="12" :sm="6" v-for="item in statsData" :key="item.title">
        <stat-card :title="item.title" :value="item.value" :icon="item.icon" 
                   :color="item.color" />
      </el-col>
    </el-row>
    
    <!-- 图表区域 -->
    <el-row :gutter="20" class="chart-row">
      <el-col :xs="24" :sm="12">
        <div class="chart-card">
          <h3>垃圾分类统计</h3>
          <div ref="pieChart" style="height: 300px;"></div>
        </div>
      </el-col>
      <el-col :xs="24" :sm="12">
        <div class="chart-card">
          <h3>每日投放趋势</h3>
          <div ref="lineChart" style="height: 300px;"></div>
        </div>
      </el-col>
    </el-row>
    
    <!-- 最近记录 -->
    <recent-records :records="recentRecords" />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
import StatCard from '@/components/StatCard.vue'
import RecentRecords from '@/components/RecentRecords.vue'
import { getDashboardData } from '@/api/dashboard'

const statsData = ref([
  { title: '今日投放', value: 0, icon: 'trash', color: '#409EFF' },
  { title: '正确率', value: '0%', icon: 'check', color: '#67C23A' },
  { title: '活跃用户', value: 0, icon: 'user', color: '#E6A23C' },
  { title: '累计积分', value: 0, icon: 'coin', color: '#F56C6C' }
])

const recentRecords = ref([])
const pieChart = ref(null)
const lineChart = ref(null)

onMounted(async () => {
  await loadDashboardData()
  initCharts()
})

const loadDashboardData = async () => {
  try {
    const data = await getDashboardData()
    statsData.value[0].value = data.today_count
    statsData.value[1].value = `${data.accuracy_rate}%`
    statsData.value[2].value = data.active_users
    statsData.value[3].value = data.total_points
    recentRecords.value = data.recent_records
  } catch (error) {
    console.error('加载仪表盘数据失败:', error)
  }
}

const initCharts = () => {
  // 初始化饼图
  const pieInstance = echarts.init(pieChart.value)
  pieInstance.setOption({
    tooltip: { trigger: 'item' },
    legend: { top: 'bottom' },
    series: [{
      name: '垃圾分类',
      type: 'pie',
      radius: ['40%', '70%'],
      data: [
        { value: 335, name: '可回收物' },
        { value: 310, name: '有害垃圾' },
        { value: 234, name: '厨余垃圾' },
        { value: 135, name: '其他垃圾' }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }]
  })
  
  // 初始化折线图
  const lineInstance = echarts.init(lineChart.value)
  lineInstance.setOption({
    tooltip: { trigger: 'axis' },
    xAxis: {
      type: 'category',
      data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
    },
    yAxis: { type: 'value' },
    series: [{
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'line',
      smooth: true,
      areaStyle: {}
    }]
  })
}
</script>

3. AI模型训练代码

train_model.py - 模型训练脚本

python

复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.utils.data import DataLoader
import argparse
from tqdm import tqdm

def train_model(model, dataloaders, criterion, optimizer, num_epochs=25):
    """训练模型"""
    best_acc = 0.0
    
    for epoch in range(num_epochs):
        print(f'Epoch {epoch}/{num_epochs-1}')
        print('-' * 10)
        
        # 每个epoch都有训练和验证阶段
        for phase in ['train', 'val']:
            if phase == 'train':
                model.train()
            else:
                model.eval()
            
            running_loss = 0.0
            running_corrects = 0
            
            # 迭代数据
            for inputs, labels in tqdm(dataloaders[phase]):
                inputs = inputs.to(device)
                labels = labels.to(device)
                
                # 梯度清零
                optimizer.zero_grad()
                
                # 前向传播
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)
                    
                    # 反向传播+优化仅在训练阶段
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()
                
                # 统计
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)
            
            epoch_loss = running_loss / len(dataloaders[phase].dataset)
            epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
            
            print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')
            
            # 深度复制模型
            if phase == 'val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                torch.save(model.state_dict(), 'best_model.pth')
    
    print(f'Best val Acc: {best_acc:4f}')
    return model

if __name__ == '__main__':
    # 数据预处理
    data_transforms = {
        'train': transforms.Compose([
            transforms.RandomResizedCrop(224),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
        'val': transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
        ]),
    }
    
    # 加载数据集
    image_datasets = {
        'train': datasets.ImageFolder('data/train', data_transforms['train']),
        'val': datasets.ImageFolder('data/val', data_transforms['val'])
    }
    
    dataloaders = {
        x: DataLoader(image_datasets[x], batch_size=32, shuffle=True, num_workers=4)
        for x in ['train', 'val']
    }
    
    # 初始化模型
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    model = models.resnet50(pretrained=True)
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(num_ftrs, 4)  # 4分类
    model = model.to(device)
    
    # 定义损失函数和优化器
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    
    # 训练模型
    model = train_model(model, dataloaders, criterion, optimizer, num_epochs=25)

部署与运行指南

Docker部署配置

docker-compose.yml

yaml

复制代码
version: '3.8'

services:
  postgres:
    image: postgres:13
    environment:
      POSTGRES_DB: garbage_db
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secure_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

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

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    environment:
      DATABASE_URL: postgresql://admin:secure_password@postgres:5432/garbage_db
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - postgres
      - redis
    volumes:
      - ./backend:/app
      - model_volume:/app/models

  frontend:
    build: ./frontend
    ports:
      - "8080:80"
    depends_on:
      - backend

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - backend
      - frontend

volumes:
  postgres_data:
  model_volume:

项目运行步骤

bash

复制代码
# 1. 克隆项目
git clone https://github.com/yourusername/garbage-classification-system.git
cd garbage-classification-system

# 2. 安装依赖
cd backend && pip install -r requirements.txt
cd ../frontend && npm install

# 3. 配置环境变量
cp backend/.env.example backend/.env
# 编辑.env文件设置数据库等信息

# 4. 初始化数据库
flask db init
flask db migrate
flask db upgrade

# 5. 训练AI模型(可选)
python train_model.py

# 6. 启动开发服务器
# 后端
cd backend && flask run --port=5000
# 前端
cd frontend && npm run serve

# 7. 或使用Docker部署
docker-compose up -d

项目亮点与参赛建议

技术亮点

  1. 全栈技术整合:前后端分离,微服务架构

  2. AI技术落地:深度学习模型实际应用

  3. 数据可视化:丰富的数据展示与分析

  4. 响应式设计:多端适配,用户体验优秀

商业价值

  1. 政策契合:符合国家垃圾分类政策导向

  2. 社会效益:提升社区管理效率,环保教育

  3. 商业模式:积分商城、广告投放、数据服务

参赛建议

  1. 突出创新点:重点展示AI识别准确率和用户体验

  2. 准备演示数据:使用真实社区数据进行演示

  3. 商业模式清晰:设计可行的盈利模式

  4. 团队分工明确:展示团队的技术与商业能力

后续优化方向

  1. 技术优化

    • 使用YOLOv8实现实时目标检测

    • 集成GPT-4进行垃圾回收建议

    • 添加区块链积分系统

  2. 功能扩展

    • 垃圾回收预约功能

    • 环保知识问答社区

    • AR垃圾分类指导

  3. 部署优化

    • Kubernetes集群部署

    • 边缘计算优化

    • CDN加速静态资源

总结

本项目为三创赛提供了一个完整、可实施、创新性强的Python全栈项目方案。通过结合AI技术、可视化展示和社区管理,既解决了实际问题,又展示了团队的技术实力。建议参赛团队根据自身情况调整功能,重点突出项目的创新性和实用性。

相关推荐
踩着两条虫1 小时前
AI驱动的Vue3应用开发平台深入探究(十):物料系统之内置组件库
android·前端·vue.js·人工智能·低代码·系统架构·rxjava
慧一居士2 小时前
nuxt3 项目和nuxt4 项目区别和对比
前端·vue.js
irizhao2 小时前
《高质量数据集 质量评测规范》(TC609-5-2025-04)
人工智能·分类·数据挖掘
神舟之光4 小时前
jwt权限控制简单总结(乡村意见簿-vue-express-mongdb)
前端·vue.js·express
独隅5 小时前
PyTorch 模型性能优化:图像分类与 NLP 模型实战指南
pytorch·性能优化·分类
流光墨佰5 小时前
我做了一个专为油猴打造的轻量级 Vue 组件注入库
vue.js
踩着两条虫10 小时前
AI驱动的Vue3应用开发平台 深入探究(十三):物料系统之区块与页面模板
前端·vue.js·人工智能·架构·系统架构
luanma15098011 小时前
Laravel 8.X重磅特性全解析
前端·javascript·vue.js·php·lua
向哆哆12 小时前
人脸眼部特征检测数据集(千张图片已划分、已标注)适用于YOLO系列深度学习分类检测任务
深度学习·yolo·分类