工训赛智能垃圾桶分类全流程代码分享(树莓派+stm32)

这篇具有很好参考价值的文章主要介绍了工训赛智能垃圾桶分类全流程代码分享(树莓派+stm32)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

目录

前言

一、机械结构设计

附机械部分图纸​编辑

二、树莓派视觉识别

1.数据集的制作

(1)数据集处理部分

 (2)数据集的优化

2.数据集打标签建议

(1)网站推荐

(2)问题示例

3.树莓派环境搭建部分

(1)  YOLOv5-Lite环境的部署

4.具体Yolov5-lite算法部分

(1)引入库部分

(2)视觉识别主要部分代码

(3)树莓派控制舵机部分代码

三、淘晶池串口屏通讯代码

四、stm32部分代码

1.UART 通讯部分函数



前言

本人有幸参加2023年10月的“联通杯”江苏省工程实践与创新能力大赛,即中国大学生工训赛江苏省省赛。我组在初赛时十中八(识别成功率100%,但是由于我们在机械结构方面考虑不佳导致垃圾卡住),最后获得江苏省一等奖。(以下代码仅用于分享,不做商业用途)

硬件设备:

树莓派4B+8G(用于视觉识别和控制系统总控)

stm32f103c8t6(用于满载和人体红外检测)

淘晶池串口屏(x5七寸电阻触摸款)

(本文模板参考csdn关于工训赛的多个相关文档,感谢多位博主的无私分享,使我组的垃圾桶在构建之初可以集众家之所长。但碍于网络上并没有关于此竞赛的完整搭建方案,故作此篇,仅作分享。)

附照片佐证:

智能垃圾桶机械结构设计,工训赛,yolo,python,YOLO,stm32,计算机视觉,目标检测,机器学习,人工智能

智能垃圾桶机械结构设计,工训赛,yolo,python,YOLO,stm32,计算机视觉,目标检测,机器学习,人工智能智能垃圾桶机械结构设计,工训赛,yolo,python,YOLO,stm32,计算机视觉,目标检测,机器学习,人工智能

一、机械结构设计

  1. 我组在讨论中为了防止投放垂直高度过高的问题,采取了增加斜坡的方式来减小下落速度,但是由于投放垃圾中存在扁平的碎砖片,表面过于粗糙,导致在斜坡上无法正常滑下导致在初赛中发生了垃圾卡在投放的问题。虽然我们为这个比赛准备时间很长,但是真正实践动手时间较短(大概20多天),很多实际问题只有在真正做出来后才能发现不足,加之我们只有一个机械方面学长参加(大爱学长,为了比赛承担了很多责任),导致我们在机械结构方面只是达到了最基础的功能(还有一个很重要的原因是我们都是控制小白,实现最基本的功能已经耗费我们太多时间了)

附机械部分图纸

智能垃圾桶机械结构设计,工训赛,yolo,python,YOLO,stm32,计算机视觉,目标检测,机器学习,人工智能智能垃圾桶机械结构设计,工训赛,yolo,python,YOLO,stm32,计算机视觉,目标检测,机器学习,人工智能

机械结构建议:

  1. ​有能力的还是要搞机械臂--这才是最终方案
  2. 或者多个差速传送带也是不错的选择,虽说其他省有一些裁判对暂存装置的理解不一样,但是国赛还是允许的(平心而论,个人认为传送带打破了只能有一个暂存区的设定,属于bug范畴,但是采用传送带大家实属无奈之举)

二、树莓派视觉识别

1.数据集的制作

(1)数据集处理部分

数据集的制作是识别训练模型最为关键的一点,并且在我的实际操作过程中发现有几大注意要点(一般人可不会告诉你哦)

1.一定要注意数据集的制作的多角度——以垃圾识别为例,在拍摄时一定要多角度多方位的拍摄多组照片,包括一个垃圾的360°照片和在托盘上的所有方位(位置点)照片。因为垃圾掉落下去具有不确定因素,可能在托盘停稳后落在托盘上的各个方位并且旋转成为各个角度,给目标检测带来不确定性。(为了更好的模拟识别环境,我选择在摄像头固定好位置后在树莓派上拍摄照片,以追求更好的仿真性)

以下是我在树莓派上运行的拍照程序(程序运行后将会逐帧保存拍摄照片——保存在images文件夹里,打标签时可以从树莓派里提取出来)

