AI垃圾分类助手

举报
DS小龙哥 发表于 2025/12/25 11:54:37 2025/12/25
【摘要】 项目开发背景随着全球城市化进程的加速和人口的增长,垃圾产量持续上升,给环境带来了巨大压力。传统的垃圾分类方法主要依赖人工操作,效率低下且容易出错,导致大量可回收资源被浪费,有害物质未能妥善处理,加剧了环境污染和资源短缺问题。因此,开发一种高效、自动化的垃圾分类系统成为迫切需求,以提升垃圾处理效率,促进可持续发展。人工智能技术的快速发展为垃圾分类提供了新的解决方案。通过计算机视觉和深度学习模...

项目开发背景

随着全球城市化进程的加速和人口的增长,垃圾产量持续上升,给环境带来了巨大压力。传统的垃圾分类方法主要依赖人工操作,效率低下且容易出错,导致大量可回收资源被浪费,有害物质未能妥善处理,加剧了环境污染和资源短缺问题。因此,开发一种高效、自动化的垃圾分类系统成为迫切需求,以提升垃圾处理效率,促进可持续发展。

人工智能技术的快速发展为垃圾分类提供了新的解决方案。通过计算机视觉和深度学习模型,系统能够自动识别垃圾类型,从而实现精准分类。然而,现有的AI应用多依赖于高性能计算设备,成本高昂且难以在资源受限的嵌入式环境中部署。本项目旨在探索轻量级AI模型在嵌入式端的应用,以实现低成本、高效率的实时垃圾分类,推动智能环保技术的普及。

本项目基于嵌入式系统设计,集成了图像采集、AI识别和执行控制功能,能够通过摄像头实时捕捉垃圾图像,利用轻量级模型进行分析,并驱动机械臂完成分类操作。这种一体化的设计不仅减少了人力依赖,还提高了分类准确性和响应速度。此外,系统支持模型再训练功能,可灵活适应不同地区的垃圾分类标准,便于未来扩展和优化。

在硬件选择上,项目采用Raspberry Pi Pico作为主控芯片,其支持MicroPython编程,适合轻量级AI模型的部署;OV7670摄像头模块负责图像采集,SG90舵机构建机械臂执行分类动作,1.44寸TFT LCD屏幕用于实时显示结果。这些组件的组合确保了系统的可靠性、低功耗和易用性,为智能垃圾分类的推广提供了实用基础。

设计实现的功能

(1) 通过OV7670摄像头模块采集垃圾图像。
(2) 在Raspberry Pi Pico上运行轻量级AI模型,识别垃圾类型(如可回收、厨余、有害)。
(3) 通过1.44寸TFT LCD屏幕显示识别结果,并控制SG90舵机驱动的机械臂将垃圾推入对应垃圾桶。
(4) 支持模型再训练功能,以增加新的垃圾种类。

项目硬件模块组成

(1)主控芯片:Raspberry Pi Pico
(2)视觉模块:OV7670摄像头模块
(3)执行模块:SG90舵机(多个)
(4)显示模块:1.44寸TFT LCD屏幕

设计意义

该设计通过自动化垃圾分类过程,显著提升了垃圾处理的效率和准确性,有助于减少人为错误分类造成的环境污染,促进资源回收和可持续发展,在实际应用中能够为社区或家庭提供便捷的垃圾分类解决方案。

系统采用轻量级AI模型在嵌入式设备上运行,体现了边缘计算的优势,降低了对外部网络的依赖,提高了实时处理能力,并为低成本智能设备的开发提供了可行范例,推动了人工智能技术在日常生活场景中的普及和应用。

基于开源硬件和模块化组件,该设计具备良好的教育价值,可作为学习平台帮助用户深入理解人工智能、计算机视觉和机器人控制等关键技术,培养实践能力和创新思维。

支持模型再训练的功能增强了系统的适应性和灵活性,使其能够随着垃圾种类变化或新标准出台而更新,确保长期实用价值,并为未来扩展更多功能奠定了基础。

设计思路

该系统设计以Raspberry Pi Pico作为核心主控,结合OV7670摄像头采集垃圾图像,在嵌入式端部署轻量级AI模型进行实时分类识别。通过1.44寸TFT LCD屏幕显示识别结果,并驱动多个SG90舵机构建的机械臂执行垃圾分拣动作,将垃圾推入对应类别的垃圾桶。整体流程注重实际硬件限制,确保高效运行和可扩展性。

