졸작 코드 리뷰

강민수·2021년 7월 2일

코드리뷰

목록 보기
1/1
post-thumbnail

페트병자동분리수거장치

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초를 줬고 이후 위 코드와 같은 서보모터 그리고 동작시간이 긴 만큼 적외선센서 값을 더 오래 받았다.


아쉬운점

  • 우선 글을 적는데 있어 문체가 바뀌고 있는데 이 부분에 대해서 굉장히 아쉽습니다.
  • SSAFY랑 마이다스아이티 (떨어졌지만 😔)지원하면서 시간이 부족해 하드웨어나 디자인을 조금 더 신경을 못 쓴거 특히 하드웨어 쪽에 시간을 조금 더 투자했더라면 이런 코드가 덜 나왔을거 같다는 생각이 드네요
  • 초기 아이디어는 YOLO와 같은 객체인식 모델을 사용해 여러가지를 한꺼번에 분류하고 싶었지만 리니어모터, 모터모듈, 젯슨나노, 카메라 등을 사는데 너무 많은 돈을 써 크게 제작하기도 힘들었을 뿐더러 만약 크게 제작했더라도 내가 YOLO를 사용해 할 수 있을까 젯슨나노가 버텨줬을까라는 의문도 듭니다.
  • 단순 이진분류모델을 사용하고자 결심했을 때에는 VGG-16을 쓸려고 했지만 학습시간이 너무 오래걸려 간단하게 모델을 줄였고 이 후 600X400의 이미지 입력을 데이터로 넣었지만 예측시간이 너무 오래걸린다는 점 2~3분만 지나면 젯슨나노가 멈추는게 아쉽습니다.
  • 연산량을 줄이기 위해 60X60으로 줄이다보니 단순히 색으로 투명과 불투명 페트병을 분류하는 상황에 도달했으며 하얀색 불투명 페트병의 경우 페트병을 놓는 판 색깔과 겹쳐 투명으로 인식하는 점, 총 4개정도의 페트병 데이터만으로 학습시켰기 때문에 그 이외의 페트병은 정확도가 떨어진다는 점이 아쉽습니다. 굳이 딥러닝을 사용할 필요도 없었던거 같기도 하고...

코드리뷰를 쓰면서 작품에대한 아쉬움, 코드에 대한 아쉬움도 있었는데 글을 쓰면서 글에대한 아쉬움이 생기네요...

profile
발전, 흥미, 개발

2개의 댓글

comment-user-thumbnail
2021년 8월 10일

subo.write(str.encode("1")) 시리얼로 값을 보내는 건가요?

1개의 답글