import cv2
from threading import Thread
import uuid
import os
import time
count = 0
def image_collect(cap):
    global count
    while True:
        success, img = cap.read()
        if success:
            file_name = str(uuid.uuid4())+'.jpg'
            cv2.imwrite(os.path.join('images',file_name),img)
            count = count+1
            print("save %d %s"%(count,file_name))
        time.sleep(0.4)
 
if __name__ == "__main__":
    
    os.makedirs("images",exist_ok=True)
    
    # 打开摄像头
    cap = cv2.VideoCapture(0)
 
    m_thread = Thread(target=image_collect, args=([cap]),daemon=True)
    
    while True:
 
        # 读取一帧图像
 
        success, img = cap.read()
 
        if not success:
 
            continue
 
        cv2.imshow("video",img)
 
        key =  cv2.waitKey(1) & 0xFF   
 
        # 按键 "q" 退出
        if key ==  ord('c'):
            m_thread.start()
            continue
        elif key ==  ord('q'):
            break
 
    cap.release()

(代码参考了csdn大佬——链接如下 基于树莓派4B的YOLOv5-Lite目标检测的移植与部署(含训练教程)_树莓派yolo-CSDN博客)

 (2)数据集的优化

自己在手动拍摄时无法做到真正的360°,并且如果全靠手动调整角度后期修改时会无形的添加工作量。为了更好的添加多角度照片,以下是我旋转已有照片的代码。

 使用前先在文件夹里创建imagesresult文件夹,然后把需要旋转的照片放在images里

from math import *
import cv2
import os
import glob
import imutils
import numpy as np


def rotate_img(img, angle):
    '''
    img   --image
    angle --rotation angle
    return--rotated img
    '''
    h, w = img.shape[:2]
    rotate_center = (w / 2, h / 2)
    # 获取旋转矩阵
    # 参数1为旋转中心点;
    # 参数2为旋转角度,正值-逆时针旋转;负值-顺时针旋转
    # 参数3为各向同性的比例因子,1.0原图,2.0变成原来的2倍,0.5变成原来的0.5倍
    M = cv2.getRotationMatrix2D(rotate_center, angle, 1.0)
    # 计算图像新边界
    new_w = int(h * np.abs(M[0, 1]) + w * np.abs(M[0, 0]))
    new_h = int(h * np.abs(M[0, 0]) + w * np.abs(M[0, 1]))    # 调整旋转矩阵以考虑平
    M[0, 2] += (new_w - w) / 2
    M[1, 2] += (new_h - h) / 2

    rotated_img = cv2.warpAffine(img, M, (new_w, new_h))
    return rotated_img


if __name__ == '__main__':

    output_dir = "result"
    image_names = glob.glob("images/e080b004-ddd9-404c-a518-a8f5671bc658.jpg")#这里改为自己需要旋转的照片名称,注意路径

    for image_name in image_names:
        image = cv2.imread(image_name, -1)

        for i in range(1, 361, 3):  # 修改步长为3(每3°旋转一次)可修改
            rotated_img1 = rotate_img(image, i)
            basename = os.path.basename(image_name)
            tag, _ = os.path.splitext(basename)
            cv2.imwrite(os.path.join(output_dir, 'qqq-%d.jpg' % i), rotated_img1)#这里可以修改自己的文件名称

2.数据集打标签建议

(1)网站推荐

官方指定的labelimg特别容易崩溃,本人深受其害,后发现makesense网站出奇好用,现推荐给大家——附链接  Make Sense

(2)问题示例

以本项目为例(垃圾识别)需要四种垃圾,标签需要转出为yolo形式,yolo中的第一个数字含义为物体的种类,如下图所示,不同数字代表不同种类。所以如果是小组四个人在打标签,一个人打一种,在配置标签种类时四种也都需要配上,并且每个人打标签的名称顺序以及名称的命名也要保持一致(0,1,2,3)

0 0.503021 0.511827 0.477341 0.261498
3 0.319119 0.725658 0.050600 0.053973

如果打错了想要修改标签数字怎么办,以下是我的代码 

import os

def modify_yolo_txt(file_path, new_value):
    with open(file_path, 'r') as file:
        lines = file.readlines()

    modified_lines = []
    for line in lines:
        parts = line.split(' ')
        parts[0] = str(new_value)
        modified_lines.append(' '.join(parts))

    with open(file_path, 'w') as file:
        file.writelines(modified_lines)