图像采集部分使用OV7670摄像头模块,通过MicroPython编程控制图像捕捉,将采集到的垃圾图像数据进行预处理,如调整分辨率和格式转换,以适配后续AI模型输入要求。这一步骤需优化图像质量,减少环境光干扰,确保识别准确性。

AI模型部署在Raspberry Pi Pico上,采用轻量级神经网络架构,例如基于TensorFlow Lite Micro的预训练模型,实现垃圾类型分类,包括可回收、厨余和有害等类别。模型推理过程利用Pico的有限计算资源,通过量化或剪枝技术降低复杂度,保证实时响应。识别结果以概率形式输出,用于后续控制决策。

结果显示和控制环节通过1.44寸TFT LCD屏幕实时展示垃圾分类结果和系统状态,同时主控芯片根据识别类型生成PWM信号,驱动SG90舵机机械臂运动。机械臂设计需考虑多自由度动作,将垃圾精准推入指定垃圾桶,执行模块的协调通过MicroPython脚本实现,确保稳定性和重复精度。

模型再训练功能支持系统扩展,允许用户通过外部接口(如USB连接)上传新数据集,在PC端进行模型重训练后,将更新后的轻量模型部署到Pico中。这一过程依赖于模块化设计,便于添加新垃圾种类,同时保持嵌入式端的低资源消耗,无需改动核心硬件。

框架图

+---------------------------------------------+
|             AI垃圾分类助手系统框架图           |
+---------------------------------------------+

+----------------+     +---------------------+     +-------------------+
|  视觉模块       |     |    主控芯片         |     |    显示模块       |
|  OV7670摄像头   |---->|  Raspberry Pi Pico  |---->|  1.44TFT LCD   |
|  (图像采集)     |     |  (运行轻量级AI模型) |     |  (显示识别结果)   |
+----------------+     +---------------------+     +-------------------+
                                |
                                | (控制信号)
                                v
                        +-------------------+
                        |    执行模块       |
                        |  SG90舵机机械臂   |
                        |  (推入对应垃圾桶)  |
                        +-------------------+

+-------------------------------------------------+
|              模型再训练支持                     |
|          (通过USB/串口更新模型,增加垃圾种类)   |
+-------------------------------------------------+

系统总体设计

系统总体设计围绕一个基于嵌入式平台的智能垃圾分类装置展开,该系统通过集成硬件模块和轻量级AI模型实现垃圾的自动识别与分类处理。系统以Raspberry Pi Pico作为主控芯片,负责协调各模块的运行,并利用MicroPython环境进行程序开发,确保高效处理图像数据和控制执行机构。整体工作流程始于摄像头采集垃圾图像,经AI模型分析后输出分类结果,再通过屏幕显示信息并驱动机械臂完成垃圾投放。

在硬件架构中,视觉模块采用OV7670摄像头,负责实时捕捉垃圾图像,并将数据传输至主控芯片进行处理。执行模块由多个SG90舵机构成的机械臂组成,根据识别结果精确控制舵机运动,将垃圾推入对应的分类垃圾桶。显示模块使用1.44寸TFT LCD屏幕,用于直观展示垃圾类型和系统状态,提供用户反馈。

软件部分的核心是嵌入式端运行的轻量级AI模型,该模型针对垃圾类型进行识别,包括可回收、厨余和有害等类别。模型设计考虑了资源限制,确保在Raspberry Pi Pico上高效执行推理过程,同时支持后续的再训练功能,允许用户通过更新数据集来扩展识别种类,以适应新的垃圾分类需求。

系统操作流程从图像采集开始,摄像头获取垃圾图片后,主控芯片调用AI模型进行实时分析,识别结果立即显示在LCD屏幕上。随后,主控芯片生成控制信号,驱动舵机机械臂移动,将垃圾准确推入指定垃圾桶,完成整个分类任务。整个过程强调低功耗和实时性,确保在嵌入式环境中稳定运行。

此外,系统支持模型再训练机制,用户可以通过连接外部设备上传新数据,重新训练AI模型以增加垃圾种类识别能力。这增强了系统的适应性和可扩展性,使其能够应对垃圾分类标准的变化,而无需更换硬件组件。整体设计注重实用性和模块化,各部件协同工作,实现高效的自动化垃圾分类。

系统功能总结

系统功能 描述
图像采集 通过OV7670摄像头模块实时采集垃圾图像,用于后续分析。
垃圾类型识别 在Raspberry Pi Pico主控芯片上运行轻量级AI模型,自动识别垃圾类型(如可回收、厨余、有害等)。
结果反馈与执行 使用1.44寸TFT LCD屏幕显示识别结果,并通过SG90舵机驱动机械臂将垃圾推入对应垃圾桶,实现自动分拣。
模型再训练 支持模型再训练功能,允许用户增加新的垃圾种类,以提升系统的适应性和扩展性。

