핸즈온 머신러닝 2판을 일기 시작한 것이 언제인지 기억도 안나고, 1장을 읽고 연습 문제 위주로 공부를
해보고자 하면서 1장의 연습문제를 정리해 블로그에 올린 것도 이미 2020년 11월 그러니까…4개월
하고도 열흘이 지났다…남들 같았으면 책을 네댓 권은 마스터했을 시간이다…ㅠ.ㅠ
물론 그렇다고 논 것은 아니지만 이래서야 되겠는가 하는 자괴감이 든다…ㅠ.ㅠ
게다가 더더욱 놀라운 것은 나름 열심히 보느라고 2장은 3번 정도 반복해가면서 읽었는데도 불구하고…
연습문제를 풀려고 보니…뭔소린지 하나도 모르겠더라는…그냥 풀기가 어려운 정도가 아니고 아예
어디부터 시작을 해야 하는지를 모르겠는 황당한 상황…ㅠ.ㅠ
결국 그 이후 다시 수차례 훑어보고 나서야 2장에서 이야기하고자 하는 것이 무엇인지를 이해할 수 있었다.
그리고 그 전까지 이 부분을 제대로 이해하지 않고 그저 모델을 만들고 학습을 시키고 테스트를 하는 과정만
생각하고 있던 내가 얼마나 어리석었는지를 깨달았다…ㅠ.ㅠ (오늘의 글은 눈물바다로구나…ㅠ.ㅠ)
그래서 일단 2장의 내용을 간략하게 정리해두고 좀 더 기초로 돌아가서 데이터를 다루는 법, Python 및
관련 패키지들의 API에 대해 조금 더 기초를 다지기로 하였다.
더불어 핸즈온 머신러닝 2판 학습은 잠시 미뤄두고 우선 몇 권의 다른 책들을 먼저 읽어보기로 하였다.
아래는 올해 목표로 잡은 책들 목록이다.
모두의 데이터 과학 with 파이썬
파이썬을 활용한 머신러닝 쿡북
머신러닝 탐구생활
캐글 가이드
일단 이렇게 데이터 조작에 대해 조금 더 학습을 진행하고 다시 본격적으로 머신러닝에 대해 공부를 진행할
계획이다. 여기에 교재로 쓸 책은 다음과 같다.
핸즈온 머신러닝 2판
밑바닥부터 시작하는 딥러닝 1
밑바닥부터 시작하는 딥러닝 2
밑바닥부터 시작하는 딥러닝 3
일단 여기까지만 해도 올 한 해는 훌쩍 가버릴듯싶다.
그럼 일단 핸즈온 머신러닝 2판 2장의 내용을 간략하게 정리해보자~
머신러닝 프로젝트 진행 프로세스
데이터 준비 : 다운로드 및 저장
. 어떤 데이터를 다운로드 받느냐에 따라 다르지만 대체로 웹에서 다운로드 받는 경우가 많을 것이며,
로컬에 저장된 파일로부터 읽어올 수도 있고, 또 머신러닝 관련 패키지에서 제공하는 경우들도 있다.
. 관련 라이브러리 및 API
import urllib
import ssl
# 아래 오류 벌생으로 ssl import
# URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)>
ssl._create_default_https_context = ssl._create_unverified_context
urllib.request
적절한 데이터 타입으로 변환 (판다스의 DataFrame 등)
. 다운로드 받아 저장한 파일에 들어있는 데이터들은 실제 사용하기 위해 적절한 형식으로 바꾸어 주어야
한다. numpy의 ndarray나 pandas의 DataFrame으로 바꾸면 데이터를 가공하기가 쉬워진다.
. 관련 라이브러리 및 API
import pandas as pd
# csv 파일을 읽어 DataFrame 타입으로 리턴한다.
pd.read_csv(csv_path)
샘플 데이터를 통해 데이터셋의 상태 확인
. 누락된 데이터는 없는지?
. 각 특성의 데이터 타입 확인 (숫자형, 문자형, 범주형 등)
. 각 특성간의 스케일 차이 확인 (추후 스케일 조정 수행)
. 수치형 데이터의 분포 확인 (추후 가능한한 종모양의 분포로 조정)
. 관련 라이브러리 및 API
# housing은 DataFrame 타입의 객체이며 info() 함수를 실행하면 Non-Null Count 컬럼으로
# null 데이터가 있는지 여부를 확인할 수 있다. 또한 Dtype 컬럼으로 각 컬럼의 데이터 타입을
# 확인할 수 있다.
housing.info()
# 수치형 데이터의 경우 describe() 함수를 이용하여 각종 통계적 값을 확인할 수 있다.
housing.describe()
# matplotlib의 hist() 함수를 이용하여 히스토그램을 그리면 데이터의 형태를 시각적으로
# 확인할 수 있다.
%matplotlib inline
import matplotlib.pyplot as plt
# Pandas DataFrame의 hist() 함수는 matplotlib.pyplot.hist()를 호출한다.
housing.hist(bins=50, figsize=(20,15))
plt.show()
훈련 세트와 테스트 세트로 구분
. 모델을 결정하고 훈련을 하기 전에 미리 훈련 세트와 테스트 세트를 분리하여 테스트 세트는 완전히 샌드박스로 만든다.
. 훈련 세트와 테스트 세트는 대략 80 : 20의 비율로 나눈다.
. 테스트 세트가 계속 변경되어 전체 데이터 세트가 모두 한 번씩 테스트 세트에 포함되는 것을 막아야 한다.
. 관련 라이브러리 및 API
from sklearn.model_selection import train_test_split
# scikit-learn의 train_test_split을 이용하여 훈련 세트와 테스트 세트를 나눈다.
train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)
데이터 샘플링
. 계층적 샘플링 : 모집단과 같은 분포를 갖도록 데이터 샘플링
. 너무 많은 계층으로 나뉘어서는 안되면 각 계층은 충분히 커야 함
. 훈련세트와 테스트 세트를 나눌 때는 이러한 계층적 샘플링이 되도록 나눔
from sklearn.model_selection import StratifiedShuffleSplit
# scikit-learn의 StratifiedShuffleSplit을 이용하여 계층적 샘플링을 수행한다.
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["income_cat"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
데이터 시각화
. 훈련 세트를 이용하여 데이터를 시각화 함으로써 데이터에 대해 조금 더 깊이 파악하는 과정
. 산점도를 이용하여 특성간의 상관관계 조사
. 이상 형태를 보이는 데이터(특성) 구간을 확인
. 특성들의 조합(상관관계 분석)을 통해 의미 있는 특성을 새로이 발견
. 관련 라이브러리 및 API
%matplotlib inline
import matplotlib.pyplot as plt
# 시각화를 위해서는 matplotlib 패키지를 잘 숙지하도록 하자!
# Pandas DataFrame의 corr() 함수를 이용하면 특성간의 상관관계를 확인할 수 있다.
corr_matrix = housing.corr()
데이터 정제
. 학습을 하기 위한 데이터의 준비는 가능한한 자동화를 하는 것이 좋음
. 누락된 특성 값 처리 : 누락된 값이 있는 특성을 모두 버리거나, 값이 누락된 특성이 있는 샘플들을 모두 버리거나, 누락된 값을 대표값으로 채우기 (평균, 중간값 등). 누락된 특성에 대한 처리는 나중에 추가된 데이터에서 어떤 특성에서 누락된 값이 나올지 모르므로 모든 수치형 특성에 적용하는 것이 좋음.
. 범주형 특성 정리
. 특성 스케일링 : 특성들의 스케일이 유사하도록 조정한다. 목표값에 대한 스케일링은 불필요하다.
> min-max 스케일링(정규화) : 데이터에서 최솟값을 뺀 후 최댓값과 최솟값의 차이로 나눔.
0~1사이의 범위에 들도록 함.
MinMaxScaler
> 표준화 : 평균을 뺀 후 표준편차로 나누어 결과 분포의 분산이 1이 되도록 함.
상한과 하한이 없어 어떤 알고리즘에서는 문제가 될 수 있음.
이상치에 영향을 덜받음.
StandardScaler
> 스케일링은 훈련 세트에 대해서만 fit() 메서드를 적용해야 함. transform()
메서드는 훈련 및 테스트 세트 모두 적용.
. 관련 라이브러리 및 API
# 숫자형 특성들만을 대상으로 누락된 값을 각 컬럼의 특정 값으로 채움
from sklearn.impute import SimpleImputer
# 범주형 문자열 데이터를 수치형으로 바꿈
from sklearn.preprocessing import OrdinalEncoder
# 범주형 문자열 데이터를 One-Hot_Encoding 형식으로 변환
from sklearn.preprocessing import OneHotEncoder
# 데이터를 표준화 시켜주는 변환기
from sklearn.preprocessing import StandardScaler
데이터 샘플링을 통해 변경된 샘플들을 테스트 세트에 추가한다. 이러한 과정들을 추후 파이프라인에 넣을수 있도록 추정기 또는 변환기를 만든다.
. 관련 라이브러리 및 API
# fit() 함수와 transform() 함수를 구현하여 변환기를 만들 수 있다.
# TransformerMixin을 상속하면 fit_transform() 함수가 자동 생성된다.
# BaseEstimator를 상속하면 get_params() 함수와 set_params() 함수가
# 자동 생성된다.
from sklearn.base import BaseEstimator, TransformerMixin
만들어진 추정기와 변환기를 파이프라인에 넣어 자동화 한다.
. scikit-learn의 PipeLime 이용
. 컬럼 단위로 파이프라인을 적용하고자 할 때는 scikit-learn의 ColumnTransformer를 이용
나의 개인적인 취향 상 분석을 먼저 면밀하게 한 후 사용을 하는 것 보다는 실제 동작하는 모습을
보면서 연관된 코드를 뜯어보는 것이 수월하기에 이번 포스팅 역시 그러한 방식으로 진행을 하겠다.
Firmata의 전체적인 구성은 호스트 PC 쪽의 Firmata Client와 아두이노쪽의 Firmata 스케치로 구성되어
있다. 물론 아두이노쪽에서 범용 Firmata 스케치가 아닌 Firmata 라이브러리를 사용한다면 구성이 조금
달라지겠지만 이 시리즈에서는 범용 Firmata 스케치를 사용한다는 전제로 포스팅을 하고 있다.
이러한 전제 하에 지난 번 포스팅의 가장 마지막에 실행했던 digital_output.py 소스를 가지고 간단하게
Firmata의 흐름과 관련 소스를 살펴보도록 하겠다.
다만 모든 소스를 세세하게 분석하는데는 많은 시간과 지식이 필요하므로 이번 포스팅에서는 우선 프로토콜이
어떻게 구현되는 지에 초점을 맞춰 살펴보도록 하자.
Firmata Client - pymata4
이전 포스팅에서도 언급한 바와 같이 Firmata Client는 다양한 언어로 구현이 가능하다. 그리고 이 시리즈
에서는 Python으로 된 Client를 사용하고 있으며 그 중에서도 자료가 가장 잘 정리되어있는 pymata4를
대상으로 한다.
pymata4의 핵심 소스는 3개의 파일로 구성이 되어있다.
pin_data.py : 아두이노의 pin mode를 설정하기 위한 각종 속성이 정의되어 있다.
private_constants.py : Firmata 프로토콜을 정의하는 여러가지 상수가 정의되어 있다.
pymata4.py : 가장 핵심적인 소스로 사용자들인 호출하여 사용할 API들이 구현되어있다.
단순히 Firmata를 이용하기 위해서는 pymata4.py에 있는 API를 어떻게 호출하는 지만 알면 된다. 이제
예제 코드인 digital_output.py로부터 시작하여 차근차근 알아보자.
digital_output.py 코드는 매우 단순하다 (코드에 집중하기 위해 원래 있던 주석은 모두 삭제하였다).
사전 이해를 위해 간단하게 코드에 주석을 붙였다.
import sys
import time
from pymata4 import pymata4
# 사용할 디지털 핀 번호 설정. 기본 예제 코드에서는 6번을 사용한다.
DIGITAL_PIN = 6 # arduino pin number
# blink라는 함수를 구현하였다. 실제로 Firmata를 구동시키는 함수이다.
def blink(my_board, pin):
# 가장 먼저 PIN 모드를 설정한다. 파라미터로 전달받은 핀(6번 핀)을 출력 모드로 설정한다.
my_board.set_pin_mode_digital_output(pin)
# 1초 간격으로 6번 핀에 HIGH(1), LOW(0) 신호를 4회 반복하여 보낸다.
# 이 6번 핀에 LED를 연결하여 1초 간격으로 LED가 켜지고 꺼지는 것을 4회 반복한다.
# 직전 포스팅의 마지막 영상 참조.
for x in range(4):
print('ON')
# 아두이노 보드의 6번 핀에 HIGH(1) 신호를 보낸다. LED가 연결된 경우 LED가 켜진다.
my_board.digital_write(pin, 1)
time.sleep(1)
print('OFF')
# 아두이노 보드의 6번 핀에 LOW(0) 신호를 보낸다. LED가 연결된 경우 LED가 꺼진다.
my_board.digital_write(pin, 0)
time.sleep(1)
# 아두이노와의 연결을 끊는다.
my_board.shutdown()
# 아두이노 보드의 인스턴스를 생성한다.
board = pymata4.Pymata4()
try:
# blink 함수를 호출한다. 위에서 생성된 아두이노 보드 인스턴스와 사용할 핀 번호를
# 파라미터로 전달한다.
blink(board, DIGITAL_PIN)
except KeyboardInterrupt:
board.shutdown()
sys.exit(0)
이제 위의 코드를 차근차근 살펴보자.
보드(아두이노) 인스턴스 생성하기
pymata4의 편리한 점 중 하나는 아두이노를 자동으로 찾아준다는 것이다. 아두이노와 호스트 PC간에
정상적으로 연결만 되어있으면 단지 생성자 함수인 Pymata4()를 호출하는 것만으로 자동으로
_set_pin_mode 함수는 공통 함수이기 때문에 좀 더 복잡하게 구현이 되어있다. 하지만 우리는 Digital
핀을 OUTPUT으로 사용할 것이므로 관련 부분만 집중해서 보도록 하자.
우선 전체 코드는 다음과 같다. 원래의 주석은 삭제를 하고 간단게 관련된 부분에만 별도의 주석을 달았다.
# Python에서 class의 멤버 함수들은 반드시 첫 번째 파라미터가 self여야 한다.
# 하지만 함수를 호출할 때는 self는 생략한다. 따라서 앞서 set_pin_mode_digital_output
# 함수에서 전달한 파라미터는 pin_number와 pin_state 2개이다. 나머지 2개의 파라미터는
# 전달받지 않았으므로 기본 값이 사용된다. 즉, callback은 None, differential은 1이다.
def _set_pin_mode(self, pin_number, pin_state, callback=None,
differential=1):
# callback 파라미터는 전달받지 않았고 따라서 기본 값인 None을 사용하므로 아래 if문은
# 실행되지 않는다(None은 다른 언어의 null로 생각하면 된다).
if callback:
if pin_state == PrivateConstants.INPUT:
self.digital_pins[pin_number].cb = callback
elif pin_state == PrivateConstants.PULLUP:
self.digital_pins[pin_number].cb = callback
self.digital_pins[pin_number].pull_up = True
elif pin_state == PrivateConstants.ANALOG:
self.analog_pins[pin_number].cb = callback
self.analog_pins[pin_number].differential = differential
else:
print('{} {}'.format('set_pin_mode: callback ignored for '
'pin state:', pin_state))
#################################################################
# pin_state는 PrivateConstants.OUTPUT 값을 전달받았으므로 Pin_mode 역시
# PrivateConstants.OUTPUT 이다.
pin_mode = pin_state
# pin_mode가 PrivateConstants.OUTPUT 이므로 아래 if문도 실행되지 않는다.
if pin_mode == PrivateConstants.ANALOG:
pin_number = pin_number + self.first_analog_pin
#################################################################
# _send_command 함수를 호출하여 아두이노로 통신하기 전에 마지막으로 Firmata
# 프로토콜을 구현한다.
command = [PrivateConstants.SET_PIN_MODE, pin_number, pin_mode]
self._send_command(command)
# pin_mode가 PrivateConstants.OUTPUT 이므로 아래 if에서는 else절로 들어가
# 아무것도 실행하지 않고 끝낸다.
if pin_state == PrivateConstants.INPUT or pin_state == PrivateConstants.PULLUP:
self.enable_digital_reporting(pin_number)
else:
pass
위 소스 코드를 보면 대부분의 코드들은 파라미터 조건에 의해 실행되지 않거나 별다른 실행 없이 넘어가고
가장 핵심적인 부분인 Firmata 프로토콜을 구성하여 아두이노로 보내는 2줄만이 실행된다.
에서 보면 Message Type에 set pin mode(I/O) 라는 항목이 있고 이 항목은 다음과 같이 구성된다.
command
MIDI channel
first byte
second byte
0xF4
pin # (0~127)
pin mode
그럼 이제 private_constants.py 파일을 열어 이 과정에서 사용된 2개의 상수인 OUTPUT과 SET_PIN_MODE가
어떻게 정의되어 있는지 살펴보자.
SET_PIN_MODE = 0xF4 # set a pin to INPUT/OUTPUT/PWM/etc
...
OUTPUT = 0x01 # pin set as output
프로토콜로 정의된 것과 동일하게 command에 해당하는 SET_PIN_MODE는 0xF4 라는 값이 설정되어
있다. 나머지 2개의 파라미터는 범위 내에서 유동적으로 지정할 수 있으며 second byte에 해당하는
OUTPUT은 카테고리 성격으로 자주 사용되는 값이므로 별도로 상수로 지정해 놓은 것이다.
마지막 단계의 _send_command함수도 단순한데, 전달받은 파라미터(프로토콜 포맷)를 byte 배열로
바꾸어 serial port에 write(전송)하는 것이 끝이다. 전체 코드는 다음과 같다.
def _send_command(self, command):
# 전달받은 파라미터(Firmata의 프로토콜 규격에 맞춘 데이터)를 바이트 배열로 바꾼다.
send_message = bytes(command)
# ip_address 전역변수가 지정되지 않은 경우 if 절 내의 내용을 실행한다.
# 앞서 pymata4의 생성자 파라미터 설명에서 보았듯이 ip_address는
# StandardFirmataWifi라는 스케치에서만 사용되므로 여기서는 지정되지 않아
# if 절이 실행되는 것이다.
if not self.ip_address:
try:
# serial port에 write한다.
result = self.serial_port.write(send_message)
except SerialException:
if self.shutdown_on_exception:
self.shutdown()
raise RuntimeError('write fail in _send_command')
return result
else:
self.sock.sendall(send_message)
정리
코드와 함께 정리하다보니 포스팅이 너무 길어진데다가 blink 함수에서 호출되는 digital_write API는 조금
복잡한 내용을 담고 있어서 다음 포스팅으로 넘기는 것이 좋을 것 같다.
다시 한번 강조하지만 전체 소스를 세세하게 파헤치는 것이 목표가 아니라 Firmata라는 프로토콜과 관련
라이브러리들이 어떤 흐름을 가지고 작동하는지를 아는 것이 목적인 만큼 관련 부분만 주목해서 파악하면
될 것 같다. 하지만 소프트웨어라는 것이 어찌보면 상당히 유기적이기도 한 만큼 생략으로 인해 혼란 스러운
그 한 가지 방법은 아두이노의 스케치에서 Firmata 라이브러리를 include한 후 라이브러리의 API를
활용하는방법이고, 다른 한 가지는 아두이노에는 범용 스케치를 업로드하고 모든 코딩을 클라이언트
쪽에서 하는 방법이다.
우선 두 번째 방법인 범용 Firmata 스케치를 아두이노에 업로드한 후 클라이언트에서만 코딩을 하는 방법을
알아보도록 하겠다. 이 방법은 아두이노 스케치의 C 프로그래밍에 익숙하지 않은 개발자들이 클라이언트
(호스트 PC)에서 본인에게 익숙한 언어로 개발을 할 수 있다는 장점이 있다.
본격적으로 Firmata의 소스 코드를 알아보기 전에 우선 간단하게 개발 환경을 먼저 정리하고 진행하고자
한다. 다만 이 글에서는 매킨토시 환경 하에서 Python Client를 선택하여 진행하는 만큼 Windows 사용자나
Python이 아닌 다른 언어를 사용하시는 분들은 다른 곳을 참조하셔서 환경을 구성하는 것이 좋을 것 같다.
환경 구성하기1 - 아두이노에 FirmataExpress 설치하기
Firmata 기반의 개발을 하기 위해서는 클라이언트(호스트 PC)에서는 Firmata Client 라이브러리를 설치
하여야 하며 아두이노에는 범용 Firmata 스케치를 업로드하는 것으로 끝난다.
누누이 말하듯이 Firmata Client 라이브러리는 다양한 언어로 구현이 되어있다. 각자에게 익숙한 언어로 된
라이브러리를 선택하면 되지만 역시 가장 심플하게 구현되어있는 것은 Python으로 구현된 라이브러리이다.
나역시 Java와 iOS의 Obj-c가 주로 사용하는 개발 언어이지만 코드를 분석하고 구현하는데는 오히려
Python이 더 접근하기 쉬웠고 소스 파일 구성도 매우 단순하여 부담이 없었다.
일반적으로 Python 라이브러리 중에는 pyFirmata, 아두이노 범용 스케치로는 StandardFirmata를 많이
이용하는데 나는 자료가 좀 더 상세하게 정리되어있는 pymata4와 여기에 더 적합하다는 FirmataExpress를
사용하였다.
아두이노에서 기본적으로 제공하는 범용 Firmat 스케치 예제들이다.
사용하게 될 FirmataExpress는 기본 예제에 포함되어있지 않으므로 아두이노 스케치 IDE의 라이브러리
관리 메뉴로 들어가 검색한 후 설치해야 한다(나는 이미 설치한 후라서 Installed로 표시되고 있다).
1. 툴 > 라이브러리 관리... 메뉴를 선택한다.
2. 라이브러리 매니저 창에서 Firmata로 검색을 한 후 FirmataExpress를 찾아 설치를 진행한다.
3. 설치가 완료되면 파일 > 예제 > 사용자 지정 라이브러리의 예제 항목에서 FirmataExpress를 찾을 수 있다.
라이브러리가 설치된 후 파일 > 예제 > 사용자 지정 라이브러리의 예제 항목에서 FirmataExpress를 찾아
열고 아두이노에 업로드하면 아두이쪽의 준비는 끝이다.
환경 구성하기 2 - Python 개발환경 구성하기
다음은 클라이언트 쪽인데…일단 Python의 설치 및 환경설정에 대한 내용은 그 자체만으로 한 번의 포스팅으로
끝날 내용이 아니므로 여기서는 생략을 하겠다. 일단 PC에 Python이 설치되어 있다는 전제하에 진행을 하겠다.
이 부분이 어려운 경우 앞서 말한대로 각자에게 익숙한 언어로 구현된 라이브러리를 이용하면 된다.
일단 나는 PyCharm을 이용하여 학습을 진행하였다. PyCharm에서 Virtualenv로 프로젝트를 생성하여 코딩
하는 법에 대해서만 간략하게 설명하도록 하겠다 (현재 맥미니를 사용 중이라 매킨토시 기준으로 작성한다).
1. Pycharm의 File 메뉴를 선택한 후 New Project를 선택한다.
2. Create Project 창에서 제일 처음 Location은 그냥 두고 Project Interpriter… 항목에서 New environment using Virtualenv를 선택한다. Location 항목에 프로젝트를 생성할 경로를 입력하고 Base Interpreter는 시스템에 설치된 Python 중 하나를 선택한다.
3. 프로젝트를 생성할 시의 옵션으로 기존 프로젝트에 붙여넣을지, 새 창으로 열지, 현재 윈도우에서 열지를 물어보는 창. New Window로 선택하면 무난함.
4. 프로젝트가 생성되면 아래와 같이 창이 열린다.
5. Firmata를 사용하기 위해 필요한 라이브러리를 추가하기 위해 Preferences... 메뉴를 연다.
6. Project <프로젝트 이름> 항목을 선택한 후 Project Interpreter를 선택한다. 기본으로 설치된 pip와 setup tool 라이브러리가 보인다. 필요한 라이브러리를 추가하기 위해 하단 좌측의 + 버튼을 클릭한다.
7. + 버튼을 누르면 열리는 Available Packages 창에서 pymata4로 검색을 한다. 검색된 목록에서 pymata4를 선택하고 좌측 하단의 Install Package 버튼을 클릭하여 설치한다. 설치 시 의존성에 필요한 패키지를 모두 설치한다.
8. 설치를 하고 난 후 아래 화면과 같이 pymata4와 pyserial 2개의 패키지가 추가로 설치된 것을 확인할 수 있다. Firmata가 기본적으로 Serial 통신을 하기 때문에 의존 패키지로 pyserial이 함께 설치된다.
9. 이제 다시 프로젝트 화면으로 돌아가 프로젝트 이름에 마우스 우클릭을 하여 New > Python File을 선택하여 소스 코드를 입력할 Python 파일을 만든다.
10. 파일명을 입력하고 OK 버튼을 클릭하여 파일을 생성한다.
11. 일단 빠른 테스트를 위해 생성된 Python 파일에는 pymata4의 예제 중 digital_output.py 파일을 그대로 카피하여 붙여넣기 하였다.
12. 코딩된 Python 파일을 실행하기 위하여 Run 메뉴의 Run…항목을 클릭한다.
13. 코드가 실행되면서 아래의 영상과 같이 LED에 1초 간격으로 4번 불이 들어오고 프로그램이 종료된다. 하단의 Run 창에는 아래 화면과 같이 로그가 찍힌다. Auto-discovery complete. Found 22 Digital Pins and 8 Analog Pins까지는 pymata4 라이브러리에서 찍는 로그로 아두이노가 연결된 serial 포트를 찾고 아두이노 및 아두이노에 업로드된 Firmata에 대한 정보를 가져오는 내용이 출력된다.
정리
오늘은 간단하게 개발 환경 구성과 간단한 예제를 하나 돌려보았다.
본문에서 설명한 것과 같이 아두이노는 FirmataExpress 스케치를 업로드한 이후 더이상 할 것이 없다.
이제부터 모든 아두이노 제어는 호스트 PC에서 Python(물론 지원하는 타 언어를 이용해도 됨) 언어로
코딩하여 진행하게 된다.
다음 포스팅부터 pymata4와 FirmataExpress를 하나하나 뜯어보면서 어떻게 동작하는지에 대해
링크를 볼 수 있다. 또한 시리얼 통신만 가능하다면 PC 뿐만 아니라 모바일 기기에서도 아두이노를 제어할 수
있다.
3. 사용의 용이성
Firmata Client 프로그램들은 이 프로토콜을 손쉽게 이용할 수 있도록 API가 잘 구현되어있다. 따라서
적절한 API를 가져다 쓰기만 한다면 웬만한 아두이노 컨트롤은 손쉽게 수행할 수 있다. 게다가 아두이노에는
Firmata 라이브러리만 올려두면 추가적인 프로그래밍이나 스케치 업로드등을 할 필요도 없어 오직 호스트
PC(혹은 모바일 기기)에서만 개발자에게 익숙한 클라이언트로 프로그래밍하여 제어할 수 있다.
이렇게 Firmata는 아두이노를 활용하는데 많은 이점을 주는 라이브러리라고 할 수 있다.
왜 라즈베리파이와 아두이노를 연동하여 사용하는가?
이 질문은 사실 많은 고민을 하게 만들었다. 라즈베리파이도 이미 충분한 수의 GPIO 핀이 있는데 굳이
아두이노를 연결해서 사용하는 이유는 무엇일까? 라즈베리파이와 아두이노 연동, 시리얼 통신 등으로
검색을 해보면 많은 자료들이 검색되지만 정작 왜 연동을 하는지 그 목적에 대해 설명한 자료는 거의
없다. 그저 기술적인 호기심 때문만은 아닐텐데…
하지만 내 개인 프로젝트에 국한했을 때는 이러한 구성에 의문을 가질 수 있지만 일반적인 상황에서, 또는
아두이노를 중심에 둔다면 이러한 구성이 충분히 합리적이라는 것을 알 수 있다.
1. 원격 센서 제어
호스트에서 원격으로 어떤 센서로 측정된 값을 가져와 연산을 하여 처리한다고 했을 때 당연히 원격 센서는
아두이노를 사용하는 것이 가격적인 측면에서 합리적일 것이다. 라즈베리파이는 호스트로서 수신된 데이터를
연산하기만 하면 된다.
2. 하드웨어적인 차이
원격이 아닌 같은 모듈 내에서 사용을 하더라도 라즈베리파이와 아두이노의 하드웨어적인 차이에 의해 함께
연동하여 사용할 수 있을 것이다. 가장 큰 차이라면 역시나 GPIO인데 아두이노에는 라즈베리파이에 없는
Analog PIN이 별도로 있으며, 작동 전압도 라즈베리파이가 3.3v 인 반면 아두이노의 경우 3.3v와 5v용을
선택하여 사용할 수 있고, 핀당 사용 가능한 전류도 라즈베리파이가 16mA인 반면 아두이노는 40mA까지
사용할 수 있다. 이러한 특성상 비록 라즈베리파이에 40여개의 GPIO가 있지만 아두이노를 연동해서 사용
하는 것이 더 나은 경우도 있을 것이다.
하드웨어에 대한 정보들은 간단하게 아래 내용을 참고하면 될 것이다.
The operating voltage of the GPIO pins is 3.3v with a maximum current draw of 16mA. This means that we can safely power one or two LEDs (Light Emitting Diodes) from a single GPIO pin, via a resistor. But for anything requiring more current, a DC motor for example, we will need to use external components to ensure that we do not damage the GPIO.
컴퓨터 한 대의 메인 메모리에 들어갈 수 없을 정도로 큰 데이터 세트를 사용하는 경우 데이터의 일부를 읽어 들여 학습을 진행하는 방법을 전체 데이터가 모두 적용될 때까지 반복적으로 수행하는 방식. 보통 오프라인으로 수행되는 점진적 학습 방법 (기법 상으로는 온라인 학습에 속함)