# 遍历指定目录下的所有txt文件
directory = 'C:/Users/under/Desktop/Hazardous/labels/val'#修改的标签文件夹(注意绝对路径)
new_value = 1 #修改想要的数字

for filename in os.listdir(directory):
    if filename.endswith('.txt'):
        file_path = os.path.join(directory, filename)
        modify_yolo_txt(file_path, new_value)

3.树莓派环境搭建部分

本项目识别采用Yolov5-lite算法,YOLOv5-Lite 与 YOLOv5 相比虽然牺牲了部分网络模型精度,但是缺极大的提升了模型的推理速度,该模型属性将更适合实战部署使用,并且使用onnxruntime替代pytorch,其运行速度更一步提升。

(1)  YOLOv5-Lite环境的部署

requirements.txt请在文章末尾资源中下载

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

4.具体Yolov5-lite算法部分

(1)引入库部分

#!/usr/bin/python
import serial
import time
import math
import smbus
import cv2
import numpy as np
import onnxruntime as ort
import random

(2)视觉识别主要部分代码

if __name__=='__main__':
    
    model_pb_path = "......"  # 这里改为自己的onnx模型,注意用yolov5-lite中的export.py导出
    so = ort.SessionOptions()
    net = ort.InferenceSession(model_pb_path, so)
    
    dic_labels = {
        0:'RecyclableWaste',
        1:'Hazardous',
        2:'kitchengarbage',
        3:'Other waste'
    }
    
    model_h = 320
    model_w = 320
    nl = 3
    na = 3
    stride = [8., 16., 32.]
    anchors = [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]]
    anchor_grid = np.asarray(anchors, dtype=np.float32).reshape(nl, -1, 2)
    
    video = 0
    cap = cv2.VideoCapture(video)
    flag_det = True
    last_detect = time.time()
    while True:

        success, img0 = cap.read()

        if success:
            
            if flag_det and time.time() - last_detect > 1.0:
                img = cv2.resize(img0, [model_w, model_h], interpolation=cv2.INTER_AREA)
                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                img = img.astype(np.float32) / 255.0
                blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0)
                outs = net.run(None, {net.get_inputs()[0].name: blob})[0].squeeze(axis=0)
                outs = cal_outputs(outs, nl, na, model_w, model_h, anchor_grid, stride)
                img_h, img_w, _ = np.shape(img0)
                boxes, confs, ids = post_process_opencv(outs, model_h, model_w, img_h, img_w, thred_nms=0.4, thred_cond=0.5)
                #FPS
                t1 = time.time()
                det_boxes, scores, ids = infer_img(img0, net, model_h, model_w, nl, na, stride, anchor_grid, thred_nms=0.4, thred_cond=0.5)
                t2 = time.time()
                receive_and_send()
                for box, score, id in zip(det_boxes, scores, ids):
                    label = '%s:%.2f' % (dic_labels[id], score)
                    plot_one_box(box.astype(np.int16), img0, color=(255, 0, 0), label=label, line_thickness=None)

 (代码参考了csdn大佬——链接如下 基于树莓派4B的YOLOv5-Lite目标检测的移植与部署(含训练教程)_树莓派yolo-CSDN博客)

(3)树莓派控制舵机部分代码