设计的各个功能模块描述

图像采集模块使用OV7670摄像头模块连接到Raspberry Pi Pico主控芯片,通过摄像头实时采集垃圾图像,为后续识别处理提供视觉输入数据。

AI识别模块在Raspberry Pi Pico上运行轻量级AI模型,对采集到的图像进行分析和处理,识别垃圾的具体类型,例如可回收、厨余或有害垃圾,并输出分类结果。

显示模块通过1.44寸TFT LCD屏幕实时展示AI识别的垃圾类型和分类信息,方便用户直观查看系统运行状态和结果。

执行模块利用多个SG90舵机构建的机械臂,根据AI识别结果控制舵机动作,精确地将垃圾推入对应的分类垃圾桶中,实现自动化分拣。

模型再训练模块支持用户对现有AI模型进行更新和再训练,通过添加新数据来扩展识别垃圾种类,提高系统的适应性和准确性。

上位机代码设计

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <wiringPi.h>
#include <wiringSerial.h>

// 垃圾分类定义
enum TrashType {
    RECYCLABLE = 0,
    KITCHEN_WASTE = 1,
    HARMFUL = 2,
    OTHER = 3
};

class AITrashClassifier {
private:
    cv::dnn::Net net;
    cv::VideoCapture cap;
    std::vector<std::string> classNames;
    int servoPins[3];
    int serialPort;
    
public:
    AITrashClassifier() {
        // 初始化类别名称
        classNames = {"可回收垃圾", "厨余垃圾", "有害垃圾", "其他垃圾"};
        
        // 舵机控制引脚
        servoPins[RECYCLABLE] = 18;    // GPIO18 - 可回收垃圾桶
        servoPins[KITCHEN_WASTE] = 23; // GPIO23 - 厨余垃圾桶  
        servoPins[HARMFUL] = 24;       // GPIO24 - 有害垃圾桶
        
        initializeCamera();
        initializeModel();
        initializeGPIO();
        initializeSerial();
    }
    
    ~AITrashClassifier() {
        if (cap.isOpened()) {
            cap.release();
        }
        serialClose(serialPort);
    }

private:
    void initializeCamera() {
        cap.open(0); // 打开默认摄像头
        if (!cap.isOpened()) {
            std::cerr << "无法打开摄像头" << std::endl;
            return;
        }
        
        // 设置摄像头参数
        cap.set(cv::CAP_PROP_FRAME_WIDTH, 640);
        cap.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
        cap.set(cv::CAP_PROP_FPS, 30);
        
        std::cout << "摄像头初始化成功" << std::endl;
    }
    
    void initializeModel() {
        // 加载TensorFlow Lite模型或ONNX模型
        try {
            net = cv::dnn::readNetFromONNX("trash_classifier.onnx");
            // 或者使用TensorFlow Lite
            // net = cv::dnn::readNetFromTensorflow("trash_classifier.tflite");
            
            // 设置推理后端(根据硬件选择)
            net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
            net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
            
            std::cout << "AI模型加载成功" << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "模型加载失败: " << e.what() << std::endl;
        }
    }
    
    void initializeGPIO() {
        if (wiringPiSetupGpio() == -1) {
            std::cerr << "GPIO初始化失败" << std::endl;
            return;
        }
        
        // 初始化舵机引脚为PWM输出
        for (int i = 0; i < 3; i++) {
            pinMode(servoPins[i], PWM_OUTPUT);
            pwmWrite(servoPins[i], 0); // 初始位置
        }
        
        std::cout << "GPIO初始化成功" << std::endl;
    }
    
    void initializeSerial() {
        // 初始化串口通信(用于与Pico通信)
        serialPort = serialOpen("/dev/ttyAMA0", 115200);
        if (serialPort < 0) {
            std::cerr << "串口初始化失败" << std::endl;
        } else {
            std::cout << "串口通信初始化成功" << std::endl;
        }
    }
    
    cv::Mat preprocessImage(const cv::Mat& frame) {
        cv::Mat processed;
        // 调整图像尺寸为模型输入尺寸
        cv::resize(frame, processed, cv::Size(224, 224));
        
        // 归一化
        processed.convertTo(processed, CV_32F, 1.0/255.0);
        
        // 转换为模型需要的输入格式
        cv::cvtColor(processed, processed, cv::COLOR_BGR2RGB);
        
        return processed;
    }
    
