
https://github.com/minsu3007/SeperateWasteCollectionDevice
공대생들은 한번씩은 접한다는 '캡스톤디자인' 혹은 '졸업작품' 전 2개가 동시에 해당되었고
당시에는 작동을 우선으로 최선으로 프로그래밍했지만 몇개월 지나지도 않아
너무 부끄러운 코드라 리뷰를 할려고 합니다...
작품배경, 사용물품, 설명 등은 깃허브에 올렸으며
단순히 작품이 궁금하신 분들은 깃허브를 봐주시면 좋을거 같습니다.
😢시연시간이 너무 길어서 굉장히 아쉬웠던 작품입니다. ㅠ
import time
import glob
import serial
import numpy as np
import tensorflow as tf
이미지를 사용하기 위한 OpenCV
delay를 주기위한 time
젯슨나노와 아두이노 연결 확인 및 시리얼통신을 위한 glob과 serial
OpenCV를 통해 이미지를 numpy배열로 받아 전처리를 위한 numpy
딥러닝 모델을 불러와 예측하기 위한 Tensorflow
def findPort():
print("Find Port")
while True:
portList = glob.glob("/dev/ttyACM*")
print(portList)
if len(portList) > 2:
return portList
젯슨나노와 아두이노를 연결하면 터미널창에서는 ls /dev/ttyACM*을 통해 알 수 있듯이
파이썬 코드를 통해 아두이노와의 연결확인을 위한 함수
리스트 원소 하나당 아두이노 한개로 받았으며 3개를 연결했기 때문에 <<< 코드가 더러워진 가장 패착
리스트 요소가 2개 이상일 때 PortList를 반환
def findCamera():
print("finding Camera")
while True:
time.sleep(10)
cameraPort = glob.glob("/dev/video*")
if len(cameraPort) > 0:
print(cameraPort)
return cameraPort[0]
💡참고 : 젯슨나노 카메라모듈과 라즈베리파이용 카메라 모듈을 처음엔 사용했지만 젯슨나노 카메라모듈은 동작하지 않았고 라즈베리파이용 카메라는 CSI카메라 github를 참고했지만 어째선지 여러가지 라이브러리를 같이 쓸 경우 동작하지 않아 USB웹캠을 사용하였습니다.
위 findPort함수와 같이 카메라 포트를 찾는 함수
위와 코드가 유사하기 때문에 패스
def takePicture():
cap = cv2.VideoCapture(0)
_, img = cap.read()
cap.release()
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = img / 255
img = cv2.resize(img, dsize=(60, 60), interpolation=cv2.INTER_AREA)
return img.reshape(1, 60, 60, 3)
동작중인 웹캠의 사진을 찍어 전처리해주는 함수
리턴값은 (batch, width, height, channel)의 numpy array로 리턴합니다.
print("Loading Model")
model = tf.keras.models.load_model("/home/kangminsu/Desktop/capstone/model/model.h5")
portList = findPort()
LINEAR_PORT = portList[-1]
SUBO_PORT = portList[-3]
DISTANCE_PORT = portList[-2]
linear = serial.Serial(LINEAR_PORT, 9600)
print("LINEAR Arduino")
time.sleep(2)
subo = serial.Serial(SUBO_PORT, 9600)
print("SUBO Arduino")
time.sleep(2)
distance = serial.Serial(DISTANCE_PORT, 9600)
print("DISTANCE Arduino")
time.sleep(2)
우선 저장한 모델을 부르는데 텐서플로우를 import하는 과정과 모델을 불러오는 과정이 겹치다보니 체감시간이 엄청 오래걸려 시연하기 전 미리 세팅했습니다.
findPort()로 포트3개를 다 찾는데 여기서는 또 각각 아두이노 순서를 정해서 연결해야했던점 시리얼 연결이후에 바로 다른 포트와의 시리얼 연결이 에러가 떠 delay를 줬던점이 아쉬웠습니다.
cameraPort = findCamera()
cap = cv2.VideoCapture(0)
boolean, _ = cap.read()
if boolean:
print("Camera ON")
cap.release()
else:
print("Camera Error")
cap.release()
가끔씩 카메라 포트는 인식되지만 사진이 안찍히는 경우 특히 카메라 모듈을 사용했을 때 그런경우가 잦아 넣은 코드이며 여기서 아쉬운점은 else로 들어가 카메라에러가 나올경우 프로그램을 종료했었어야 하지 않았나 하는 아쉬움이 있습니다.
while True:
try:
distanceValue = int(distance.readline().decode())
except:
distaneValue = 20
print(distanceValue)
if distanceValue < 10:
time.sleep(1)
img = takePicture()
result = np.argmax(model.predict(img))
이 코드부터 너무 말도 안되는 짜집기 코딩을 한거같아 후회가 많고 코드리뷰를 쓰게 되었습니다.
우선 아두이노의 자외선센서의 값을 받는데 이건 굳이 아두이노를 사용하지않고 젯슨나노의 GPIO를 사용해도 되지않았나 생각도 들고, 가끔 값을 디코딩을 할 경우 문자열이 뜨는 경우가 있어 어거지로 예외처리문을 써서 지속적으로 실행하게 만들었는데 너무 부끄럽다 😅
이 후 충분히 가까운거리(=즉 페트병이 올라왔다)가 되었다면 사진을 찍고 딥러닝 모델을 통해 예측해 결과값을 확인했다. 여기서도 delay를 주었는데 카메라가 실시간으로 바로 보여주는게 아니라 약간의 딜레이와 페트병을 올리는 순간 손이 같이 찍히거나 페트병이 움직여 사진이 잘 안찍히는 경우가 있어서 delay를 주었다.
if result == 0:
print("non-trans")
try:
subo.write(str.encode("1"))
except:
portList = findPort()
time.sleep(3)
subo = serial.Serial(portList[0])
time.sleep(3)
subo.write(str.encode("1"))
time.sleep(5)
count = 0
for x in range(6):
try:
distanceValue = int(distance.readline().decode())
except:
distaneValue = 20
print(f"{count}delay{distanceValue}")
count += 1
결과값 0 즉 불투명페트병이면 서보모터만 동작하는 함수이고 아두이노 기판의 문제 혹은 연결 선의 문제인지 가끔씩 인식이 끊기고 바로 연결되는 경우가 있어 또 한번 예외처리문을 썻고 여기서도 delay를 많이준게 매우 아쉽지만 충분한 텀을 주지 않으면 연결은 되지만 서보모터가 동작하지 않았기때문에 delay를 줄 수 밖에 없었다.
이후 또 하나의 문제가 동작 이후에 N번정도 적외선센서 거리측정값을 받아내는데
동작하는 와중에 적외선 센서가 계속 작동 되어 데이터가 쌓이는 현상이 발생해서 걸러내는 작업으로 구현했다.
else:
print("trans")
linear.write(str.encode("1"))
time.sleep(50)
try:
subo.write(str.encode("0"))
except:
portList = findPort()
time.sleep(3)
subo = serial.Serial(portList[0])
time.sleep(3)
subo.write(str.encode("0"))
count = 0
for x in range(13):
try:
distanceValue = int(distance.readline().decode())
except:
distaneValue = 20
print(f"{count}delay{distanceValue}")
count += 1
이 블록은 투명페트병일 경우인데 페트병을 찌그려줘야할 리니어모터가 왔다갔다하는 속도가 최대한으로 빠르게해도 46초였고 배터리에 따라 2~4초 차이가 있어 50초를 줬고 이후 위 코드와 같은 서보모터 그리고 동작시간이 긴 만큼 적외선센서 값을 더 오래 받았다.
코드리뷰를 쓰면서 작품에대한 아쉬움, 코드에 대한 아쉬움도 있었는데 글을 쓰면서 글에대한 아쉬움이 생기네요...
subo.write(str.encode("1")) 시리얼로 값을 보내는 건가요?