第七屆全國大學生工程訓練大賽垃圾分類
- 前言
- 一、機械結構設計
- 二、視覺識別部分
- 1.引入庫
- 2.識別部分
- 三、上下位機通信方式:
- 1.高低電平通信:
- 1.2高低電平樹莓派部分:
- 2.stm32串口通信部分:
- 四、下位機電機驅動部分
- 1.電機:渦輪蝸桿電機(履帶負載較大,不可直接用步進直流電機)
- 2.有關于延時的改進:
- 3.stm32主函式:
- 五、炸電機:
文章目錄
- 前言
- 一、機械結構設計
- 二、視覺識別部分
- 1.引入庫
- 2.識別部分
- 三、上下位機通信方式:
- 1.高低電平通信:
- 1.2高低電平樹莓派部分:
- 2.stm32串口通信部分:
- 四、下位機電機驅動部分
- 1.電機:渦輪蝸桿電機(履帶負載較大,不可直接用步進直流電機)
- 2.有關于延時的改進:
- 3.stm32主函式:
- 五、炸電機:
前言
本人有幸代表內蒙古工業大學參加內蒙古自治區的全國工程訓練大賽省賽,并在初賽取得前三名的成績,可惜后來決賽由于樹莓派死機導致程式崩潰,從而無緣國賽,但是經過測驗,我們的識別程式可以做到識別率95%,分類準確率90%以上,
硬體設備:樹莓派4B+8G(用于視覺識別以及播放視頻)
stm32f103zet6 (用于下位機控制電機進行分類)
機械結構設計:雙層履帶交叉分揀
一、機械結構設計
示例:如圖所示,采用雙層履帶結構

然而實物卻是這樣:
二、視覺識別部分
1.引入庫
代碼如下(示例):
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import io
import time
import numpy as np
#import picamera
import cv2
import RPi.GPIO as GPIO
#import tensorflow as tf
from 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[:, :] = image
def 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 results
if 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)
#擷取畫面 寬度 設定為640
cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)
#擷取畫面 高度 設定為480
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
key_detect = 0
times=1
while (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) * 1000
label_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+1
if (times>1):
times=1
cv2.imshow('Detecting....',crop_img)
if cv2.waitKey(1) & 0xFF == ord('q'):
key_detect = 1
cap.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 4
void 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口速度為50MHz
GPIO_Init(GPIOE, &GPIO_InitStructure);
//根據設定引數初始化GPIOB.5
GPIO_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 2
void 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口速度為50MHz
GPIO_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); //串口初始化為115200
LED_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;}
}
}
}
}
五、炸電機:
在除錯程序中,我們炸了六個電機驅動,弄壞了一個步進電機
原因:一夜炸了六個電機驅動,我們依次排查電路接線、程式,最后竟然是電池的原
因!!!

視覺由吉飛敏大力支持
id:weixin_45785085
機械由秦俊酉大力支持
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/275869.html
標籤:其他
上一篇:電子設計大賽應該準備什么
