안녕하세요! 충남삼성고등학교 이호은입니다.
지난 2018 고교동아리소프트웨어경진대회에서 무려 금상(1등)과 관람객들의 투표로 선정된 인기상까지 평정하고 왔는데요.
제작했던 소프트웨어에 대해 간략히 소개해드리는 시간을 갖도록 하겠습니다.
먼저 주제는 가상현실 공간 속에서 발표력을 증진시켜보자는 목적을 갖고 시작했습니다.
먼저 대회 부스에서 전시했던 영상을 보실까요?
제가 밤을 새가며 제작했던 애플의 홍보영상을 패러디한 홍보영상입니다.
위 영상은 KTX와 택시에서 급하게 편집한 추가 홍보영상입니다. 사용법에 대해 간략히 설명해줍니다.
정말 오랜만에 영상을 두 개 만드느라 잊었던 편집 기술까지 다 기억해가며 급하게 제작했습니다. 그래도 만들고 보니 뿌듯하더군요.
다음으로는 A1사이즈로 부스에서 전시했던 포스터인데요. 전체적인 설명이 작성되어 있습니다. 포스터도 전부 제가 직접 제작하였습니다.
제가 맡은 일은 메인 프로그램중에서 csv 로그를 읽어오는 프로그램을 C#을 이용해서 작성하였고, 파이썬을 통해 아두이노의 값을 포트를 통해 들여오는 리딩 프로그램, 파이썬을 통한 녹화, 재생 기능 구현 그리고 csv 파일에 로그를 쓰는 파이썬 프로그램, 소리를 측정하는 아두이노 장치, 심박수를 측정하는 팔찌, 그래프를 그리는 함수, 그리고 점수 계산 공식, 메인화면 공간, 교실 3D 디자인, 강당 3D 디자인, 탭과 모니터 디자인을 담당했습니다. 메인 프로그램의 큰 틀은 두 친구가 제작하였고 주변 프로그램과 부가 기능들을 전부 제가 담당하였습니다.
먼저 아두이노 값을 읽어오는 파이썬 프로그램 코드입니다. 이는 심박수센서와 소리 크기 센서의 값을 읽어와 로그를 작성합니다.
데이터 흐름도입니다. 서버 부분은 추후 제작 계획에 있습니다.
알고리즘입니다. 위 알고리즘 중 시리얼값을 포트를 통해 로그 작성하는 파이썬 프로그램입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
import serial
import csv
from datetime import datetime
import os
import time
import socket
# 아두이노 시리얼 통신 개시
SoundPort = 'COM13' # 시작 전에 장치 관리자에서 확인하기
print(SoundPort)
SoundBRate = 9600
HeartPort = 'COM200' # 시작 전에 장치 관리자에서 확인하기
print(HeartPort)
HeartBRate = 115200
Sound_arduino = serial.Serial(SoundPort, SoundBRate) # 소리 센서 통신 설정
Heart_arduino = serial.Serial(HeartPort, HeartBRate) # 심박수 센서 통신 설정
SoundSmall = 0
SoundBig = 0
SoundScoreTime = 0
# 서비스 스타팅 메세지
print("여우별 VR 발표준비 시스템 로그 서비스 프로그램 시작됨")
print("시스템 검사 중...")
# 시스템 검사: 만약 finishCheck.txt 파일이 없다면 -> 생성
if not os.path.isfile("C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt"):
makeNew = open("C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt", 'w')
makeNew.close()
print("파일 오류 수정 완료: finishCheck.txt 생성함")
print("시스템 검사 완료")
print("통신 설정 완료. 통신 시작")
COM = socket.gethostname()
print("호스트 이름 : " + COM)
# 아두이노 로그 파일 지정
j = open("C:\\YUB\\10.28\\Assets\\Resources\\VRLog.csv", 'w', encoding='utf-8', newline='')
SoundScoreRead = open("C:\\YUB\\10.28\\Assets\\Resources\\SoundScoreLog.csv", 'w', encoding='utf-8', newline='')
wr = csv.writer(j)
SoundScoreReadWriter = csv.writer(SoundScoreRead)
SoundSize = 0
SoundSizeStandard = 6.5
# 반복문 제어 변수
type = True
# 로그 넘버 변수
CountLog = 1
SoundScoreReadWriter.writerow("A")
# 발표 동안 무한 반복
while type:
# 로그 값 지정
Sa = Sound_arduino.readline()
Sa = Sa.decode()
Ha = Heart_arduino.readline()
Ha = Ha.decode()
if int(Ha) <= 65:
Ha = '065xx'
elif int(Ha) >= 105:
Ha = '105xx'
# csv 파일 쓰기
now = datetime.now()
if float(Sa) > SoundSizeStandard:
SoundSize = 1
SoundBig = SoundBig + 1
else:
SoundSize = 0
SoundSmall = SoundSmall + 1
SoundScoreTime = SoundScoreTime + 1
# 로그 쓰기 위한 날짜 받아오기
yearlog = str(now.year)
monthlog = str(now.month)
daylog = str(now.day)
hourlog = str(now.hour)
minutelog = str(now.minute)
secondlog = str(now.second)
# YYYY 형태를 YY로 변환
yearlog = yearlog[2:]
# 각 문자열의 길이가 1이면(X) 0을 붙여 2자리고 만듬(0X)
if len(monthlog) is 1:
monthlog = '0' + monthlog
else:
monthlog = str(monthlog)
if len(daylog) is 1:
daylog = '0' + daylog
else:
daylog = str(daylog)
if len(hourlog) is 1:
hourlog = '0' + hourlog
else:
hourlog = str(hourlog)
if len(minutelog) is 1:
minutelog = '0' + minutelog
else:
minutelog = str(minutelog)
if len(secondlog) is 1:
secondlog = '0' + secondlog
else:
secondlog = str(secondlog)
Sa = Sa[:4]
lenHa = str(Ha)
lenHa = len(lenHa)
lenHa = int(lenHa-2)
Ha = Ha[:lenHa]
HeaderRow = ["CountLog", "LogTime", "SoundLog", "HeartLog"]
if CountLog is 1:
wr.writerow(HeaderRow)
# 로그 쓰기 위한 writer 세팅
AllLog = [str(CountLog), str(Sa), str(Ha)]
dateLog = str(yearlog + monthlog + daylog + '_' + hourlog + minutelog + secondlog)
CountLog = str(CountLog)
if len(CountLog) is 1:
CountLog = str("000" + CountLog)
elif len(CountLog) is 2:
CountLog = str("00" + CountLog)
elif len(CountLog) is 3:
CountLog = str("0" + CountLog)
Ha = str(Ha)
if len(Ha) is 1:
Ha = str("00" + Ha)
elif len(Ha) is 2:
Ha = str("0" + Ha)
if len(Sa) is 4:
Sa = str(Sa)
Sa = str(Sa)
if len(Sa) is 3:
Sa = str(Sa + "0")
elif len(Sa) is 2:
Sa = str(Sa + "00")
elif len(Sa) is 1:
Sa = str(Sa + ".00")
Log = (CountLog, dateLog, Sa, Ha)
# 로그 작성
wr.writerow(Log)
print(Log)
CountLog = int(CountLog)
# 만약 finishCheck.txt 파일이 존재하지 않는다면 type 변수를 False로 바꾸어 반복문 break
if not os.path.isfile('C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt'):
print("Finish 버튼 활성화: finishCheck.txt 삭제 감지")
break
# 0.5초마다 실행
time.sleep(1)
CountLog = CountLog + 1
NowHeart = open("C:\\YUB\\10.28\\Assets\\Resources\\HeartLog.csv", 'w', encoding='utf-8', newline='')
NowHeart = csv.writer(NowHeart)
NowHeart.writerow("A")
NowHeart.writerow([str(Ha)])
# 반복문 종료시 로그 읽기 종료
j.close()
'''
SoundScoreRead = open("C:\\YUB\\10.28\\Assets\\Resources\\VRLog.csv", 'w', encoding='utf-8', newline='')
SoundScoreWrite = open("C:\\YUB\\10.28\\Assets\\Resources\\SoundScoreLog.csv", 'w', encoding='utf-8', newline='')
'''
readLog = open("C:\\YUB\\10.28\\Assets\\Resources\\VRLog.csv", 'r', encoding='utf-8', newline='')
writeNew = open("C:\\YUB\\10.28\\Assets\\Resources\\HeartAVLog.csv", 'w', encoding='utf-8', newline='')
SoundScoreReadWriter.writerow("A")
if (SoundSmall >= SoundBig):
SoundSizeScore = (SoundSmall / SoundScoreTime)
SoundScoreReadWriter.writerow(str(SoundSizeScore))
elif (SoundSmall < SoundBig):
SoundSizeScore = (SoundBig / SoundScoreTime)
SoundScoreReadWriter.writerow(str(SoundSizeScore))
Num = int(CountLog/15)
j = 1
NotHeader = 0
writeNew = csv.writer(writeNew)
writeNew.writerow("A")
for line in readLog:
if NotHeader is 1:
if int(line[0:4]) == int(j*Num):
NowWR = line[24:]
NowWR = int(NowWR)-50
NowWR = str(NowWR)
writeNew.writerow([NowWR])
j = j + 1
NotHeader = 1
readLog.close()
SoundScoreReadWriter.writerow(str(SoundSizeScore))
# 파일 생성
file = open('C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt', 'w')
file.close()
|
cs |
코드를 최대한 간략하게 구성하기 위해 많은 고민을 했습니다. 저번 캔위성때와 마찬가지로 비슷한 코드인데요. 한 번 해봐서 조금 쉬웠지만 포트를 통해 읽어오는 것과 이를 변수에 적용시켜 연산하는 알고리즘이 어려웠지만 성공했습니다.
다음으로는 파이썬을 통해 발표 도중의 목소리를 녹음하고 결과창에서 재생할 수 있도록 도와주는 파이썬 프로그램 코드입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
import pyaudio
import os
import wave
if not os.path.isfile("C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt"):
makeNew = open("C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt", 'w')
makeNew.close()
print("파일 오류 발견... 생성 완료")
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "YUB.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)
print("* recording")
frames = []
while True:
data = stream.read(CHUNK)
frames.append(data)
if not os.path.isfile('C:\\YUB\\10.28\\Assets\\Resources\\finishCheck.txt'):
print("Finish 버튼 활성화: finishCheck.txt 삭제 감지")
break
print("* done recording")
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
|
cs |
예외처리와 최대한 간단히 구성한 코드입니다. 녹음 코드는 인터넷에 정보가 많아서 정보를 얻기는 쉬웠으나 프로그램에 맞게 알고리즘을 다시 짜는 것이 힘들었습니다.
프로그램 실행 도중 관여되는 로그 파일들과 파일들입니다. 특정 디렉토리의 파일의 유무와 생성, 제거를 이용해서 프로그램 알고리즘을 작성했습니다. 프로그램을 하며 가장 어려웠던 점이 C#과 파이썬을 연동하는 것이었는데 파일의 유무를 이용한 알고리즘 아이디어가 떠올라 이를 적용시켜 성공했습니다.
다음은 제가 라이노 5를 이용해 3D로 제작한 모델들입니다.
첫 번째로 교실입니다. 책상과 의자는 오픈소스를 이용하였습니다.
두 번째로 강당입니다. 이 강당은 5번째 버전으로 실제로 적용하고 실행해보며 3번 갈아 엎고 처음부터 제작하고 두 번의 큰 수정을 통해 제작된 가장 오래걸린 모델입니다.
세 번째로 태블릿입니다. 남은 시간과 정보를 어떻게 나타낼까 고민하다가 제가 탭에 표시하자고 하여 디자인하게 된 탭입니다. 현실감을 살리기 위해 최대한 비슷하게 디자인하기 위해 노력했습니다.
다음으로 심박수 팔찌입니다. 3D프린터를 이용해 출력하여 제작하였습니다. 내부에는 아두이노용 심박수 센서를 장착하고 아두이노 프로미니와 연결하여 사용하였습니다.
마지막으로 모니터입니다. 이는 강당에서 정보를 띄우기 위해 탭 대신 사용되며 메인화면에서 공간을 고를 때 사용합니다.
그리고 전체적인 프로그램에 UI와 결과창, 버튼 등 전부 디자인하였습니다.
마지막으로 아두이노를 통해서 목소리 크기를 측정하는 기기를 제작하였는데요.
이와 같이 3D 디자인을 하였고 3D 프린터로 뽑으려 했으나 학교 3D프린터의 점검으로 직접 제작하였습니다. 프링글스 통과 도화지를 사용해서 제작하였는데요.
또한 소리의 크기에 따라 게이지가 초록 - 파랑 - 빨강의 순서로 표시됩니다.
내부는 이렇습니다. 전부 납땜을 하여 연결하였고 고정시켰습니다.
마이크와 하단의 LED를 장착하고 아두이노 우노를 장착하여 제작하였습니다.
아두이노에서는 소리를 컴퓨터로 전송합니다.
이렇게 전체적인 프로그램과 제가 작업한 내역에 대해 간단히 소개해드렸는데요. 코드나 작업에 대해 궁금하시면 댓글로 남겨주세요!
대회 준비를 하며 정말 많은 시간을 투자했지만 결과도 좋고 완성도 있는 프로그램을 제작한 것 같고 많이 성장한 모습이 보여 뿌듯합니다. 감사합니다!