    TrashType classifyTrash(const cv::Mat& frame) {
        if (net.empty()) {
            std::cerr << "模型未加载" << std::endl;
            return OTHER;
        }
        
        cv::Mat inputBlob = cv::dnn::blobFromImage(frame, 1.0, 
                                                  cv::Size(224, 224),
                                                  cv::Scalar(), true, false);
        
        net.setInput(inputBlob);
        cv::Mat outputs = net.forward();
        
        // 获取预测结果
        cv::Point classIdPoint;
        double confidence;
        cv::minMaxLoc(outputs.reshape(1, 1), nullptr, &confidence, 
                     nullptr, &classIdPoint);
        
        int classId = classIdPoint.x;
        
        std::cout << "检测到: " << classNames[classId] 
                  << " 置信度: " << confidence << std::endl;
        
        return static_cast<TrashType>(classId);
    }
    
    void controlServo(TrashType trashType) {
        int angle = 0;
        switch (trashType) {
            case RECYCLABLE:
                angle = 45;   // 可回收垃圾桶角度
                break;
            case KITCHEN_WASTE:
                angle = 90;   // 厨余垃圾桶角度
                break;
            case HARMFUL:
                angle = 135;  // 有害垃圾桶角度
                break;
            default:
                angle = 0;    // 其他垃圾桶角度
                break;
        }
        
        // 控制对应舵机
        int pwmValue = angle * 1000 / 180 + 500; // 转换为PWM值
        pwmWrite(servoPins[trashType], pwmValue);
        
        std::cout << "控制舵机 " << trashType << " 到角度: " << angle << std::endl;
        
        // 发送指令到Pico
        std::string command = "SERVO:" + std::to_string(trashType) + ":" + 
                             std::to_string(angle) + "\n";
        serialPuts(serialPort, command.c_str());
        
        // 等待动作完成
        std::this_thread::sleep_for(std::chrono::seconds(2));
        
        // 复位舵机
        pwmWrite(servoPins[trashType], 0);
    }
    
    void displayResult(cv::Mat& frame, TrashType trashType, double confidence) {
        std::string label = classNames[trashType] + ": " + 
                           std::to_string(confidence).substr(0, 4);
        
        // 在图像上显示结果
        cv::putText(frame, label, cv::Point(10, 30),
                   cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 255, 0), 2);
        
        // 显示图像
        cv::imshow("AI垃圾分类助手", frame);
    }

public:
    void run() {
        std::cout << "启动AI垃圾分类助手..." << std::endl;
        
        cv::Mat frame;
        while (true) {
            cap >> frame;
            if (frame.empty()) {
                std::cerr << "无法获取图像帧" << std::endl;
                break;
            }
            
            // 预处理图像
            cv::Mat processed = preprocessImage(frame);
            
            // 分类识别
            TrashType result = classifyTrash(processed);
            
            // 显示结果
            displayResult(frame, result, 0.95); // 假设置信度
            
            // 控制机械臂
            controlServo(result);
            
            // 检查退出条件
            char key = cv::waitKey(1);
            if (key == 'q' || key == 'Q') {
                break;
            }
            
            // 添加延迟避免过于频繁检测
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
    }
    
    // 模型再训练接口
    void retrainModel(const std::vector<std::string>& newImagePaths, 
                     const std::vector<int>& newLabels) {
        std::cout << "开始模型再训练..." << std::endl;
        
        // 这里应该调用训练脚本或训练函数
        // 实际实现需要根据具体的训练框架
        
        std::string command = "python3 retrain_model.py";
        for (size_t i = 0; i < newImagePaths.size(); i++) {
            command += " " + newImagePaths[i] + " " + std::to_string(newLabels[i]);
        }
        
        system(command.c_str());
        std::cout << "模型再训练完成" << std::endl;
        
        // 重新加载模型
        initializeModel();
    }
};

int main() {
    try {
        AITrashClassifier classifier;
        classifier.run();
    } catch (const std::exception& e) {
        std::cerr << "程序运行错误: " << e.what() << std::endl;
        return -1;
    }
    
    return 0;
}

配套的CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.10)
project(AITrashClassifier)

set(CMAKE_CXX_STANDARD 11)

# 查找OpenCV
find_package(OpenCV REQUIRED)

# 查找WiringPi
find_library(WIRINGPI_LIB wiringPi)

# 包含目录
include_directories(${OpenCV_INCLUDE_DIRS})

# 添加可执行文件
add_executable(ai_trash_classifier main.cpp)

# 链接库
target_link_libraries(ai_trash_classifier ${OpenCV_LIBS} ${WIRINGPI_LIB} pthread)