class PCA9685:
    # Registers/etc.
    __SUBADR1 = 0x02
    __SUBADR2 = 0x03
    __SUBADR3 = 0x04
    __MODE1 = 0x00
    __PRESCALE = 0xFE
    __LED0_ON_L = 0x06
    __LED0_ON_H = 0x07
    __LED0_OFF_L = 0x08
    __LED0_OFF_H = 0x09
    __ALLLED_ON_L = 0xFA
    __ALLLED_ON_H = 0xFB
    __ALLLED_OFF_L = 0xFC
    __ALLLED_OFF_H = 0xFD

    def __init__(self, address=0x40, debug=False):
        self.bus = smbus.SMBus(1)
        self.address = address
        self.debug = debug
        if (self.debug):
            print("Reseting PCA9685")
        self.write(self.__MODE1, 0x00)

    def write(self, reg, value):
        "Writes an 8-bit value to the specified register/address"
        self.bus.write_byte_data(self.address, reg, value)
        if (self.debug):
            print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))

    def read(self, reg):
        "Read an unsigned byte from the I2C device"
        result = self.bus.read_byte_data(self.address, reg)
        if (self.debug):
            print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
        return result

    def setPWMFreq(self, freq):
        "Sets the PWM frequency"
        prescaleval = 25000000.0    # 25MHz
        prescaleval /= 4096.0       # 12-bit
        prescaleval /= float(freq)
        prescaleval -= 1.0
        if (self.debug):
            print("Setting PWM frequency to %d Hz" % freq)
            print("Estimated pre-scale: %d" % prescaleval)
        prescale = math.floor(prescaleval + 0.5)
        if (self.debug):
            print("Final pre-scale: %d" % prescale)

        oldmode = self.read(self.__MODE1);
        newmode = (oldmode & 0x7F) | 0x10        # sleep
        self.write(self.__MODE1, newmode)        # go to sleep
        self.write(self.__PRESCALE, int(math.floor(prescale)))
        self.write(self.__MODE1, oldmode)
        time.sleep(0.005)
        self.write(self.__MODE1, oldmode | 0x80)

    def setPWM(self, channel, on, off):
        "Sets a single PWM channel"
        self.write(self.__LED0_ON_L + 4 * channel, on & 0xFF)
        self.write(self.__LED0_ON_H + 4 * channel, on >> 8)
        self.write(self.__LED0_OFF_L + 4 * channel, off & 0xFF)
        self.write(self.__LED0_OFF_H + 4 * channel, off >> 8)
        if (self.debug):
            print("channel: %d  LED_ON: %d LED_OFF: %d" % (channel, on, off))

    def setServoPulse(self, channel, pulse):
        "Sets the Servo Pulse,The PWM frequency must be 50HZ"
        pulse = pulse * 4096 / 20000  # PWM frequency is 50HZ,the period is 20000us
        # 调整舵机旋转速度更快
        pulse = pulse * 1
        self.setPWM(channel, 0, int(pulse))

三、淘晶池串口屏通讯代码

        # 根据接收到的数据发送对应的控制指令
            if value == 1:
            # 向串口屏发送控制指令
                send_command("click b5,1")
                send_end()
            elif value == 2:
                # 向串口屏发送控制指令
                send_command("click b6,1")
                send_end()
            elif value == 3:
                # 向串口屏发送控制指令
                send_command("click b7,1")
                send_end()
            elif value == 4:
                # 向串口屏发送控制指令
                send_command("click b8,1")
                send_end()
            elif value == 5:
                # 向串口屏发送控制指令
                send_command("click b0,1")
                send_end()
            else:
                print("Invalid value: ", value)  # 处理无效的值
        except ValueError:
            print("Invalid data format")

四、stm32部分代码

1.UART 通讯部分函数

树莓派与stm32通讯我们采用的是树莓派4B与 STM32 的 UART 通讯 ,此种方法我们认为较为简单,代码比较容易

ser32 = serial.Serial('/dev/ttyAMA0', 9600, timeout=1)
# 解码从STM32接收到的数据
def decode_data(data):
    decoded_data = data.decode().strip()  # 使用ASCII解码并移除首尾空白字符
    return decoded_data
def receive_and_send():

    # 从STM32接收数据
    data = ser32.read(1)

    # 解码接收到的数据
    decoded_data = decode_data(data)

# 如果接收到的数据不为空
    if decoded_data:
    # 尝试将数据转换为整数
        try:
            value = int(decoded_data)

代码包含——yolov5lite环境部署,视觉识别,stm32部分所有代码,淘晶池串口屏打包代码及UI界面,舵机控制代码以及所有代码整合。

数据集共四千多张近五千张(已标注)

本项目所有代码均已开源,需要的可以私聊 可提供垃圾桶搭建前后相关建议

详情+QQ  3037034536文章来源地址https://www.toymoban.com/news/detail-856764.html

