第七届全国大学生工程训练大赛垃圾分类
- 前言
- 一、机械结构设计
- 1.Solidworks建模
- 2.建模的不足以及改进
- 1.挡板的添加
- 2.履带防滑
- 3.整体实物
- 二、视觉识别部分
- 1.引入库
- 2.识别部分
- 三、上下位机通信方式:
- 1.高低电平通信:
- 1.2高低电平树莓派部分:
- 2.stm32串口通信部分:
- 四、下位机电机驱动部分
- 1.电机:涡轮蜗杆电机(履带负载较大,不可直接用步进直流电机)
- 2.有关于延时的改进:
- 3.stm32主函数:
- 五、炸电机:
文章目录
- 前言
- 一、机械结构设计
- 1.Solidworks建模
- 2.建模的不足以及改进
- 1.挡板的添加
- 2.履带防滑
- 3.整体实物
- 二、视觉识别部分
- 1.引入库
- 2.识别部分
- 三、上下位机通信方式:
- 1.高低电平通信:
- 1.2高低电平树莓派部分:
- 2.stm32串口通信部分:
- 四、下位机电机驱动部分
- 1.电机:涡轮蜗杆电机(履带负载较大,不可直接用步进直流电机)
- 2.有关于延时的改进:
- 3.stm32主函数:
- 五、炸电机:
前言
本人有幸代表内蒙古工业大学参加内蒙古自治区的全国工程训练大赛省赛,并在初赛取得前三名的成绩。可惜后来决赛由于树莓派死机导致程序崩溃,从而无缘国赛。但是经过测试,我们的识别程序可以做到识别率95%,分类准确率90%以上。
硬件设备:树莓派4B+8G(用于视觉识别以及播放视频)
stm32f103zet6 (用于下位机控制电机进行分类)
机械结构设计:双层履带交叉分拣
本人具有嵌入式开发经验两年,可代指导单片机,linux,物联网视觉识别,各种机器人的大创、毕设、项目 具体联系:qq
1549843074
一、机械结构设计
1.Solidworks建模
示例:如图所示,采用双层履带结构
2.建模的不足以及改进
1.挡板的添加
从上履带识别后,移交至第二层履带时。会出现飞出去的情况导致分类失败,于是我们在履带两侧以及垃圾桶整体四周加上挡板。这样情况大大改善。
挡板:
2.履带防滑
当进行调试过程中,如果出现瓶子、电池等容易滚的物体,很容易在投掷过程中滚下去导致无法实现识别。为此,我们决定在履带上用胶水粘上小突起,经检验这样能很好解决这个问题。
3.整体实物
整体实物图:
俯视图:
二、视觉识别部分
1.引入库
代码如下(示例):
from __future__ import absolute_import
from __future__ import division
from __future__ import print_functionimport argparse
import io
import time
import numpy as np
#import picamera
import cv2
import RPi.GPIO as GPIO#import tensorflow as tffrom PIL import Image
from tflite_runtime.interpreter import Interpreter
2.识别部分
代码如下:
def load_labels(path):with open(path, 'r') as f:return {i: line.strip() for i, line in enumerate(f.readlines())}def set_input_tensor(interpreter, image):tensor_index = interpreter.get_input_details()[0]['index']input_tensor = interpreter.tensor(tensor_index)()[0]input_tensor[:, :] = imagedef classify_image(interpreter, image, top_k=1):"""Returns a sorted array of classification results."""set_input_tensor(interpreter, image)interpreter.invoke()output_details = interpreter.get_output_details()[0]output = np.squeeze(interpreter.get_tensor(output_details['index']))# If the model is quantized (uint8 data), then dequantize the resultsif output_details['dtype'] == np.uint8:scale, zero_point = output_details['quantization']output = scale * (output - zero_point)ordered = np.argpartition(-output, top_k)return [(i, output[i]) for i in ordered[:top_k]]def main():parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)parser.add_argument('--model', help='File path of .tflite file.', required=True)parser.add_argument('--labels', help='File path of labels file.', required=True)args = parser.parse_args()labels = load_labels(args.labels)#interpreter = tf.lite.Interpreter(args.model)interpreter = Interpreter(args.model)interpreter.allocate_tensors()_, height, width, _ = interpreter.get_input_details()[0]['shape']#with picamera.PiCamera(resolution=(640, 480), framerate=30) as camera:#camera.start_preview()cap = cv2.VideoCapture(0)#擷取畫面 寬度 設定為640cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)#擷取畫面 高度 設定為480cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)key_detect = 0times=1while (key_detect==0):ret,image_src =cap.read(0)frame_width=image_src.shape[1]frame_height=image_src.shape[0]cut_d=int((frame_width-frame_height)/2)crop_img=image_src[0:frame_height,cut_d:(cut_d+frame_height)]image=cv2.resize(crop_img,(224,224),interpolation=cv2.INTER_AREA)start_time = time.time()if (times==1):results = classify_image(interpreter, image)elapsed_ms = (time.time() - start_time) * 1000label_id, prob = results[0]print(labels[label_id],prob)num=int(label_id)cv2.putText(crop_img,labels[label_id] + " " + str(round(prob,3)), (5,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 1, cv2.LINE_AA)times=times+1if (times>1):times=1cv2.imshow('Detecting....',crop_img)if cv2.waitKey(1) & 0xFF == ord('q'):key_detect = 1cap.release()cv2.destroyAllWindows()if __name__ == '__main__':main()
以上基于tenserflow,tenserflow适合在树莓派上跑,但是如果数据集过大就会崩溃(我们就是因为这个原因,止步于省赛)建议数据集采样图片时控制在2000张左右,不然会崩
三、上下位机通信方式:
1.高低电平通信:
最开始因为下位机仅仅需要接受树莓派识别结果,而结果种类只有四种,于是乎最开始想到的是:树莓派往gpio写高低电平,stm32浮空输入电平结果,通过排列组合进行通信。源码如下:
communicate.h:#define Type_2 PEin(10)// PF13#define Type_3 PEin(11)// PF14#define Type_4 PEin(12)// PF15#define Type_5 PEin(13)// PF16#define Nothing 0#define hazardous_waste 1#define other_waste 2#define Recyclable_waste 3#define Kitchen_waste 4void communicate_Init(void);//初始化int adjust(void);communicate.c:void communicate_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //使能PB,PE端口时钟GPIO_InitStructure.GPIO_Pin =GPIO_Pin_10 |GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13; //LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5GPIO_SetBits(GPIOE,GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13); //PB.5 输出高}int adjust(void){//判断函数delay_ms(10);//防抖if(Type_2==0&&Type_3==1&&Type_4==1&&Type_5==1)//第一个树莓派io输出低电平为可回收return Recyclable_waste;if(Type_2==1&&Type_3==0&&Type_4==1&&Type_5==1)//第二个io输出低电平为有害垃圾return hazardous_waste;if(Type_2==1&&Type_3==1&&Type_4==0&&Type_5==1)//第三个输出低电平为其他return other_waste;if(Type_2==1&&Type_3==1&&Type_4==1&&Type_5==0)//第四个输出低电平为厨余垃圾return Kitchen_waste;return 0;}
1.2高低电平树莓派部分:
GPIO.setmode(GPIO.BCM)GPIO.setup(2,GPIO.OUT)GPIO.setup(3,GPIO.OUT)GPIO.setup(4,GPIO.OUT)GPIO.setup(17,GPIO.OUT)if num == 0:GPIO.output(2,GPIO.HIGH)GPIO.output(3,GPIO.HIGH)GPIO.output(4,GPIO.HIGH)GPIO.output(17,GPIO.HIGH)print('')elif num == 1:GPIO.output(2,GPIO.LOW)GPIO.output(3,GPIO.HIGH)GPIO.output(4,GPIO.HIGH)GPIO.output(17,GPIO.HIGH)print('可回收垃圾')elif num == 2:GPIO.output(2,GPIO.HIGH)GPIO.output(3,GPIO.LOW)GPIO.output(4,GPIO.HIGH)GPIO.output(17,GPIO.HIGH)print('有害垃圾')elif num == 3:GPIO.output(2,GPIO.HIGH)GPIO.output(3,GPIO.HIGH)GPIO.output(4,GPIO.LOW)GPIO.output(17,GPIO.HIGH)print('其他垃圾')elif num == 4:GPIO.output(2,GPIO.HIGH)GPIO.output(3,GPIO.HIGH)GPIO.output(4,GPIO.HIGH)GPIO.output(17,GPIO.LOW)print('厨余垃圾')else:GPIO.output(2,GPIO.HIGH)GPIO.output(3,GPIO.HIGH)GPIO.output(4,GPIO.HIGH)GPIO.output(17,GPIO.HIGH)
2.stm32串口通信部分:
void usb_communicate(void){u16 t;u16 len;if(USART_RX_STA&0x8000){//防抖delay_ms(100);delay_ms(100);if(USART_RX_STA&0x8000){ len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度printf("\r\n您发送的消息为:\r\n\r\n");for(t=0;t<len;t++){USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束}}}}int USB_adjust(void){usb_communicate();if(USART_RX_BUF[0]=='0'){USART_RX_STA=0;return Nothing ;//没有垃圾}if(USART_RX_BUF[0]=='1'){USART_RX_STA=0;return Recyclable_waste ;}//可回收if(USART_RX_BUF[0]=='2'){USART_RX_STA=0;return hazardous_waste ;}//有害if(USART_RX_BUF[0]=='3'){USART_RX_STA=0;return other_waste;}//可回收垃圾if(USART_RX_BUF[0]=='4'){USART_RX_STA=0;return Kitchen_waste;}//厨余垃圾USART_RX_STA=0;return EOF;//错误标志位
}
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
四、下位机电机驱动部分
1.电机:涡轮蜗杆电机(履带负载较大,不可直接用步进直流电机)
驱动:l298N(12V)
//.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "sys.h"#define IN_1 PFout(1)// PB5
#define IN_2 PFout(2)// PE5
#define IN_3 PFout(3)// PB5
#define IN_4 PFout(4)// PE5 #define ZHENGXIANG 0
#define FANXIANG 1
#define STOP 2void motor_Init(void);//初始化
void zongxiang_run(u16 model);
void hengxiang_run(u16 model);
void _delay_s(u16 s); void stop(void);
void Recyclable_waste_work(void);//可回收3号
void hazardous_waste_work(void);//有害垃圾1号
void other_waste_waste_work(void);//其他垃圾2号
void Kitchen_waste_waste_work(void);//厨余垃圾4号
#endif
//.c
#include "motor.h"
#include "delay.h"#define ZHENGXIANG 0
#define FANXIANG 1
#define STOP 2
void motor_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE); //使能PB,PE端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //LED0-->PB.5 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOF, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5}void hengxiang_run(u16 model){switch(model){case ZHENGXIANG : {IN_3=0;IN_4=1;break;}case FANXIANG :{IN_3=1;IN_4=0;break;}case STOP:{IN_3=1;IN_4=1;break;}}
}void zongxiang_run(u16 model){switch(model){case ZHENGXIANG : {IN_1=0;IN_2=1;break;}case FANXIANG :{IN_1=1;IN_2=0;break;}case STOP:{IN_1=1;IN_2=1;break;}}
}void _delay_s(u16 s){int i; for(i=0;i<=s;i++)delay_ms(1000);}void Recyclable_waste_work(void){//可回收3号hengxiang_run(FANXIANG);zongxiang_run(FANXIANG);_delay_s(5);zongxiang_run(STOP);hengxiang_run(STOP);_delay_s(1);}void hazardous_waste_work(void){//有害垃圾1号zongxiang_run(ZHENGXIANG);hengxiang_run(FANXIANG);_delay_s(5);zongxiang_run(STOP);hengxiang_run(STOP);_delay_s(1);}void other_waste_waste_work(void){//其他垃圾2号hengxiang_run(ZHENGXIANG);zongxiang_run(ZHENGXIANG);_delay_s(5);zongxiang_run(STOP);hengxiang_run(STOP);_delay_s(1);}void Kitchen_waste_waste_work(void){//厨余垃圾hengxiang_run(ZHENGXIANG);zongxiang_run(FANXIANG);_delay_s(5);zongxiang_run(STOP);hengxiang_run(STOP);_delay_s(1);}
void stop(void){zongxiang_run(STOP);hengxiang_run(STOP);}
2.有关于延时的改进:
本人在调试时发现keil环境中:delay_ms(1000)和delay_ms(3000)差别不大竟然有一下发现:
void _delay_s(u16 s){int i; for(i=0;i<=s;i++)delay_ms(1000);
}//明显好于s*delay_ms(1000)
3.stm32主函数:
int main(void){
// u16 adcx;
// float temp;delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级uart_init(115200); //串口初始化为115200LED_Init(); //LED端口初始化LCD_Init(); Adc_Init(); //ADC初始化POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"Elite STM32"); LCD_ShowString(60,70,200,16,16,"ADC TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(60,110,200,16,16,"2015/1/14"); //显示提示信息POINT_COLOR=BLUE;//设置字体为蓝色LCD_ShowString(60,130,200,16,16,"ADC_CH0_VAL:"); LCD_ShowString(60,150,200,16,16,"ADC_CH0_VOL:0.000V"); motor_Init();while(1){if(adjust()){delay_ms(200);delay_ms(200);switch(adjust()){case hazardous_waste:{//有害垃圾分类hazardous_waste_work();_delay_s(5);break;}case other_waste :{//其他垃圾分类other_waste_waste_work();_delay_s(5);break;}case Recyclable_waste :{//可回收垃圾分类Recyclable_waste_work();_delay_s(5);break;}case Kitchen_waste :{//厨余垃圾分类Kitchen_waste_waste_work();_delay_s(5);break;}default :{ //没垃圾 stop();break;}}}
}}
五、炸电机:
在调试过程中,我们炸了六个电机驱动,弄坏了一个步进电机
原因:一夜炸了六个电机驱动,我们依次排查电路接线、程序,最后竟然是电池的原
因!!!
机械由秦俊酉大力支持