.. Cover Letter

ㅇ 프로젝트/TEAM_스마트 팩토리

5. 컨베이어 벨트와 합치는 중

BrainKimDu 2022. 12. 30. 11:01

이렇게 해서 카메라를 위에 올리자..


완성..
디자인은 아직 설계단계니까..



컨베이어 벨트 코드
이걸 굴리면서
적절한 값을 찾아야겠다.

bool is_start = 0;
void setup() {
  // put your setup code here, to run once:
  pinMode(4, 1);
  pinMode(5, 1);
}
void loop() {
  if (is_start == 0) {
    digitalWrite(4, 1); // 5V : +
    digitalWrite(5, 0); // GND (0V) :-
    analogWrite(3, 255);
    delay(50);
    is_start = 1;
  }
  // put your main code here, to run repeatedly:
  digitalWrite(4, 1); // 5V : +
  digitalWrite(5, 0); // GND (0V) :-
  analogWrite(3, 255/4);
}

이건 적절한 파라미터 값을 찾기위해서 하는 것

import sys
import numpy as np
import cv2

#import serial
import time

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("열리지 않아요")
    sys.exit()

#ser = serial.Serial('/dev/ttyACM0', 9600)
count = 0
de_count = 0 
radius_list = []

while True:
    ret, frame = cap.read()
    if not ret:
        break

    #src = cv2.resize(frame, dsize=(900, 959))
    # 이미지의 사이즈를 조절한다.

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
    # 이미지의 잡음을 제거한다.


    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=40, minRadius=10, maxRadius=80)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    dst = frame.copy()
    # 이미지를 복사해서 dst에 저장한다.

    cv2.imshow('img', frame)
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        #for i in range(circles.shape[1]):
        # 검출된 원의 개수만큼 돌아서 원을 그린다.
        cx, cy, radius = circles[0][0]
        cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
        cv2.putText(dst, str(radius), org=(int(cx), int(cy)), 
        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
        radius_list.append(radius)
        time.sleep(0.25)
    
    else:
       de_count += 1
        if de_count == 5:
            if len(radius_list) > 1:
                print(radius_list) 
                radius_list = []
                de_count = 0
        
    
    

    cv2.imshow('img', dst)
    key = cv2.waitKey(1)

    if key == 27:
        break
    


cap.release()
cv2.destroyAllWindows()



0.1초마다 원을 디텍션
결과값을 복사해서

[21.6, 22.3, 15.0, 36.1, 21.0, 36.0, 65.1, 68.3, 35.9, 36.0, 20.5, 66.2, 70.5, 21.0, 27.5, 29.4, 24.1, 24.1, 25.4, 26.6, 26.7, 27.7, 28.5, 26.6, 28.5, 28.9, 27.4, 26.4, 26.4, 28.0, 27.8, 28.9, 28.9, 27.8, 28.0, 28.4, 28.4, 28.9, 28.8, 28.2, 29.9, 30.2, 30.5, 30.1, 28.9]
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
list = [21.6, 22.3, 15.0, 36.1, 21.0, 36.0, 65.1, 68.3, 35.9, 36.0, 20.5, 66.2, 70.5, 21.0, 27.5, 29.4, 24.1, 24.1, 25.4, 26.6, 26.7, 27.7, 28.5, 26.6, 28.5, 28.9, 27.4, 26.4, 26.4, 28.0, 27.8, 28.9, 28.9, 27.8, 28.0, 28.4, 28.4, 28.9, 28.8, 28.2, 29.9, 30.2, 30.5, 30.1, 28.9]
print(len(list))
print(np.average(list))


시각화까지 진행하자.

plt.figure(figsize=(12, 6))
sns.boxplot(x=list, orient='h')

박스안에 들어가는 값이 진짜 값이다.

그래서 원을 어떻게 접근해야할까
평균으로 접근하면 위험성이 생긴다.

박스안에 들어가는 경우 카운트를 하는거로 구현하는게 맞지 않을까?

일단 원을 디텍션하는 시간을 0.25초로 줄여보고 진행해보자.

[23.2, 28.6, 28.9, 26.1, 26.6, 28.8, 27.4, 25.8, 27.5, 25.7, 26.7, 29.2, 28.8, 28.9, 29.7, 29.7]

이를 시각화해보자.

list = [23.2, 28.6, 28.9, 26.1, 26.6, 28.8, 27.4, 25.8, 27.5, 25.7, 26.7, 29.2, 28.8, 28.9, 29.7, 29.7]
print(len(list))
print(np.average(list))


plt.figure(figsize=(12, 6))
sns.boxplot(x=list, orient='h')

일단 평균은 박스플롯안에 들어간다.
0.25초로 줄였을때 이상치가 상당히 많이 줄어드는 것으로 보인다.


그러면 0.25초로 확정하기 위해서 5번 정도 측정을 해보고 boxplot 을 그려본다.
총 9번의 측정결과

[23.0, 26.6, 25.7, 29.9, 24.0, 24.3, 25.8, 26.6, 26.8, 29.6, 28.4, 29.7, 28.8, 29.9]
[29.1, 28.8, 27.9, 25.4, 24.6, 25.2, 28.6, 30.8, 26.6, 26.7, 26.3, 26.7, 28.4, 30.2, 29.6, 29.3, 28.6]
[24.9, 26.6, 28.6, 24.0, 27.1, 27.1, 26.3, 26.3, 30.2, 29.1, 29.0, 29.9, 28.4]
[27.4, 26.3, 28.6, 25.0, 29.3, 27.4, 25.8, 25.2, 25.7, 25.7, 27.4, 28.6, 29.3, 29.0, 28.8, 29.1]
[25.4, 25.2, 24.3, 26.6, 24.5, 25.7, 25.2, 26.6, 30.2, 29.0, 29.9, 31.4, 30.8]
[24.6, 25.2, 25.4, 24.6, 24.6, 25.0, 25.2, 26.0, 26.4, 27.0, 29.3, 28.9, 29.6, 29.7, 30.8, 29.4]
[26.3, 27.4, 24.1, 25.0, 26.6, 26.4, 26.4, 26.8, 28.5, 29.9, 29.6, 28.0]
[24.3, 24.5, 25.8, 25.2, 24.6, 28.8, 29.1, 27.8, 27.7, 29.1, 28.8, 28.6, 28.0]
[28.9, 25.2, 25.4, 22.3, 24.6, 25.4, 25.2, 25.8, 26.4, 26.3, 27.1, 26.7, 28.2, 28.4, 28.8, 28.8, 30.5]

쥬피터에 넣고 돌리자

list = [[23.0, 26.6, 25.7, 29.9, 24.0, 24.3, 25.8, 26.6, 26.8, 29.6, 28.4, 29.7, 28.8, 29.9],
[29.1, 28.8, 27.9, 25.4, 24.6, 25.2, 28.6, 30.8, 26.6, 26.7, 26.3, 26.7, 28.4, 30.2, 29.6, 29.3, 28.6],
[24.9, 26.6, 28.6, 24.0, 27.1, 27.1, 26.3, 26.3, 30.2, 29.1, 29.0, 29.9, 28.4],
[27.4, 26.3, 28.6, 25.0, 29.3, 27.4, 25.8, 25.2, 25.7, 25.7, 27.4, 28.6, 29.3, 29.0, 28.8, 29.1],
[25.4, 25.2, 24.3, 26.6, 24.5, 25.7, 25.2, 26.6, 30.2, 29.0, 29.9, 31.4, 30.8],
[24.6, 25.2, 25.4, 24.6, 24.6, 25.0, 25.2, 26.0, 26.4, 27.0, 29.3, 28.9, 29.6, 29.7, 30.8, 29.4],
[26.3, 27.4, 24.1, 25.0, 26.6, 26.4, 26.4, 26.8, 28.5, 29.9, 29.6, 28.0],
[24.3, 24.5, 25.8, 25.2, 24.6, 28.8, 29.1, 27.8, 27.7, 29.1, 28.8, 28.6, 28.0],
[28.9, 25.2, 25.4, 22.3, 24.6, 25.4, 25.2, 25.8, 26.4, 26.3, 27.1, 26.7, 28.2, 28.4, 28.8, 28.8, 30.5]],

for i in list[0]:
    print("길이 : " + str(len(i)) + "평균 : " + str(np.average(i)))
    print("boxplot 시작점 : " + str(np.percentile(i, 25)) + "boxplot 끝점 : " + str(np.percentile(i, 75)))
    print("------------")

그러면
불량 판단기준을 25 ~29.5로 잡고
원이 10개가 검출되었을때의 평균이
25~29.5 범위안에 들어왔다면 정상으로 판단하고
그 외라면 불량으로 판단하자

그러면 코드를 잠시 수정해보자.


직접 컨베이어 벨트를 돌려보면서 측정한 결과

약간의 수정이 필요했다.

25~29.5 범위로 할 경우
컨베이어 속도가 일정하지 않아서 정상을 불량으로 판단하는 경우가 생겼다.
그래서
25~31범위로 설정했다.

그리고 화면상에 정상, 비정상여부와 반지름을 출력하게 만들었다.

import sys
import numpy as np
import cv2
import time

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("열리지 않아요")
    sys.exit()

#ser = serial.Serial('/dev/ttyACM0', 9600)
count = 0
de_count = 0 
radius_list = []

munjang = "not detect"
R = 255
G = 255
B = 255

while True:
    ret, frame = cap.read()
    if not ret:
        break

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
    # 이미지의 잡음을 제거한다.


    # 실질적인 허프변환이 시작되는 부분
    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50, param1=120, param2=40, minRadius=10, maxRadius=80)
    # 반지름과 threshold를 조절하면서 확인해볼 분이다.

    
    dst = frame.copy()
    # 이미지를 복사해서 dst에 저장한다.
    
    cv2.imshow('img', frame)
    
    # 원을 검출할 때 실행된다.
    if circles is not None:
        cv2.putText(dst, munjang, org=(0, 30), 
        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
        color=(R,G,B),thickness=1, lineType=cv2.LINE_AA)
        cx, cy, radius = circles[0][0]
        cv2.circle(dst, (int(cx), int(cy)), int(radius), (255, 0, 0), 2, cv2.LINE_AA)
        cv2.putText(dst, str(radius), org=(int(cx), int(cy)), 
        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
        color=(0,0,255),thickness=3, lineType=cv2.LINE_AA)
        radius_list.append(radius)
        count += 1
        print(count)
        
        if count == 5:
            print("디텍션 평균은 : " + str(np.average(radius_list)))
            if np.average(radius_list) >= 25 and np.average(radius_list) <= 31:
                munjang = "pass  (" + str(np.average(radius_list)) + ")"
                R = 255
                G = 0
                B = 255
            else:
                munjang = "fail  (" + str(np.average(radius_list)) + ")"
                R = 0
                G = 0
                B = 255
            radius_list.clear()
            count = 0   
        de_count = 0

        time.sleep(0.25)
    
    else:
        cv2.putText(dst, munjang, org=(0, 30), 
        fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, 
        color=(R,G,B),thickness=1, lineType=cv2.LINE_AA)
        
        de_count += 1
        if de_count == 5:
            munjang = "not detect"
            R = 255
            G = 255
            B = 255
            de_count = 0
            count = 0
        time.sleep(0.25)
    

    cv2.imshow('img', dst)
    key = cv2.waitKey(1)

    if key == 27:
        break
    


cap.release()
cv2.destroyAllWindows()




이걸로 측정을 하는 경우 이런식으로 표현된다.