본문 바로가기
Project

[Project] Vision Al 기반 컨베이어 벨트 객체 인식 시스템 구축 및 딥러닝 모델 최적화 프로젝트(1)

by ykr0919 2025. 3. 5.

 

 

 

Vision Al 기반 컨베이어 벨트 객체 인식 시스템 구축 및 딥러닝 모델 최적화 프로젝트를 진행해 보았음.

 

컨베이어 벨트에서 이동되는 PCB 제품에 대해서 외관 검사하고, 모델을 개선하는 것이 목적임! 

 

학습데이터와 실시간 객체 인식 시스템을 구축할 때, 데이터 질의 중요성을 인식하여 모델을 개선하여 객체 인식의 정확도를 높여가는 과정을 적어보았음.

 

 

프로젝트 인원 

  • 4명

프로젝트 기간 

  • 2024.11.07 ~  2024. 11.11

프로젝트 Stack & Tool

  • Python
  • OpenCV
  • Superb AI : 데이터 라벨링 및 AI 모델 학습 지원 플랫폼

프로젝트 환경 

 

객체 인식 과정은 아래와 같음!🔽

 

기본 코드 

# 필요한 라이브러리 가져오기
import time
import serial  # 아두이노와 같은 장치와의 시리얼 통신을 위한 라이브러리
import requests  # HTTP 요청을 보내기 위한 라이브러리
import numpy  # 이미지 처리를 위한 배열 조작 라이브러리
from io import BytesIO  # 바이트 스트림을 다루기 위한 라이브러리
from pprint import pprint  # 보기 좋게 데이터를 출력하기 위한 라이브러리
import cv2  # OpenCV, 이미지 캡처 및 처리 라이브러리

# 시리얼 통신 설정 ("/dev/ttyACM0" 포트에서 9600 속도로 통신)
ser = serial.Serial("/dev/ttyACM0", 9600)

# API 엔드포인트 URL 설정 (실제 URL로 변경 필요)
api_url = ""


# USB 카메라에서 이미지 캡처 함수
def get_img():
    """USB 카메라로부터 이미지 캡처

    Returns:
        numpy.array: 이미지의 numpy 배열
    """
    # 카메라 장치 열기 (0번 장치, 일반적으로 첫 번째 카메라)
    cam = cv2.VideoCapture(0)

    # 카메라가 열리지 않으면 오류 메시지를 출력하고 종료
    if not cam.isOpened():
        print("카메라 오류")
        exit(-1)

    # 카메라에서 이미지 읽기
    ret, img = cam.read()
    # 카메라 리소스 해제
    cam.release()

    # 이미지 반환
    return img


# 이미지 자르기 함수
def crop_img(img, size_dict):
    # 자를 위치와 크기 지정
    x = size_dict["x"]
    y = size_dict["y"]
    w = size_dict["width"]
    h = size_dict["height"]
    # 이미지 자르기 (y:y+h, x:x+w)
    img = img[y : y + h, x : x + w]
    return img


# 추론 요청 함수
def inference_reqeust(img: numpy.array, api_rul: str):
    """이미지를 API 엔드포인트로 전송하여 추론 요청

    Args:
        img (numpy.array): 이미지의 numpy 배열
        api_rul (str): API URL. 추론 엔드포인트
    """
    # 이미지를 JPEG 형식으로 인코딩
    _, img_encoded = cv2.imencode(".jpg", img)

    # 바이트 스트림 준비
    img_bytes = BytesIO(img_encoded.tobytes())

    # 전송할 파일 설정
    files = {"file": ("image.jpg", img_bytes, "image/jpeg")}

    print(files)  # 전송할 파일 구조 출력 (디버깅용)

    try:
        # API로 이미지 전송
        response = requests.post(api_url, files=files)
        if response.status_code == 200:
            # 성공 시 JSON 응답 출력 및 반환
            pprint(response.json())
            return response.json()
            print("이미지 전송 성공")
        else:
            # 실패 시 상태 코드 출력
            print(f"이미지 전송 실패. 상태 코드: {response.status_code}")
    except requests.exceptions.RequestException as e:
        # 요청 예외 발생 시 오류 메시지 출력
        print(f"요청 오류 발생: {e}")