模型再训练脚本 retrain_model.py:

import tensorflow as tf
import sys
import os

def retrain_model(new_images, new_labels):
    # 模型再训练逻辑
    # 这里需要根据实际使用的模型框架实现
    pass

if __name__ == "__main__":
    # 解析命令行参数进行再训练
    args = sys.argv[1:]
    new_images = args[0::2]
    new_labels = list(map(int, args[1::2]))
    
    retrain_model(new_images, new_labels)

模块代码设计

#include <stdint.h>

// STM32F103寄存器定义
#define RCC_APB2ENR   (*(volatile uint32_t *)0x40021018)
#define RCC_APB1ENR   (*(volatile uint32_t *)0x4002101C)
#define RCC_CR        (*(volatile uint32_t *)0x40021000)
#define RCC_CFGR      (*(volatile uint32_t *)0x40021004)

// GPIOA寄存器
#define GPIOA_CRL     (*(volatile uint32_t *)0x40010800)
#define GPIOA_CRH     (*(volatile uint32_t *)0x40010804)
#define GPIOA_ODR     (*(volatile uint32_t *)0x4001080C)
#define GPIOA_IDR     (*(volatile uint32_t *)0x40010808)

// GPIOB寄存器
#define GPIOB_CRL     (*(volatile uint32_t *)0x40010C00)
#define GPIOB_CRH     (*(volatile uint32_t *)0x40010C04)
#define GPIOB_ODR     (*(volatile uint32_t *)0x40010C0C)
#define GPIOB_IDR     (*(volatile uint32_t *)0x40010C08)

// I2C1寄存器
#define I2C1_CR1      (*(volatile uint32_t *)0x40005400)
#define I2C1_CR2      (*(volatile uint32_t *)0x40005404)
#define I2C1_OAR1     (*(volatile uint32_t *)0x40005408)
#define I2C1_OAR2     (*(volatile uint32_t *)0x4000540C)
#define I2C1_DR       (*(volatile uint32_t *)0x40005410)
#define I2C1_SR1      (*(volatile uint32_t *)0x40005414)
#define I2C1_SR2      (*(volatile uint32_t *)0x40005418)
#define I2C1_CCR      (*(volatile uint32_t *)0x4000541C)
#define I2C1_TRISE    (*(volatile uint32_t *)0x40005420)

// TIM2寄存器(PWM)
#define TIM2_CR1      (*(volatile uint32_t *)0x40000000)
#define TIM2_CR2      (*(volatile uint32_t *)0x40000004)
#define TIM2_SMCR     (*(volatile uint32_t *)0x40000008)
#define TIM2_DIER     (*(volatile uint32_t *)0x4000000C)
#define TIM2_SR       (*(volatile uint32_t *)0x40000010)
#define TIM2_EGR      (*(volatile uint32_t *)0x40000014)
#define TIM2_CCMR1    (*(volatile uint32_t *)0x40000018)
#define TIM2_CCER     (*(volatile uint32_t *)0x40000020)
#define TIM2_CNT      (*(volatile uint32_t *)0x40000024)
#define TIM2_PSC      (*(volatile uint32_t *)0x40000028)
#define TIM2_ARR      (*(volatile uint32_t *)0x4000002C)
#define TIM2_CCR1     (*(volatile uint32_t *)0x40000034)
#define TIM2_CCR2     (*(volatile uint32_t *)0x40000038)
#define TIM2_CCR3     (*(volatile uint32_t *)0x4000003C)
#define TIM2_CCR4     (*(volatile uint32_t *)0x40000040)

// SPI1寄存器(TFT LCD)
#define SPI1_CR1      (*(volatile uint32_t *)0x40013000)
#define SPI1_CR2      (*(volatile uint32_t *)0x40013004)
#define SPI1_SR       (*(volatile uint32_t *)0x40013008)
#define SPI1_DR       (*(volatile uint32_t *)0x4001300C)

// 函数原型
void SystemInit(void);
void GPIO_Init(void);
void I2C1_Init(void);
void TIM2_Init(void);
void SPI1_Init(void);
void OV7670_Init(void);
void TFT_Init(void);
void TFT_WriteCommand(uint8_t cmd);
void TFT_WriteData(uint8_t data);
void TFT_WriteString(char *str);
void Servo_SetAngle(uint8_t servo_id, uint8_t angle);
uint8_t AI_Recognize(void);
void Delay(uint32_t count);