到了这里,关于工训赛智能垃圾桶分类全流程代码分享(树莓派+stm32)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 毕业设计 单片机语音识别分类智能垃圾桶(STM32 ESP8266 LD3320)

    离线语音识别识别垃圾种类并且垃圾桶自动翻盖: 说出唤醒词“垃圾桶”后,再说一句垃圾名称,语音识别模块端识别到相应,便会将结果通过串口发送到STM32端,STM32端接着会发送打开相应垃圾桶盖的指令,6s后,垃圾桶盖自动关闭。其中翻盖功能是通过STM32端控制舵

    2024年04月26日
    浏览(71)
  • 【STM32单片机】基于语音识别的智能分类垃圾桶,ld3320语音识别模块如何使用,mp3播放模块如何使用

    对于“可回收物”“有害垃圾”“厨余垃圾”“其它垃圾”,不能分清扔到哪个垃圾桶怎么办? 基于语音识别的智能分类垃圾桶,识别到就打开对应的垃圾桶,完全没有分不清的烦恼。 //可回收物:塑料瓶、玻璃瓶、铝罐、纸张、纸板、报纸、纸质包装盒、金属罐头等

    2024年02月10日
    浏览(60)
  • 智能语音识别垃圾桶

    主要采用Arduino单片机与LD3320语音模块结合,实现垃圾分类。 目录 设计方案 详细设计 总结 简介:传统垃圾桶都是采用不封口,手动或者脚踩的方式打开桶盖投递垃圾,不但操作麻烦,而且对人体的卫生健康有不利的影响,对空气环境也会造成污染。针对以上存在的问题,本

    2023年04月27日
    浏览(48)
  • 智能垃圾桶项目【课程设计】【全套开源】

    1.1 功能描述 当物体接近垃圾桶时,垃圾桶自动打开,并有\\\" 嘀\\\"的一声;当远离垃圾桶后,自动关闭; 当按下按键时,垃圾桶也可以自动打开,并有\\\" 嘀\\\"的一声; 当感受到震动时,也自动打开,(同上) 垃圾桶开时,led1 灯开,led2 灯关;当垃圾桶关时,led1 灯关,led2 灯开

    2024年01月16日
    浏览(47)
  • 机器人制作开源方案 | 智能垃圾桶

           智能垃圾桶是一种利用物联网技术和智能感知能力的智能设备,旨在提高垃圾分类和处理的效率。通常具备以下特点和功能: ① 智能感知:智能垃圾桶配备各种传感器,如压力传感器、红外线传感器等,可以实时感知垃圾桶内垃圾的填充情况和类型。 ② 垃圾分类:

    2024年02月16日
    浏览(57)
  • 智能垃圾桶丨悦享便捷生活

           垃圾桶是人们日常生活所必不可少的必需品,它让生活中所产生的垃圾有了一个正确的存放地方。随着生产技术的迅速发展,垃圾桶也得以更新换代。由最初的简单式的圆筒式垃圾桶,到现在出现的感应式垃圾桶、智能语音控制垃圾桶,垃圾桶也变得越来越智能,让

    2024年02月07日
    浏览(55)
  • 基于STM32的智能语音垃圾桶设计

    一. 系统设计及框图: 本设计整体功能如下: 1. 超声波感应到有人靠近时语音提示“垃圾放置请分类”。 2. 检测垃圾筒时是否满,当满时语音提示“垃圾桶已满”。 3. 光传感器检测,指示灯指示。 4. 语音识别不同的垃圾类型。 二. 离线语音模块的设计: 离线语音模块SU-0

    2024年02月01日
    浏览(40)
  • 32、基于51单片机红外智能垃圾桶系统设计

    随着现代化进程的日益推进,科技越来越发达,人们的生活水平也提高了,城市化程度越来越高,与此同时也带了许多问题,生活垃圾越来越多垃圾设施却不够完善。无论是在公共场合还是家庭厨房的垃圾大都是没有盖或者有盖但需要人用手打开的,比如夏天的家庭厨房没有

    2023年04月14日
    浏览(60)
  • 基于STM32+华为云IOT设计的智能垃圾桶

    在商业街、小吃街和景区等人流密集的场所,垃圾桶的及时清理对于提供良好的游客体验至关重要。然而,传统的垃圾桶清理方式通常是定时或定期进行,无法根据实际情况进行及时响应,导致垃圾桶溢满,影响环境卫生,给游客带来不便和不满。 为了解决这一问题,本项目

    2024年02月08日
    浏览(107)
  • 【毕业设计】基于单片机的智能感应垃圾桶设计与实现 - 物联网 stm32 嵌入式

    Hi,大家好,这里是丹成学长,今天向大家介绍一个 单片机项目 基于单片机的智能感应垃圾桶设计与实现 大家可用于 课程设计 或 毕业设计 单片机-嵌入式毕设选题大全及项目分享: https://blog.csdn.net/m0_71572576/article/details/125409052 学长设计的系统主要使用 STC89C52 单片机为基础设

    2024年01月17日
    浏览(146)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包