Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Archives
Today
Total
관리 메뉴

Deep CV

페트병 라벨 AI 자동 분류기 만들기[아두이노, 파이썬, 딥러닝, CNN] 본문

딥러닝

페트병 라벨 AI 자동 분류기 만들기[아두이노, 파이썬, 딥러닝, CNN]

Present_Kim 2021. 10. 6. 18:34

작품 개발 의도 이런 건 생략하고 간단하게 어떻게 만들었는지 가겠습니다.

 

레츠고~

 

 

모습은 이렇습니다. 처음 구상할 때 쓰레기통을 생각해서 라벨이 부착돼 있으면 뱉는 걸 생각해서 생김새가 이렇습니다.

상품성을 위해 자동 선별기로 구현 방향을 바꿔 컨베이어 벨트를 추가했습니다.

 

모터, 초음파 제어는 아두이노를 사용했습니다. 

 

작동 알고리즘

1. 컨베이어 벨트가 작동되고, 초음파에서 거리를 측정해서 일정 거리 이하가 되면 잠깐 더 작동 후에 멈춥니다.

2. PC에게 신호를 보내면 PC에서 웹캠 모듈을 통해 사진을 촬영하고, 전처리 후 prediction을 합니다.

3. 결과에 따라 이름을 붙여 사진을 저장합니다.

4. 결과를 신호를 다시 아두이노에 보냅니다. 

5. 신호에 따라 서보모터 방향을 다르게 돌립니다.

 

회로도

 서보모터는 9V-12V 짜리를 사용했으나, 계획이 수정되면서 큰 힘이 필요 없어 USB 입력 전압으로 충분했습니다.

외부 전압은 없지만 Vin에 연결했습니다. 서보모터와 초음파 센서의 Vin은 분리해서 연결해 주세요.

(초음파랑 같이 연결되면 제대로 작동하지 않고, 외부 전압이 연결됐을 때는 아두이노가 고장 나기도 했습니다.)  

코드

아두이노

#include <Servo.h>

Servo myservo1;  // 분류대 서보모터
Servo myservo2;  // create servo object to control a servo

//분류대
int pos1 = 0;    // variable to store the servo position
int flag = 3; // 모드 설정
char motor_flag = '0'; // 회전 방향

int input_data = 10; 
int output_data = 0;
float duration;
float distance = 50;

// 컨베이어 벨트
int motor_1 = 5; // IN1
int motor_2 = 6; // IN2
int trigPin2 = 4;
int echoPin2 = 3;

void setup() {

  Serial.begin(9600);
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT);
  pinMode(motor_1, OUTPUT);
  pinMode(motor_2, OUTPUT);
}

void loop() {
  // 컨베이어 작동
  if (flag == 3){ 
      analogWrite(motor_1, 255);
      digitalWrite(motor_2, 0);
      
      while(distance > 7 and flag == 3){
          digitalWrite(trigPin2, HIGH);
          delay(10);
          digitalWrite(trigPin2, LOW);
          // echoPin 이 HIGH를 유지한 시간을 저장 한다.
          duration = pulseIn(echoPin2, HIGH); 
          // HIGH 였을 때 시간(초음파가 보냈다가 다시 들어온 시간)을 가지고 거리를 계산 한다.
          distance = ((float)(340 * duration) / 10000) / 2; 
          
          if (distance<=10){
                  delay(3500);
                  flag = 0;
                  analogWrite(motor_1, 0);
                  digitalWrite(motor_2, 0);  
                  distance = 100;

            }
      }
  }
  // PC에 신호를 전송(쓰레기 값이 들어가는 일이 있어 delay 부여)
  if (flag == 0){
      flag = 1;
      Serial.println(1);
      delay(2000);
      
 
  }
  
  // PC 결과를 받아 서보 모터 제어
  if (flag == 1){
  while(Serial.available()>0)
  {
    motor_flag = Serial.read();
  }
  
  if (motor_flag == '1'){  // case1 모터 동작  
  
  myservo1.attach(8);
  myservo2.attach(9);
  
  for (pos1 = 90; pos1 < 180; pos1 += 1) 
  { 
    myservo1.write(pos1);
    myservo2.write(180-pos1);            
    delay(10);                      
  }
  delay(1000);
  for (pos1 = 179; pos1 >= 90; pos1 -= 1) 
  { 
    myservo1.write(pos1);
    myservo2.write(180-pos1);            
    delay(10);                      
  }  
  myservo1.detach();
  myservo2.detach();
  motor_flag = '0';
  flag =3;
  }

  if (motor_flag == '2'){ // case2 모터 동작  
     
  myservo1.attach(8);
  myservo2.attach(9);
  
  for (pos1 = 90; pos1 > 0; pos1 -= 1) 
  { 
    myservo1.write(pos1);
    myservo2.write(180-pos1);            
    delay(10);                      
  }
  delay(1000);
  for (pos1 = 1; pos1 <= 90; pos1 += 1) 
  { 
    myservo1.write(pos1);
    myservo2.write(180-pos1);            
    delay(10);                      
  }
  myservo1.detach();
  myservo2.detach();
  motor_flag = '0';
  flag =3;
  }
  
  }

}

PC(Python)

import cv2
import tensorflow.keras
import numpy as np
import serial
import time

capture = cv2.VideoCapture(0)
ans = ['non_label', 'label']

ser = serial.Serial('COM4', 9600)
# Disable scientific notation for clarity
np.set_printoptions(suppress=True)

# Load the model
model = tensorflow.keras.models.load_model('keras_model.h5')
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
size = (224, 224)
img_num = 1

while True:
    if ser.readable():
        val = ser.readline()
        flag = int(val.decode()[:len(val) - 1])
        
        if flag == 1:

            print("Trash detected")
            ret, frame = capture.read()
            print("캡쳐")
            cv2.imwrite("temp.jpg", frame)
            image = cv2.resize(frame, dsize=size, interpolation=cv2.INTER_AREA)
            # Normalize the image
            normalized_image_array = (image.astype(np.float32) / 127.0) - 1
            # Load the image into the array
            data[0] = normalized_image_array
            # run the inference
            prediction = model.predict(data)
            index = np.argmax(prediction)
            print(prediction)
            cv2.imwrite(str(ans[index]) + str(img_num) + ".jpg", image)
            img_num = img_num + 1
            
            if index == 0:
                send = '2'
                send = send.encode('utf-8')
                ser.write(send)
                print(ans[index])
                time.sleep(0.5)

            elif index == 1:
                send = '1'
                send = send.encode('utf-8')
                ser.write(send)
                print(ans[index])
                time.sleep(0.5)
            flag = 0



capture.release()
cv2.destroyAllWindows()

AI

2가지를 분류하는 Classification은 사실 어떤 모델에 학습시키냐보다 양, 질의 학습 데이터가 중요하다고 생각합니다.

쓰레기 주워 왔다가 며칠 몸이 가려웠습니다.. 흑흑..

먼저 몸체랑 SW를 만들고 웹캠 모듈로 촬영한 사진을 학습 데이터로 사용했습니다.

 

CNN 모델 구현 및 학습 방법을 모르시면 아래 사이트에서 간단하게 모델을 만드실 수 있습니다.

https://teachablemachine.withgoogle.com/train/image

 

Teachable Machine

Train a computer to recognize your own images, sounds, & poses. A fast, easy way to create machine learning models for your sites, apps, and more – no expertise or coding required.

teachablemachine.withgoogle.com

 

아래와 같이 분류가 어려운 상황도 연출하여 좋은 학습 데이터를 추출해야 좋은 모델이 나옵니다. 

 

시연 영상