// 主函数
int main(void) {
    SystemInit();
    GPIO_Init();
    I2C1_Init();
    TIM2_Init();
    SPI1_Init();
    OV7670_Init();
    TFT_Init();
    
    TFT_WriteString("AI Garbage Classifier");
    Delay(1000000);
    
    while(1) {
        uint8_t trash_type = AI_Recognize();
        char display_str[20];
        switch(trash_type) {
            case 0: 
                sprintf(display_str, "Recyclable");
                Servo_SetAngle(0, 45);  // 舵机0推到可回收桶
                break;
            case 1: 
                sprintf(display_str, "Kitchen");
                Servo_SetAngle(1, 45);  // 舵机1推到厨余桶
                break;
            case 2: 
                sprintf(display_str, "Hazardous");
                Servo_SetAngle(2, 45);  // 舵机2推到有害桶
                break;
            default: 
                sprintf(display_str, "Other");
                break;
        }
        TFT_WriteString(display_str);
        Delay(2000000);
    }
}

// 系统时钟初始化
void SystemInit(void) {
    RCC_CR |= (1 << 0);  // 开启HSI
    while(!(RCC_CR & (1 << 1)));  // 等待HSI就绪
    
    RCC_CFGR = 0x00000000;  // 重置时钟配置
    RCC_APB2ENR |= (1 << 2) | (1 << 3) | (1 << 12);  // 使能GPIOA、GPIOB、SPI1时钟
    RCC_APB1ENR |= (1 << 21) | (1 << 0);  // 使能I2C1、TIM2时钟
}

// GPIO初始化
void GPIO_Init(void) {
    // I2C1引脚: PB6(SCL), PB7(SDA)
    GPIOB_CRL &= ~(0xFF << 24);
    GPIOB_CRL |= (0x0B << 24) | (0x0B << 28);  // 复用开漏输出, 50MHz
    
    // SPI1引脚: PA5(SCK), PA6(MISO), PA7(MOSI)
    GPIOA_CRL &= ~(0xFFF << 20);
    GPIOA_CRL |= (0x0B << 20) | (0x0B << 24) | (0x0B << 28);  // 复用推挽输出, 50MHz
    
    // TFT控制引脚: PA1(CS), PA2(DC), PA3(RST)
    GPIOA_CRL &= ~(0xFFF << 4);
    GPIOA_CRL |= (0x01 << 4) | (0x01 << 8) | (0x01 << 12);  // 推挽输出, 10MHz
    
    // 舵机PWM引脚: PA0(TIM2_CH1), PA1(TIM2_CH2), PA2(TIM2_CH3)
    GPIOA_CRL &= ~(0xFFF << 0);
    GPIOA_CRL |= (0x0B << 0) | (0x0B << 4) | (0x0B << 8);  // 复用推挽输出, 50MHz
    
    // OV7670并行数据引脚: PB0-PB7
    GPIOB_CRL = 0x88888888;  // 输入模式, 上拉/下拉
}

// I2C1初始化
void I2C1_Init(void) {
    I2C1_CR1 &= ~(1 << 0);  // 禁用I2C1
    I2C1_CR2 = 36;  // 设置APB1时钟频率(MHz)
    I2C1_CCR = 180;  // 100kHz I2C时钟
    I2C1_TRISE = 37;  // 设置上升时间
    I2C1_CR1 |= (1 << 0);  // 启用I2C1
}

// TIM2初始化(PWM)
void TIM2_Init(void) {
    TIM2_CR1 &= ~(1 << 0);  // 禁用TIM2
    TIM2_PSC = 719;  // 预分频器, 100kHz计数器时钟
    TIM2_ARR = 19999;  // 自动重载值, 20ms周期
    TIM2_CCMR1 |= (6 << 4) | (1 << 3);  // PWM模式1, 预加载使能
    TIM2_CCMR2 |= (6 << 4) | (1 << 3);  // 通道2
    TIM2_CCMR2 |= (6 << 12) | (1 << 11);  // 通道3
    TIM2_CCER |= (1 << 0) | (1 << 4) | (1 << 8);  // 使能通道1,2,3
    TIM2_CR1 |= (1 << 0);  // 启用TIM2
}

// SPI1初始化
void SPI1_Init(void) {
    SPI1_CR1 &= ~(1 << 6);  // 禁用SPI1
    SPI1_CR1 = (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5);  // 主模式, BR=101 (fPCLK/64)
    SPI1_CR2 |= (1 << 2);  // 8位数据格式
    SPI1_CR1 |= (1 << 6);  // 启用SPI1
}