# 메인 루프
while 1:
    # 컨베이어 상태 확인 -> 0이면 멈춤
    data = ser.read()
    print(data)
    if data == b"0":  # 컨베이어가 멈췄다면
        # 이미지 캡처
        img = get_img()
        
        # 자르기 정보 설정 (원하는 좌표와 크기)
        crop_info = {"x": 280, "y": 180, "width": 230, "height": 260}

        # 자르기 정보가 있으면 이미지 자르기
        if crop_info is not None:
            img = crop_img(img, crop_info)

        # 자른 이미지 화면에 표시
        cv2.imshow("", img)
        cv2.waitKey(1)  # 잠깐 대기 (1ms)

        # 추론 요청 함수 호출 및 결과 저장
        result = inference_reqeust(img, api_url)

        # 컨베이어 다시 동작하도록 신호 전송
        ser.write(b"1")
    else:
        pass  # 데이터가 0이 아니면 아무 동작도 하지 않음

 

 

우선 처음 주어진 학습 데이터를 보자 🔽 

 

육안으로 보아도 데이터 질이 좋지 않아 학습에 어려움이 있을 거 같다는 예상이 들었음.

 

일단 주어진 이미지로 학습을 진행.

 

1차 학습

 

SuperbAI 플랫폼에서 라벨링과 학습을 진행하였음! 

 

PCB의 "HOLE" - 4개 ,  "RASPBERRY PICO" - 1개, "OSCILLATOR" - 1개,  "CHIPSET"- 1개, "BOOTSEL" - 1개, "USB"- 1개를 라벨링.

 

학습 모델은 YOLO N을 사용하였음.

 

이번 프로젝트에서는 컨베이어 벨트가 움직이는 중에 객체를 인식해야 하는데, YOLO는 속도와 효율성에 중점을 둔 모델이기 때문에 이러한 환경에서 객체를 빠르고 정확하게 감지하는 데 적합.

특히 YOLO N 모델을 사용하였는데, 그 이유는 YOLO N 모델이 YOLO 계열 중에서도 경량화와 속도 최적화에 가장 적합하기 때문. YOLO N은 YOLOv5, YOLOv6 같은 모델의 기본 아키텍처를 기반으로 하되, 모델 크기를 줄여 연산 효율을 높인 버전. 따라서, 컨베이어 벨트에서 빠르게 움직이는 객체를 실시간으로 감지해야 하는 이번 프로젝트에서 속도와 자원 효율성 면에서 큰 장점을 가짐.

또한 YOLO N 모델은 경량화되어 적은 메모리로도 높은 정확도를 유지할 수 있기 때문에, 리소스가 제한된 시스템에서도 성능을 안정적으로 발휘할 수 있음. 이러한 특징들 덕분에, 우리 팀은 프로젝트의 요구 사항에 가장 적합한 YOLO N 모델을 선택.

 

학습된 모델을 사용해서 잘 인식하는지 테스트 보자! 

 

 

위 이미지는 컨베이어 벨트에서 실제로 촬영한 이미지를 서버로 전송하여 추론을 요청한 결과임.

 

"RASPBERRY PICO"(노란색) "OSCILLATOR"(검은색),  "CHIPSET"(하늘색) , "USB"(빨간색)은 잘 잡는 모습이 보이지만, "HOLE"(연두색)은 4개를 잡아야 하는데 HOLE이 아닌 곳을 잡기도 하고 못 잡기도 하는 모습을 보임. "BOOTSEL"(핑크색)은 한 개만 존재하지만 2개가 있다고 인식하고 있음.

 

위 예시는 2가지뿐이지만 테스트 이미지에서 PCB의 선명도와 위치에 따라 추론하는 게 달라졌음! 

 

