[68호]투명 폐페트병 분리배출 로봇
2021 ICT 융합 프로젝트 공모전 우수상
투명 폐페트병 분리배출 로봇
글 | 숭실대학교 오세찬, 강전완, 윤형섭, 임소현
1. 심사평
칩센 작품을 개발 과정과 결과에도 나와있듯이 개발자들의 정형화된 시연 환경 내에서는 만족할만한 결과를 도출해 낼 수 있지만, 실제 개발 당사자가 아닌 이들이 사용을 한다면 수많은 문제점이 발생할 수 있을 것으로 보입니다. 보고서에 포함된 후속 연구를 통해 작품의 개선을 할 수 있기를 바랍니다.
펌테크 자원재활용 이슈와 기술이 결합된 환경친화적인 작품이라고 생각합니다. 사전조사 및 아이디어가 반영된 제품 기획의 꼼꼼함이 돋보이며 영상처리 기술 구현 등을 효율적으로 접목하여 군더더기 없이 꼭 필요한 기능 위주로 목적에 맞는 최적의 시스템을 기획의도에 맞게 안정적으로 구현하였다고 생각합니다. 전체적으로 기술 구현도, 완성도 등에서 상당히 뛰어나고 훌륭한 작품으로 생각됩니다.
위드로봇 처리 속도가 아쉬습니다. 나머지는 훌륭한 작품입니다.
뉴티씨 투명 페트병만을 수거할 수 있도록하여, 투명하지 않은 페트병은 투명하게 만들어 투입하도록 하는 시스템인데, 매우 잘 제작되었습니다. 다만, 플라스틱이 아닌 금속캔 등을 투입하게 되면, 수거되지 않도록 안내하는 것도 추가하면 좋겠습니다. 검출 및 판별 속도는 개선해야 하는 점으로 보입니다. 또한, 수거되지 않는 것은 다시 가져갈 수 있도록 안내와 함께 기구적으로 문이 열리도록 하면 더 좋을 것 같습니다.
엔티렉스 부설연구소 투명 플라스틱 병 분리 배출로봇 개발은 온전한 플라스틱이 아니면 힘든 것 같습니다. 다만 카메라를 이용하여 페트병을 인식하여 분리 배출하는 기능에 대해서는 많은 생각을 하여 개발 한 것 같습니다. 패트병이 아닌 다른 물체가 왔을 때 일반쓰레기로 처리하는 방식을 적용했으면 합니다.
2. 작품 개요
2.1. 배경 및 목적
환경부에서는 2020년 12월 25일부터 투명 페트병만 분리수거 해 배출하는 정책을 펴고 있다. 하지만 기사를 통해 전용 봉투는 있지만, 그 안에 라벨과 뚜껑이 제거되지 않은 채로 페트병이 버려져 있는 사진이 공개되면서 아직 제대로 된 분리수거가 이루어지지 않음을 알 수 있다. 이러한 문제를 해결하고자 뚜껑과 라벨이 없는 무색의 플라스틱만을 모아서 재활용 효율 상승에 큰 도움을 주는 로봇을 제작하였다.
2.2. 차별점 및 기대효과
재활용 문제를 해결하기 위한 시스템은 여러 가지가 있다. 촉각을 이용해 자동으로 분리수거를 하는 ‘로사이클’, 물건을 넣으면 인공지능이 재활용 가능 여부를 판단해 버려주는 자판기 형태의 ‘네프론’, 어플을 통해 직접 사진을 찍으면 그 물건의 분리수거 방법을 안내하는 ‘스마트사이클’ 등이 바로 그것이다.
작품은 시중에 있는 다른 제품들과 크게 3가지 사항에서 차별성을 보인다. 첫 번째 차별점은 실용적인 면과 교육적인 면이 공존한다는 것이다. 투입구에 물건을 넣으면 로봇이 재활용 가능 여부를 판단하고, 재활용할 수 없다면 문제점과 해결방안을 디스플레이 화면과 음성안내 서비스를 통해 사용자에게 알려준다. 이를 통해 사람들은 본인의 재활용 인식 수준을 판단할 수 있고, 모르고 있었던 사항들을 알게 됨으로써 잘못된 행동을 개선할 수 있다. 두 번째는 로봇이 탈부착할 수 있다는 점이다. 이로 인해 공간의 큰 제약 없이 원하는 크기의 쓰레기통과 함께 사용할 수 있다. 이에 따라 기계를 열어 안에 있는 쓰레기들을 일일이 꺼내는 기존의 다른 제품들과 다르게, 봉투만 교체해서 사용하면 되므로 매우 편리하다. 세 번째는 제작 비용이 상대적으로 적다는 것이다. 값비싼 장치들과 센서들을 사용하지 않고도 필요한 기능들을 무리 없이 잘 구현해낼 수 있다.
투명 폐페트병 분리배출 로봇을 사용함으로써 다음과 같은 기대효과가 예상된다. 우선, 환경부가 마련한 투명 페트병 분리수거 정책의 실행률 증가와 정착에 큰 도움이 될 것이다. 투명 페트병 분리배출의 증가로 불필요한 플라스틱의 폐기가 방지되고, 재활용률이 증가하여 경제적, 환경적으로 큰 공익이 발생할 것이다. 또한, 로봇이 디스플레이와 음성으로 올바른 플라스틱 재활용 방법에 대하여 즉각적으로 홍보함으로써 사람들의 투명 플라스틱 분리수거 및 재활용에 대한 인식이 크게 발전될 것이다.
3. 작품 설명
3.1. 작품 구성도
3개의 디바이스는 ‘감각기관’, ‘동작 기관’, ‘뇌’로 역할이 분담되어 있다. 각 디바이스는 MQTT 통신을 통해 상호작용하며 역할에 맞는 기능을 수행한다.
(1) 감각기관(ESP8266)
센서(무게 센서, 초음파센서)가 연결되어 있다. State에 따라 필요한 센서값을 측정해 동작 기관과 라즈베리파이에 전달해준다.
(2) 동작기관(ESP8266)
동작 기관에는 서보모터가 연결되어 있다. 라즈베리파이에서 검출된 물체 명령에 따라 분리수거와 물체 반환을 하기 위한 모터작동을 수행한다.
(3) 뇌(라즈베리파이)
뇌에는 카메라, 모니터, 스피커가 연결되어 있다. 라즈베리파이에서 구성한 웹서버가 카메라로 찍은 사진을 분석하고 과정을 모니터(웹 클라이언트)와 스피커로 출력해준다.
3.2. 주요 동작 및 특징
3.2.1. 로봇 구동과 물체 진입 확인
감각기관에 해당하는 ESP8266에 설계되어있다. 물체 진입 확인은 5가지 state로 구성된다.
init state(00)
처음 감각기관에 전원이 공급되면 init 상태가 된다. 이 상태에서는 사용하는 센서(무게 센서, 초음파센서)들의 pin을 초기화하고, 와이파이와 MQTT client(sensor)를 연결한다. wait 상태로의 이동은 라즈베리파이의 명령으로 이뤄진다.
라즈베리파이의 웹서버는 서버가 실행되기 전 디바이스 간의 통신상태를 점검한다. 라즈베리파이가 보낸 ’reset’를 확인한 감각기관은 ‘connect’를 답장한다. 연결상태가 확인되면, 라즈베리파이는 ‘wait’를 보내고 서버를 실행한다. 메시지를 받은 감각기관은 ‘wait’ 상태로 이동한다.
server mqtt connect / 라즈베리파이
mqtt_client.on(‘connect’, function () {
mqtt_client.publish(‘sensor’, “reset”);
mqtt_client.publish(‘motor’, “reset”);
mqtt_client.subscribe(‘raspi’, function (err) {
if (!err) {
console.log(‘subscribe : raspi’);
}
})
})
웹서버가 MQTT client로 연결되면 raspi를 구독하고, 감각기관과 동작기관에 reset이라는 메시지를 전송한다.
server running / 라즈베리파이
var health = {
‘sensor’: 0,
‘motor’: 0
};
var server_start = setInterval(function () {
if (health.sensor == 1 && health.motor == 1) {
var server = http.createServer(app).listen(8080, function () {
console.log(‘server on’);
});
io = socketio(server);
io.sockets.on(‘connection’, function (socket) {
socket.on(‘sound’, function (data) {
console.log(data);
sound(data + ‘.mp3′);
})
});
clearInterval(server_start);
} else{
(health.sensor!=1)?mqtt_client.publish(‘sensor’,”reset”)
:mqtt_client.publish(‘motor’, “reset”);
console.log(“not connected”);
}
}, 1000)
health 변수에 key값인 sensor, motor는 서버 실행 전 감각기관, 동작기관의 통신상태 정보를 담고 있다. 1000(1초)마다 실행되는 함수를 만들어 연결상태를 점검하고 잘 연결되었다면, 웹서버와 동적인 사용자 인터페이스에 필요한 소켓 서버를 실행시키고, 감각기관에게 ‘wait’ 메시지를 보내준다. 연결상태가 올바르지 않다면, 해당 디바이스에 상태를 확인하는 reset 메시지를 재전송한다.
connect message receive / 라즈베리파이
mqtt_client.on(‘message’, function (topic, message) {
if (message == “input”) {
// 물체 진입코드
} else if (String(message).split(‘-’)[0] == ‘connect’) {
console.log(String(message));
health[String(message).split('-')[1]] = 1;
} else {
// 물체 무게 받는 코드
}
})
‘-‘를 기준으로 split 하여 감각기관, 동작기관 중 연결상태라 올바른 디바이스에 health 값을 1로 갱신하는 코드이다.
connect msg / 감각기관
void callback(char* topic, byte* payload, unsigned int length) {
String msg = “”;
for (int i = 0; i < length; i++) {
msg +=(char)payload[i];
}
if(msg == “reset”){
client.publish(“raspi”,”connect-sensor”);
}
else if(msg == “wait”){
state = 1;
}
}
wait state(01)
문이 열리길 기다리는 상태. 물체를 집어넣는 작품 상단에 있는 초음파센서가 문과의 거리를 측정한다. 센서값을 분석(문 열림)하여 문이 열렸음이 확인되면 ‘open’ 상태로 이동한다.
wait -> open state / 감각기관
int get_distance(){
int duration, distance;
digitalWrite(trig, LOW);
delayMicroseconds(10);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
duration = pulseIn(echo, HIGH);
distance = duration * 170 / 1000;
return distance;
}
// loop내 코드
int distance = get_distance();
if(state == 1 &&(200 < distance || 180 > distance)){
state++; // open state로 이동
}
거리를 측정하는 distance()를 전역함수로 작성하여 loop 내에서 필요에 따라 호출하도록 하였다. 문이 열릴 때 반사되는 초음파센서값이 열리기 전의 값(190)보다 값이 작다는 것을 이용하여 문이 열렸음을 확인한다. 추가로 200 이상의 값도 열렸다고 판단하는 이유는 초음파가 반사되어 200 이상의 숫자도 나올 수 있기 때문이다.
open state(10)
문이 열린 상태. 초음파센서는 문과의 거리를, 무게 센서는 물체의 무게를 측정한다. 두 센서값을 분석(문 닫힘, 물체무게감지)하여 정상적으로 물체가 들어왔으면 ‘close’ 상태로 이동한다.
close state(11)
물체가 들어온 상태. 라즈베리파이에게 물체가 들어왔음(input)과 물체의 무게(weight)값을 전송한다. 메시지를 수신한 라즈베리파이는 물체 분석을 시작하고 ‘analysis’ 답장을 보낸다. 답장을 받은 감각기관은 analysis 상태로 이동한다.
open -> close state / 감각기관
else if(state == 2 && (190 >= distance && 185 <= distance) && (scale.read()/1000 >= 330)){
state++; // close state로 이동
client.publish(“raspi”,”input”);
delay(1000);
String packet = String(scale.read()); // weight 전송
packet.toCharArray(weight,50);
client.publish(“raspi”,weight);
}
초음파센서로 측정한 값이 185 이상 190 이하이고 무게 센서로 측정한 값을 1000으로 나눈 값이 330 이상이면 물체가 정상적으로 들어왔다고 판단하여 ‘close’ 상태로 이동한다. close 상태로 이동 후 라즈베리파이에게 잘 들어왔음을 알리는 ‘input’과 물체의 무게 값인 ‘weight’ 값을 전송한다.
분석 시작코드 / 라즈베리파이
mqtt_client.on(‘message’, function (topic, message) {
if (message == “input”) {
mqtt_client.publish(‘sensor’, “analysis”);
io.sockets.emit(‘counter’, 3);
sound(“camera.mp3″);
} else if (String(message).split(‘-’)[0] == ‘connect’) {
// 초기 설정 관련
} else {
weight = parseInt(message);
}
})
analysis 상태(100)
물체가 분석 중인 상태. 분석이 정상적으로 끝나고, 모터 동작에 대한 메시지가 수신될 때까지 대기한다.
3.2.2. 투명 폐페트병 검출 기능
뇌에 해당하는 라즈베리파이에 투명 폐페트병을 검출하는 모델이 3단계로 설계되어있다. 아래 표는 대략적인 흐름과 각 검출 단계에서 사용되는 분석 도구들을 보여준다.
카메라 촬영
node.js의 pi-camera 모듈을 사용하여 라즈베리용 카메라를 조작한다. 사진 촬영 후 정상적으로 사진이 저장되면, 성분분석 단계로 넘어간다.
웹 클라이언트로부터 /camera 요청을 받으면, snap 함수를 사용해 라즈베리파이용 카메라를 동작시킨다. 사진이 제대로 찍혔으면, 첫 분석방법인 성분 분석 함수를 호출한다.
성분분석
google-cloud에서 제공해주는 vision api를 사용한다. 들어온 물체의 크기, object 무게 등을 측정하여 플라스틱인지 검출하는 단계이다.
ⓐ 크기 검사
이미지 분석 데이터를 분석하여 물체의 유무를 측정한다. 물체의 좌표에 해당하는 픽셀들을 추출하여 만약 추출되지 않았으면 물체가 인식되지 않았다고 판단하고 object_test 변수를 false로 변경한다.
ⓑ 오브젝트 검사
이미지 분석 데이터를 분석하여 해당하는 물체가 플라스틱인지 판단한다. [bottle, drink, plastic, glass]의 키워드가 하나도 나오지 않으면 페트병이 아니라고 인식해 object_test 변수를 false로 변경한다.
ⓒ 무게 검사
esp8266(감각기관)에서 물체 진입 확인과 함께 전달된 물체의 무게를 통해 플라스틱 bottle의 무게인지 검사한다. 이전검사에서 유리와 플라스틱을 구분하지 못했는데, 이 단계에서 분류된다. 추가로 이물질 특히 내용물이 담겨있는 경우도 이 검사에서 걸러진다.
ⓓ label_test 값 확인
ⓐ~ⓒ단계에서 기록된 object_test 값을 확인하여 false이면 물체를 반환하기 위해 모터에게 메시지(up)를 보내고, true이면 다음 단계인 글자 분석 단계로 이동한다.
성분 분석 코드 / 라즈베리파이(서버)
var weight = 0;
var area;
async function object_search() {
var object_test = false;
mqtt_client.publish(‘sensor’, “get_weight”);
const [result2] = await client.objectLocalization(‘test.jpg’);
try {
result2.localizedObjectAnnotations[0].boundingPoly.normalizedVertices;
const object =
result2.localizedObjectAnnotations[0].boundingPoly.normalizedVertices;
area = {
xs: parseInt(object[0].x * 640),
xe: parseInt(object[1].x * 640),
ys: parseInt(object[1].y * 480),
ye: parseInt(object[3].y * 480)
};
io.sockets.emit(‘area’, area);
} catch (e) {
console.log(“물체 감지X”);
}
const [result] = await client.labelDetection(‘test.jpg’);
const objects = result.labelAnnotations;
console.log(‘성분분석중’);
objects.forEach(function (objects, index) {
io.sockets.emit(‘ingredient_label’, objects.description);
if (objects.description.indexOf(‘bottle’) != -1 ||
objects.description.indexOf(‘Bottle’) != -1 ||
objects.description.indexOf(‘Drink’) != -1 ||
objects.description.indexOf(‘drink’) != -1 ||
objects.description.indexOf(‘Plastic’) != -1 ||
objects.description.indexOf(‘Glass’) != -1) {
object_test = true;
}
});
console.log(‘성분분석완료’);
if (object_test == true && weight < 420000) {
io.sockets.emit(‘complete’, ‘성분분석완료-성공-1′);
setTimeout(function () {
text_search(result);
}, 3000)
} else {
io.sockets.emit(‘complete’, ‘성분분석완료-실패-1′);
mqtt_client.publish(‘motor’, “up”);
}
}
글자 분석
google-cloud vision API를 사용한다. 폐페트병에 프린팅이 되어있거나 글자가 있는지 판단하는 단계이다.
글자 분석 코드 / 라즈베리파이(서버)
async function text_search() {
console.log(“글자분석중”);
var text_test = true;
const [result] = await client.textDetection(‘test.jpg’);
const detections = result.textAnnotations;
detections.forEach(function (text, index) {
io.sockets.emit(‘ingredient_text’, text.description);
if (text.description) {
text_test = false;
}
console.log(text.description);
});
console.log(“글자분석완료”);
if (text_test == true) {
io.sockets.emit(‘complete’, ‘글자분석완료-성공-2′);
setTimeout(function () {
color_search();
}, 3000)
} else {
io.sockets.emit(‘complete’, ‘글자분석완료-실패-2′);
mqtt_client.publish(‘motor’, “up”);
}
}
ⓐ 글자 검사
이미지 데이터 중 text 배열을 선택해 요소가 있는지 확인한다. 만약 요소가 하나라도 존재하면 label이 붙어있거나 프린팅이 되어있는 플라스틱으로 판단하고 text_test 변수를 false로 변경한다.
ⓑ text_test 값 확인
ⓐ단계에서 기록된 text_test 변수의 값을 확인하여 fasle이면 물체를 반환하기 위해 모터에게 메시지(up)를 보내고, true이면 다음 단계인 색깔 분석 단계로 이동한다.
글자 분석 결과를 나타내는 text_test 변수를 false로 초기화하고 분석 결과에 따라 true 값을 할당한다. vision api의 textDetection함수를 이용해 이미지에서의 식별된 글자들을 리턴받고, 이를 분석한다. 분석결과를 text_test 변수에 기록하고, 이를 토대로 모터를 작동시킨다.
색깔 분석
이미지를 RGB matrix로 만들어주는 image-to-rgba-matrix 모듈을 사용해 픽셀 단위로 RGB 값을 분석하여 폐페트병의 이물질 여부를 판단하는 단계이다.
ⓐ 영역설정
성분분석에서 얻은 물체 픽셀 데이터를 이용해 분석 영역을 설정한다. 영역을 설정함으로써 투명 폐페트병과 배경색만 검출된다.
ⓑ 표준편차
각 픽셀의 R, G, B값을 추출해 표준편차를 계산해 1차 분류를 진행한다. 표준편차가 15 초과면 유채색(빨강, 초록, 파랑), 15 이하면 무채색(검은, 흰)으로 분류한다.
ⓒ RGB 연산
1차 분류를 통해 나눠진 데이터를 토대로 각각 RGB 연산을 수행한다. 유채색의 경우 R, G, B값 중 큰 값을 찾아 Red, Green, Blue로 분류하고 무채색의 경우 R, G, B의 평균을 계산하여 40 이하인 것을 검은색. 200 이상인 것을 흰색으로 분류한다. 흰색은 연산하는 과정에서 영역(뚜껑, 몸통)별로 가중치를 부여해 데이터가 저장되는데, 이러한 이유는 백색 조명이 반사되어 나타나는 흰색이 몸통에 주로 위치하기 때문이다.
ⓓ 분류 연산
2차 분류된 데이터를 토대로 각 색깔의 개수를 측정한다. Red, Green, Blue, Black의 경우 10픽셀 이상 있으면 이물질이 있다고 판단한다. White의 경우 보정된 값이 5000 이상이면 흰색 이물질이라고 판단한다. 5000은 여러 번의 테스트를 거쳐 얻은 값이다. 이물질 판단 여부를 통해 color_test 값을 결정한다.
ⓔ color_test 값 확인
ⓐ~ⓓ단계에서 기록된 text_test 변수의 값을 확인하여 false이면 물체를 반환하기 위해 모터에게 메시지를 보낸다. true이면 최종적으로 투명한 폐페트병이라고 판단하여 물체를 분리수거 하기 위해 모터에게 메시지(down)을 전송한다.
색깔 분석코드 / 라즈베리파이(서버)
function color_search() {
console.log(“색깔분석중”);
// 변수 초기화
var color_test = true;
var pal = {
‘white’: 0,
‘black’: 0,
‘red’: 0,
‘green’: 0,
‘blue’: 0
}
pal['white']['state'] = 0;
imageToRgbaMatrix(‘test.jpg’).then(function (color) {
color.forEach(function (color, row) {
color.forEach(function (color, col) {
// 특정 영역 검색
if (col >= area.xs && col <= area.xe
&& row >= 115 && row <= area.ye) {
// 표준편차 구하기
var average =
(parseInt(color[0])+parseInt(color[1])+parseInt(color[2])) / 3
var sum = 0;
for (j = 0; j < 3; j++) {
sum += Math.pow(color[j] – average, 2);
}
var std = parseInt(Math.sqrt(sum / 3))
// 표준편차를 이용한 색구분
if (std <= 15) {
if (average > 200) {
if ((col < area.xs + 50) || (col > area.xe + 50)) {
pal['white'] += 300;
} else {
pal['white']++;
}
} else if (average < 40) {
pal['black']++;
}
} else {
if (color[0] > color[1] && color[0] > color[2]) {
pal['red']++;
} else if (color[1] > color[2]) {
pal['green']++;
} else {
pal['blue']++;
}
}
}
});
});
Object.keys(pal).forEach(function (key) {
console.log(key + ‘:’ + pal[key]);
if (key == ‘white’ ? pal[key] > 5000 : pal[key] >= 10) {
color_test = false;
console.log(key + “검출”)
io.sockets.emit(‘ingredient_color’, key);
}
});
console.log(“색깔분석완료”);
if (color_test == true) {
setTimeout(function () {
io.sockets.emit(‘complete’, ‘색깔분석완료-성공-3′);
mqtt_client.publish(‘motor’, “down”);
}, 2000)
} else {
io.sockets.emit(‘complete’, ‘색깔분석실패-실패-3′);
mqtt_client.publish(‘motor’, “up”);
}
});
}
색깔분석 결과를 나타내는 color_test 변수를 false로 초기화하고 분석 결과에 따라 true 값을 할당한다. imageToRgbaMatrix로 이미지를 RGBA 성분을 가지고 있는 2차원 배열로 리턴 받는다. 이를 성분분석에서 연산한 area를 토대로 색깔을 분류하고, pal 변수에 key로 지정된 색깔에 count 한다. 분류 작업을 마친 후 foreach문을 통해 색깔의 count 값을 비교하여 분석결과를 color_test 변수에 저장하고 이를 토대로 모터를 작동시킨다.
3.2.3. 분리수거
동작 기관 ESP8266에서는 서보모터를 제어하여 분리수거와 반환 기능을 수행한다.
명령 수신
라즈베리파이에서 투명 폐페트병 검출 기능이 수행된 후 분리수거가 가능 여부에 따라 UP(반환), Down(분리수거) 메시지를 동작기관으로 전송한다.
모터 동작
메시지를 받은 동작 기관은 메시지에 따라 서보모터를 제어해 물체 반환(UP)과 분리수거 (Down)을 수행한다. 그리고 감각기관으로 현재 모터의 상태인 motor_up과 motor_down을 전달한다. 감각기관의 도움을 받는 이유는 서보모터 자체 함수 중 각도를 리턴 받는 함수가 없어, 기능이 제대로 수행되었는지 알 수 없기 때문이다.
감각기관 상태 이동
모터 동작에 대한 메시지를 전달받은 감각기관은 메시지에 따라 analysis 상태에서 motor_up, motor_down 상태로 이동한다.
분리수거 및 반환 완료 검사
서보모터를 통해 틀이 움직이면 감각기관에 무게 센서값이 기준치에서 크게 변화한다. 즉 motor_up(반환), motor_down(분리수거) 상태에서 특정 무게 값을 정하여 기준치에 충족하면 반환, 분리수거가 완료되었다고 판단한다. 추가로 motor_up(반환)의 경우 물체가 가벼워 문을 뚫고 나오지 못하는 것을 고려하여 초음파센서값도 변수로 지정하였다. 초음파센서값을 통해 물체가 감지되지 않으면(물체 반환이 완료되면) 동작 기관에 reset을 명령한다.
전달받은 메시지에 따른 함수 호출 / 동작기관(모터)
void callback(char* topic, byte* payload, unsigned int length) {
String msg = “”;
for (int i = 0; i < length; i++) {
msg +=(char)payload[i];
}
Serial.print(“message : “);
Serial.print(msg);
if(msg == “up”){
motor_up();
}
else if(msg == “down”){
motor_down();
}
else if(msg == “reset”){
motor_reset();
client.publish(“raspi”,”connect-motor”);
}
}
‘up’ 메시지를 받으면 반환을, ‘down’ 메시지를 받으면 분리수거를 시작한다.
모터 제어 함수들 / 동작기관(모터)
void motor_up(){
servo1.write(180);
servo2.write(0);
client.publish(“sensor”,”motor_up”);
}
void motor_down(){
servo1.write(0);
servo2.write(180);
client.publish(“sensor”,”motor_down”);
}
void motor_reset(){
servo1.write(90);
servo2.write(90);
}
전역변수로 선언된 3개의 모터제어 함수들. 라즈베리파이에서 분리수거, 반환이 시작되면 현재 수행한 함수를 감각기관에게 메시지로 전달한다. 이는 무게 센서를 통한 서보모터 동작 완료를 확인하기 위함이다.
모터 메시지에 따른 상태 변화
감각기관(센서)
void callback(char* topic, byte* payload, unsigned int length) {
else if(msg == “motor_up”){
state = 5; // motor_up 상태로 이동
}
else if(msg = “motor_down”){
state = 6; // motor_down 상태로 이동
}
}
모터에서 받은 메시지에 따라 각각 motor_up state와 motor_down state로 상태 이동한다.
모터 동작 완료 확인 / 감각기관(센서)
else if(state ==5 && (190 >= distance && 185 <= distance) && (scale.read()/1000 <188) ){
client.publish(“motor”,”reset”);
state = 1; // wait 상태로 이동
}
else if(state ==6 && (scale.read()/1000 <0) ){
client.publish(“motor”,”reset”);
state = 1; // wait 상태로 이동
}
3.2.4. 사용자 인터페이스
디스플레이
ⓐ 메인화면
메인화면은 2가지 표정으로 구분된다. 재활용을 성공한 경우나 처음 로봇이 구동된 경우 Happy, 재활용에 실패해 물체가 반환된 경우 Angry 캐릭터가 메인화면에 나타난다.
메인화면에서 오른쪽 위 끝에 있는 설명 버튼을 누르면, 재활용 방법과 로봇에서 폐페트병을 검출하는 방법을 소개한다.
ⓑ 분석화면
3단계의 분석 진행 과정과 결과를 우측에 실시간으로 렌더링하여 보여준다.
음성 출력
google TTL 서비스를 통해 음성을 추출하였고, 서버 측에서 음성을 출력하기 위해 play-sound를 사용했다.
play-sound로 재생하는 음성은 서로 독립적으로 재생되어 소리가 겹치는 일이 발생할 수 있다. 전역변수로 audio를 생성하고, 음성 재생이 필요할 때마다 audio.kill()로 현재 재생되고 있는 소리를 제거하고 play를 통해 재생시켰다.
음성출력 / 라즈베리파이(node.js)
var audio = 0;
function sound(data) {
if (audio != 0) audio.kill();
audio = player.play(‘audio/’ + data);
}
3.3. 개발 환경
· OS : 라즈비안(라즈베리파이), 윈도우10(노트북)
· 개발 언어 : C, javascript(node.js), html, css
· 툴 : brackets, 지니, 아두이노IDE
· 통신 : MQTT, HTTP(Socket.io, GET)
Node.js 주요사용 모듈
express | Node.js를 위한 웹 프레임워크 |
socket.io | 실시간 웹 애플리케이션을위한 JavaScript 라이브러리 |
@google-cloud/vision | google cloude vision api를 사용하기 위한 모듈 |
image-to-rgba-matrix | 이미지를 rgba matrix로 변경해주는 모듈 |
pi-camera | 라즈베리파이용 카메라를 제어하기 위한 모듈 |
play-sound | 음성 재생을 위한 모듈 |
4. 단계별 제작과정
4.1. 하드웨어 제작과정
하드웨어 1주차
ⓐ 주제 선정 및 기능 아키텍처 작성을 통한 필요 물품 구매
필요 물품
· 폼보드 : 특별한 장비 없이 가공에 있어 자유롭고 튼튼하다.
· ESP8266 mini : 각 기관의 역할을 나누는 데 필요했다.
· 무게 센서 : 물체의 무게를 측정해 분류하는 데 필요했다.
라즈베리파이, 카메라, 스피커, 모니터, 초음파센서는 기존에 가지고 있던 것을 사용했다.
하드웨어 2주차
ⓐ 역할을 분담하여 센서, 모터, 카메라를 동작(테스트)해보는 학습을 진행하였다.
ⓑ 제작에 앞서 외형도를 제작했다.
하드웨어 3주차
ⓐ 외부 틀 제작 : 2주차에서 작성한 외형도를 바탕으로 문과 틀을 만들었다.
ⓑ 밑판제작 및 부품 테스트
무게 센서와 서보모터를 연결해 물체가 올라갈 밑판을 제작하였고, 모터 동작을 테스트했다.
초음파센서를 입구 상단에 설치하고, 무게센서와 테스트했다.
ⓒ 내벽 제작
좌측에 동작기관, 우측에 감각기관으로 회로를 연결하였다.
회로 정리 후 제작한 내벽을 붙여 선이 보이지 않게 만들었다.
ⓓ 앞면 제작
로봇의 모니터가 들어갈 앞판을 제작하였다.
ⓔ 윗면 제작
조명, 라즈베리파이 카메라, 스피커 등이 놓일 윗면을 제작하였다.
하드웨어 4주차
카메라 선이 길지 않아 동작 테스트에 문제가 발생했다. 윗면에 설치하는 것이 아닌, 윗면에 붙이는 것으로 계획을 변경했다.
쓰레기통의 교체가 쉽고 들어있는 상황을 볼 수 있도록 하단은 오픈형으로 제작하였다.
4.2. 소프트웨어 제작과정
소프트웨어 1주차
하드웨어 제작 1주차 첨부한 기능 구성도를 작성하고, 필요한 기술을 조사했다.
조사내용
· 각 기관 간의 통신은 MQTT 통신을 사용한다.
· 로봇의 얼굴인 디스플레이는 웹사이트 형태로 제작한다.
· 카메라 모듈을 사용해 사진을 찍은 후 구글 API을 사용해서 라벨 및 이물질의 유무 등을 분석한다.
· 분석 결과에 따라, 모터가 작동해 플라스틱을 분리배출할 수 있게 도와준다.
소프트웨어 2주차
ⓐ 필요 기술 학습(물체 검출)
vision API를 사용해 물체의 성분(plastic, glass 등), 텍스트(프린팅, 라벨), RGB 추출(이물질 검사)을 하기 기대했다. 성분 분석과 텍스트 검출에는 문제가 없었지만, 색 추출에서 이질적인 색을 잡는 민감도를 기대할 수 없었다. 다른 방법인 10개의 색 파레트를 추출하는 color-thief 모듈을 사용하여, 뚜껑 등의 이질적인 색 검출에 성공했다.
ⓑ 필요 기술 학습(사용자 인터페이스)
웹 클라이언트 : 로봇의 얼굴에 해당하는 모니터에 HTML, CSS 등으로 캐릭터를 제작하였다. 아직 센서들과 통신을 하지 않기 때문에, 3초가 지나면 사진이 찍혀 ⓐ의 단계로 넘어가도록 코드를 작성했다.
웹 소켓 : 동적인 웹 클라이언트를 만들기 제작하기 위해 웹 소켓을 사용하였다. 서버에서 연산한 결과를 클라이언트로 데이터를 전송해 사용자가 실시간으로 검사 결과를 확인할 수 있다.
ⓒ 모터, 센서 기능 구현
아두이노 IDE를 사용해 물체가 들어오고 나감을 알 수 있도록 초음파센서 관련 함수를 작성하였고, 분리수거 & 반환을 위해 모터 함수를 작성하였다.
소프트웨어 3주차
ⓐ MQTT 통신
MQTT 통신을 사용하여 각 디바이스가 상호작용한다. MQTT 통신을 사용한 이유는 클라이언트-서버 구조로 이뤄진 HTTP, TCP는 주로 클라이언트 요청에 대한 서버에 응답으로 통신하기 때문에 디바이스 간 통신이 어렵다. 반면, MQTT는 broker, publisher, subscriber 구조로 이루어져 있으므로, broker가 중계하고 client 간의 통신이 가능하다. 라즈베리파이에 브로커를 설치하고, 감각기관, 동작기관, 웹서버가 client가 되어 각자 필요한 메시지를 보내고, 받은 메시지를 통해 동작을 실행한다.
ⓑ 사용자 인터페이스 추가(웹 클라이언트)
메인화면 : 분리수거 성공과 실패에 따라서 메인화면의 캐릭터 얼굴이 변화한다.
설명페이지 : 사용자가 재활용 방법과 로봇의 검출 과정에 대해 학습할 수 있다.
ⓒ 사용자 인터페이스 추가(음성)
교육적인 로봇의 목표에 재활용에 관한 피드백을 제공하기 위해 음성 파일을 각 단계별로 출력되게 제작하였다.
소프트웨어 4주차
ⓐ 각 디바이스에 MQTT 통신을 구현하였다.
라즈베리파이(MQTT code)
var mqtt_client = mqtt.connect(‘mqtt://192.168.0.51′)
mqtt_client.on(‘connect’, function () {
mqtt_client.subscribe(‘raspi’, function (err) {
if (!err) {
console.log(‘subscribe : raspi’);
}
})
})
mqtt://주소를 통해 라즈베리파이에 구축한 브로커에 연결하여 ‘raspi’를 구독하였다.
감각기관, 동작기관 (MQTT code)
while (!client.connected()) {
Serial.print(“Attempting MQTT connection…”);
if (client.connect(“ESP8266Client”)) {
Serial.println(“connected”);
client.subscribe(“sensor”);
} else {
Serial.print(“failed, rc=”);
Serial.print(client.state());
Serial.println(” try again in 5 seconds”);
delay(5000);
}
}
esp8266 mini에서 MQTT 브로커로 연결하여 감각기관의 경우 ‘sensor’을 동작기관의 경우 ‘motor’을 구독한다.
2주차에서 분담을 통해 구현한 물체 감지, 모터작동 기능들을 MQTT 통신에 맞게 코드를 수정하였다.
소프트웨어 5주차
물체 테스트를 진행 중에 color-thief 모듈을 이용한 rgb검출에 대한 문제점이 드러났다. vision API보다는 작은 이물질에 대해 식별 가능했지만, 더 작은 이물질에 대해서는 검출하기 어려웠고, 결정적으로 배경색과 RGB 값이 비슷한 흰색, 검은색은 검출하기 어려웠다. 대체방법으로 이미지를 RGB matrix으로 변환해주는 모듈인 color-to-rgba-matrix를 사용하여 픽셀 단위의 분석을 진행하였다. 픽셀 단위로 연산량을 줄이기 위해 물체의 위치에 따른 분석하는 방법을 생각하였고, vision API에 위치 정보를 제공해주는 함수를 사용해 경곗값과 크기를 측정했다.
소프트웨어 6주차
ⓐ 사용자 인터페이스(웹 클라이언트)
5주차에서 측정한 경계값을 웹 클라이언트에 전송하여 BOX 형태로 표시하도록 하였다. 이는 사용자가 물체 분석되는 과정을 확인하는데 도움을 준다.
ⓑ 감각기관 코드 수정
감각기관의 변수를 수정하였다. 이전에는 문이 열리고 닫히면, 물체가 들어왔음을 감지했었는데, 조건문을 추가하여 무게 센서값까지 측정하는 방법으로 변경하였다.
ⓒ 연결상태 확인 추가
로봇을 테스트하기 위해 서버를 실행하는 과정에서 통신상태를 확인하지 않고, 실행한 경우, 연결상태를 확인하지 않고 실행해 로봇의 흐름이 섞이는 일이 발생했다. 이러한 문제를 해결하기 위해 처음 서버가 실행되기 전 MQTT 통신상태를 확인하기 위한 메시지를 보낸다. 메시지를 받은 디바이스는 초기화를 진행하고, 답변을 전달한다. 답변을 받은 라즈베리파이는 웹 서버를 실행시킨다.
5. 사용한 제품 리스트
모델명 | 디바 상품번호 |
SEN0160 | 1278214 |
HC-SR04 | 1076851 |
MG996R | 1313388 |
WeMos D1 mini | 1327519 |
Raspberry Pi 4 Model B | 12234534 |
Raspberry Pi Touch Display | 1273487 |
TS-CS01BO | - |
밝기조절 발광 LED 라이트 | - |
RPI 8MP CAMERA BOARD | 1077951 |
6. 후속연구제안
6.1. 배경 색과 빛 반사
페트병의 투명성 여부를 판단하는 과정은 사진을 촬영하여 배경색과의 유사도를 검사하는 방식으로 진행된다. 이 때, 두 가지 문제점이 발생한다. 만약 라벨이나 이물질 등이 배경색과 유사한 색이라면 로봇은 ‘투명한 페트병’인 것으로 판별하게 된다. 또한 플라스틱 표면에 빛을 비추면 빛이 반사되어 카메라로 사진을 찍으면 반사된 부분이 흰색으로 인식되어 투명성 판별에 장애요소가 될 수 있다. 이는 신뢰도 하락과 재활용률 하락으로 이어지는 문제이므로 주의가 필요하다. 본 작품은 배경색을 회색(카메라로 촬영했을 때 R, G, B 세 값의 표준편차가 15이내, 평균은 40이상 180이하. 이때, 각 값은 0~255사이의 값을 갖는다.)으로 설정하여 라벨이나 이물질의 색과 분명한 차이를 둘 수 있도록 하였다. 그러나 만약 회색으로 이루어진 플라스틱 제품이 나오게 되면 본 작품에서 채택한 투명성 판별법은 신뢰도가 떨어지게 된다. 또한 빛 반사문제는 카메라를 사용한다면 피할 수 없는 문제이므로 해결하기 어렵다. 따라서 후속연구에서는 LPKF TMG 3 등의 광투과율 측정 장치를 이용하여 플라스틱의 투명도를 정확하게 측정할 수 있도록 할 것을 제안한다.
6.2. 플라스틱 압축 기능
플라스틱은 압축하여 배출해야 쓰레기통이나 쓰레기봉투를 효율적으로 사용할 수 있다. 따라서 플라스틱을 쓰레기통에 담는 과정에서 플라스틱을 압축하는 기능이 있으면 좋겠다는 생각을 했다. 이를 위해서는 강한 모터나 압축기가 필요한데 전력소비, 무게, 부피, 예산 등의 측면에서 효율성이 떨어지므로 배제하였다. 대체할 수 있는 기능으로는 페트병이 압축되었는지의 여부를 판단하는 방법이 있다. 이를 구현하려 하였으나, 압축된 페트병을 판별할 수 있는 명확한 기준을 찾기는 힘들었다. 비슷한 크기의 페트병이라면 무게도 비슷할 것이라고 가정하고 압축된 페트병과 압축되지 않은 페트병의 (넓이/무게) 값을 비교하였으나 유의미한 데이터를 얻지 못했기 때문에 페트병이 압축되었는지 판별하는 기능은 구현하지 못했다. 따라서 후속연구에서는 플라스틱 압축 기능이나 압축되지 않은 플라스틱을 구별하는 기능을 추가할 것을 제안한다.
6.3. 처리량
본 작품은 한 번에 1개의 페트병만 검사할 수 있다. 따라서 많은 양의 페트병을 버리려고 한다면 하나씩 천천히 넣어줘야 한다. 이는 굉장히 불편한 작업이므로 본 작품을 일상생활에서 사용하기 위해서는 이 문제를 반드시 해결해야 한다. 그러나 한 번에 검사할 수 있는 페트병의 수는 제한적이다. 따라서 큰 통에 페트병을 담아두고 거기에서 하나씩 꺼내서 검사하는 방법을 생각해볼 수 있다. 그런데, 사진을 찍는 시간은 약 10초, 재활용 가능한 플라스틱인지 분석하는 시간은 약 10초로 한 페트병을 처리하는 것에 평균 20초의 시간이 소요된다. 이는 디스플레이에 분석과정을 보여주고 음성으로 안내하기 위해서 의도적으로 시간을 늘린 것인데, 많은 양의 페트병을 처리하는 것에 방해요소로 작용한다. 따라서 안내하는 것을 간소화하는 것으로 시간을 단축할 수 있다. 또한 여러 개의 페트병을 한 번에 촬영하여 처리시간을 단축하는 방법도 있다. 단, 한 번에 많은 페트병을 처리할 경우, 그 중에서 재활용 되지 않는 패트병을 골라내는 방법에 대한 고찰이 선행되어야 한다.
7. 참고문헌
· 그린피스 김이서(2019년 12월),플라스틱 대한민국- 일회용의 유혹https://www.greenpeace.org
· 투명 페트병 재활용 제품 안내, 환경부 :https://www.newsro.kr
· 최병용(2021.02.19), 투명 페트병 분리배출 현장에 가보니~ : https://www.korea.kr/news/reporterView.do?newsId=148883922