// OV7670初始化
void OV7670_Init(void) {
    I2C1_CR1 |= (1 << 8);  // 生成START条件
    while(!(I2C1_SR1 & (1 << 0)));  // 等待SB置位
    I2C1_DR = 0x42;  // OV7670写地址
    while(!(I2C1_SR1 & (1 << 1)));  // 等待ADDR置位
    (void)I2C1_SR2;  // 清除ADDR
    
    // 配置OV7670寄存器
    uint8_t regs[][2] = {
        {0x12, 0x80},  // 复位
        {0x11, 0x80},  // 时钟分频
        {0x3A, 0x04},  // 数据格式
        {0x40, 0xD0},  // 尺寸控制
        {0x12, 0x0C}   // 输出格式
    };
    
    for(int i = 0; i < 5; i++) {
        I2C1_DR = regs[i][0];  // 寄存器地址
        while(!(I2C1_SR1 & (1 << 7)));  // 等待TxE
        I2C1_DR = regs[i][1];  // 寄存器值
        while(!(I2C1_SR1 & (1 << 7)));  // 等待TxE
    }
    
    I2C1_CR1 |= (1 << 9);  // 生成STOP条件
    while(I2C1_CR1 & (1 << 9));  // 等待STOP完成
}

// TFT初始化
void TFT_Init(void) {
    GPIOA_ODR |= (1 << 3);  // 拉高RST
    Delay(10000);
    GPIOA_ODR &= ~(1 << 3);  // 拉低RST
    Delay(10000);
    GPIOA_ODR |= (1 << 3);  // 拉高RST
    Delay(10000);
    
    TFT_WriteCommand(0x11);  // 睡眠退出
    Delay(100000);
    TFT_WriteCommand(0x29);  // 开启显示
}

void TFT_WriteCommand(uint8_t cmd) {
    GPIOA_ODR &= ~(1 << 2);  // DC低(命令)
    GPIOA_ODR &= ~(1 << 1);  // CS低
    while(!(SPI1_SR & (1 << 1)));  // 等待TxE
    SPI1_DR = cmd;
    while(!(SPI1_SR & (1 << 0)));  // 等待RXNE
    (void)SPI1_DR;
    GPIOA_ODR |= (1 << 1);  // CS高
}

void TFT_WriteData(uint8_t data) {
    GPIOA_ODR |= (1 << 2);  // DC高(数据)
    GPIOA_ODR &= ~(1 << 1);  // CS低
    while(!(SPI1_SR & (1 << 1)));  // 等待TxE
    SPI1_DR = data;
    while(!(SPI1_SR & (1 << 0)));  // 等待RXNE
    (void)SPI1_DR;
    GPIOA_ODR |= (1 << 1);  // CS高
}

void TFT_WriteString(char *str) {
    while(*str) {
        TFT_WriteData(*str++);
    }
}

// 舵机控制
void Servo_SetAngle(uint8_t servo_id, uint8_t angle) {
    uint16_t pulse = 1000 + (angle * 1000 / 180);  // 映射到1000-2000
    switch(servo_id) {
        case 0: TIM2_CCR1 = pulse; break;
        case 1: TIM2_CCR2 = pulse; break;
        case 2: TIM2_CCR3 = pulse; break;
    }
}

// AI识别函数(示例)
uint8_t AI_Recognize(void) {
    static uint8_t counter = 0;
    return counter++ % 3;  // 循环返回0,1,2
}

// 延时函数
void Delay(uint32_t count) {
    for(uint32_t i = 0; i < count; i++);
}

项目核心代码

#include "stm32f10x.h"
#include "ov7670.h"
#include "lcd.h"
#include "servo.h"
#include "ai_model.h"

// 系统时钟初始化
void RCC_Configuration(void)
{
    // 开启GPIO和对应外设时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | 
                    RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
    
    // 开启SPI1时钟(用于LCD)
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    
    // 开启TIM2时钟(用于舵机PWM)
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
}

// GPIO初始化
void GPIO_Configuration(void)
{
    // LCD SPI引脚配置
    GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7);
    GPIOA->CRL |= GPIO_CRL_MODE5 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7;
    
    // 舵机PWM引脚配置(TIM2 CH1-4)
    GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_CNF1 | 
                    GPIO_CRL_CNF2 | GPIO_CRL_CNF3);
    GPIOA->CRL |= (GPIO_CRL_CNF0_1 | GPIO_CRL_CNF1_1 | 
                   GPIO_CRL_CNF2_1 | GPIO_CRL_CNF3_1);
    GPIOA->CRL |= (GPIO_CRL_MODE0 | GPIO_CRL_MODE1 | 
                   GPIO_CRL_MODE2 | GPIO_CRL_MODE3);
}