문제점

  • “HOLE”을 너무 많이 잡거나, 못 잡는 경우가 무수히 많았음
  • “BOOTSEL”을 중복으로 잡는 경우도 존재
  • “RASPBERRY PICO”를 이중으로 잡는 문제(예시 이미지에는 없음)
  • “OSCILLATOR”를 다른 부분으로 착각(예시 이미지에는 없음)

 

정확도는 2.5% (1/40) 정도였음.

모델의 정확도

Vision AI 추론 결과와 실제 결과를 비교해서 모델의 성능을 측정.

정확도 (True Positive): 모델이 전체 데이터에서 얼마나 정확하게 예측했는지 보여주는 지표

특히, 제조 분야에서는
과검 (False Positive, 1종 오류): 제품이 실제로는 정상인데 품질검사 결과는 불량이라고 예측
미검 (False Negative, 2종 오류): 제품이 실제로는 불량인데 품질검사 결과는 정상이라고 예측
과거과 미검은 Trade-off 관계
주로 제조에서는 미검을 0%로 만들면서 과거를 최대한 줄이는 방향을 원함

 

위의 문제점을 데이터 라벨링의 일관성 문제라고 파악!

 

모델을 학습시킬 때 팀원 4명이 나누어서 라벨링을 진행하다 보니, 개인의 기준에 따라 라벨링이 진행이 되어서 라벨링 일관성이 떨어서 정확도가 떨어진다고 생각!

 

첫 번째 개선 방안 

  • 라벨링 가이드라인을 작성하고 이를 팀원들과 공유하여 일관성을 유지.
  • 분류기준을 명확히 하여 새로운 endpoint를 생성하고, 이를 기반으로 재라벨링하여 모델의 정확도를 향상.
endpoint는 모델의 예측을 더 정확하게 하기 위한 새로운 기준이나 지점을 설정하는 것

 

 

2차 학습 

첫 번째 개선 후 새롭게 모델 학습

 

 

"HOLE"을 제외하고는 다른 부분은 잘 인식하는 모습! 

 

1차 학습에 비해 개선된 모습을 확인할 수 있었음.

 

라벨링의 일관성을 유지한 덕분에 어느 정도 성과가 있었음. 그러나 여전히 "HOLE" 부분은 잘 인식되지 않아 정확도는 크게 변하지 않은 상황. 기본적으로 PCB의 모든 부품을 인식해야만 양품과 불량을 구분할 수 있기 때문에, 아직 더 많은 개선이 필요.

 

주어진 학습 데이터의 품질이 높지 않아(어두운 환경, 흐릿한 이미지 등) 학습이 잘 이루어지지 않았다고 생각.

 

특히, 컨베이어 벨트 카메라에서 PCB를 인식할 때 이미지의 밝기, PCB에 생긴 그림자, 그리고 칩 이미지의 선명도가 낮아 각 부품의 특징을 잘 인식하지 못하는 문제 발견.

 
따라서, 실제 구동될 컨베이어 벨트 환경을 개선하여 질 좋은 학습 데이터를 확보하고, 컨베이어 벨트 위에서 실시간으로 이미지를 선명하게 촬영할 수 있도록 하는 방법을 모색.

 

두 번째 개선 방안

  • 학습 데이터 질 개선(PCB에 생기는 그림자, 이미지 밝기, 선명도)
  • 컨베이어 환경 개선(추론에 사용될 이미지의 질을 높이기 위한 환경 설정)

 

결론적으로, 컨베이어 환경을 개선하여 실제 추론 환경과 같은 장소에서 새로운 학습 데이터를 수집하고, 이를 바탕으로 학습된 모델을 실제 환경에서 사용할 수 있도록 하는 방향으로 진행. 이로 인해 정확도를 높일 수 있을 것으로 기대.

 

 

어떻게 컨베이어 환경을 개선했는지 다음 글에서 이어서 가도록 하겠음!!!