// 定时器初始化(舵机PWM)
void TIM_Configuration(void)
{
    // TIM2 PWM配置 - 50Hz舵机信号
    TIM2->PSC = 72 - 1;        // 72MHz/72 = 1MHz
    TIM2->ARR = 20000 - 1;     // 1MHz/20000 = 50Hz
    
    // 通道1-4 PWM模式配置
    TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 |
                   TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2;
    TIM2->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 |
                   TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2;
    
    // 使能输出比较
    TIM2->CCER |= TIM_CCER_CC1E | TIM_CCER_CC2E | 
                  TIM_CCER_CC3E | TIM_CCER_CC4E;
    
    TIM2->CR1 |= TIM_CR1_ARPE;
    TIM2->EGR |= TIM_EGR_UG;
    TIM2->CR1 |= TIM_CR1_CEN;
}

// SPI初始化(LCD显示)
void SPI_Configuration(void)
{
    SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI |
                SPI_CR1_SPE | SPI_CR1_BR_0;
    SPI1->CR2 = SPI_CR2_SSOE;
}

// 系统延时函数
void Delay_ms(uint32_t ms)
{
    for(uint32_t i = 0; i < ms * 8000; i++);
}

// 控制舵机动作
void Servo_Action(uint8_t trash_type)
{
    switch(trash_type)
    {
        case 0: // 可回收垃圾
            TIM2->CCR1 = 1500; // 1.5ms脉冲
            break;
        case 1: // 厨余垃圾  
            TIM2->CCR2 = 1500;
            break;
        case 2: // 有害垃圾
            TIM2->CCR3 = 1500;
            break;
        case 3: // 其他垃圾
            TIM2->CCR4 = 1500;
            break;
    }
    Delay_ms(1000);
    
    // 复位所有舵机
    TIM2->CCR1 = TIM2->CCR2 = TIM2->CCR3 = TIM2->CCR4 = 0;
}

// LCD显示识别结果
void LCD_DisplayResult(uint8_t trash_type, float confidence)
{
    LCD_Clear();
    LCD_SetCursor(0, 0);
    
    switch(trash_type)
    {
        case 0:
            LCD_WriteString("Recyclable");
            break;
        case 1:
            LCD_WriteString("Kitchen");
            break;
        case 2:
            LCD_WriteString("Hazardous");
            break;
        case 3:
            LCD_WriteString("Other");
            break;
        default:
            LCD_WriteString("Unknown");
            break;
    }
    
    LCD_SetCursor(0, 1);
    LCD_WriteString("Confidence:");
    LCD_WriteFloat(confidence, 2);
}

int main(void)
{
    uint8_t image_buffer[320*240];  // 图像缓冲区
    uint8_t trash_type;
    float confidence;
    
    // 系统初始化
    RCC_Configuration();
    GPIO_Configuration();
    SPI_Configuration();
    TIM_Configuration();
    
    // 外设初始化
    OV7670_Init();
    LCD_Init();
    Servo_Init();
    AI_Model_Init();
    
    // 显示启动信息
    LCD_DisplayResult(4, 0.0f); // 显示Unknown
    
    while(1)
    {
        // 采集图像
        if(OV7670_Capture(image_buffer))
        {
            // AI模型推理
            if(AI_Model_Predict(image_buffer, &trash_type, &confidence))
            {
                // 显示识别结果
                LCD_DisplayResult(trash_type, confidence);
                
                // 控制舵机执行分类动作
                Servo_Action(trash_type);
            }
        }
        
        Delay_ms(2000); // 2秒间隔
    }
}

总结

本AI垃圾分类助手系统通过集成嵌入式AI技术,实现了高效的自动垃圾分类功能。系统利用摄像头采集垃圾图像,并在Raspberry Pi Pico主控芯片上运行轻量级AI模型,快速识别垃圾类型,如可回收、厨余和有害垃圾等,从而提升垃圾分类的准确性和效率。

硬件配置方面,系统采用Raspberry Pi Pico作为核心控制器,支持MicroPython环境,便于AI模型的轻量化部署;OV7670摄像头模块负责图像采集,SG90舵机驱动的机械臂执行分类动作,将垃圾推入对应垃圾桶,而1.44寸TFT LCD屏幕则实时显示识别结果,确保用户交互直观便捷。

此外,系统具备模型再训练能力,可灵活扩展新的垃圾种类,增强了适应性和可维护性。整体设计兼顾了成本效益和实用性,为智能环保应用提供了可靠的嵌入式解决方案,有助于推动垃圾分类的自动化进程。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。