반응형

이제부터는 본격적으로 Firmata를 분석해보고 사용해보도록 하자.

나의 개인적인 취향 상 분석을 먼저 면밀하게 한 후 사용을 하는 것 보다는 실제 동작하는 모습을

보면서 연관된 코드를 뜯어보는 것이 수월하기에 이번 포스팅 역시 그러한 방식으로 진행을 하겠다.

 

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()를 호출하는 것만으로 자동으로 

아두이노 보드를 찾아준다.

 

보드를 찾는 과정은 다음과 같은 로그로 확인할 수 있다.

 


/Volumes/Storage2/Firmata/FirmataTest/venv/bin/python /Volumes/Storage2/Firmata/FirmataTest/digital_write.py

pymata4: Version 1.10

 

Copyright (c) 2020 Alan Yorinks All Rights Reserved.

 

Opening all potential serial ports...

/dev/cu.wchusbserial1a1230

 

Waiting 4 seconds(arduino_wait) for Arduino devices to reset...

 

Searching for an Arduino configured with an arduino_instance = 1

Arduino compatible device found and connected to /dev/cu.wchusbserial1a1230

 

Retrieving Arduino Firmware ID...

Arduino Firmware ID: 1.1 FirmataExpress.ino

 

Retrieving analog map...

Auto-discovery complete. Found 22 Digital Pins and 8 Analog Pins


 

물론 명시적으로 필요한 파라미터를 전달하여 보드의 인스턴스를 생성할 수도 있다. 전달 가능한 파라미터는

다음과 같다(= 기호의 좌측은 파라미터 이름, 우측은 파라미터의 기본 값이다. Python은 파라미터 이름을 

사용하면 파라미터 순서와 관계없이 파라미터를 전달할 수 있으며 파라미터를 전달하지 않으면 = 우측의

기본 값이 사용된다).

 

  • com_port=None : 아두이노와 연결된 호스트PC의 포트. 위의 예에서는 /dev/cu.wchusbserial1a1230이다(매킨토시 PC에서 연결한 경우임).

  • baud_rate=115200 : 시리얼 통신 전송 속도

  • arduino_instance_id=1 : 아두이노 보드의 인스턴스 ID

  • arduino_wait=4 : 아두이노 리셋까지의 대기 시간

  • sleep_tune=0.000001 : 튜닝을 위한 파라미터로 일반적으로 수정하지 않는 것이 좋다.

  • shutdown_on_exception=True : RuntimeError Exception이나 KeyboardInterrupt exception이 발생한 경우 shutdown 함수를 호출할지의 여부

  • ip_address=None : StandardFirmataWifi 스케치를 사용하는 경우 Wi-Fi 기기의 IP 주소

  • ip_port=None : StandardFirmataWifi 스케치를 사용하는 경우 Wi-Fi 기기의 포트 번호. 보통 3030 사용

 

이와 같은 많은 파라미터들이 있으나 아무런 파라미터를 전달하지 않더라도 pymata4가 자동으로 연결된 

보드를 찾아 주는 것이다.

 

이 생성자 함수는 pymata4에서 사용할 전역 변수들을 초기화 하고 실제로 보드가 연결된 시리얼 포트를

찾는 것까지 상당히 긴 코드로 구현이 되어있어 자세한 설명은 생략하도록 하겠다. 다만 특별한 경우가 

아니라면  board = pymata4.Pymata4() 코드 한 줄로 아두이노를 연결하여 사용할 수 있다는 것만

알아두도록 하자.


blink 함수 분석 - 핀 모드 설정

Firmata를 이용하여 호스트 PC에서 아두이노의 핀을 이용하기 위해서는 먼저 아두이노 핀 모드를 설정해

주어야 한다. 그래서 가장 먼저 호출되는 API가 my_board.set_pin_mode_digital_output(pin)

이다. API 함수 이름으로 짐작할 수 있듯이 파라미터로 전달되는 pin(여기서는 6번 핀)을 디지털 출력으로 

설정하겠다는 의미이다. 그 과정을 조금 더 자세하게 살펴보자.

 

pymata4.py에서 set_pin_mode_digital_output 함수는 매우 단순하게 구현되어있다. 단지 전달

받은 파라미터인 pin에 private_constants.py에 정의된 상수인 OUPUT을 파라미터로 추가하여 private

함수인 _set_pin_mode 함수를 호출하는 것이 다이다. 전체 코드는 아래와 같다(원래의 주석은 삭제

했다).

 

def set_pin_mode_digital_output(self, pin_number):
        self._set_pin_mode(pin_number, PrivateConstants.OUTPUT)

 

_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줄만이 실행된다.

 

command = [PrivateConstants.SET_PIN_MODE, pin_number, pin_mode]
self._send_command(command)

 

Firmata 프로토콜 정의 문서(protocol/protocol.md at master · firmata/protocol · GitHub)

에서 보면 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_MODE0xF4 라는 값이 설정되어

있다. 나머지 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라는 프로토콜과 관련

라이브러리들이 어떤 흐름을 가지고 작동하는지를 아는 것이 목적인 만큼 관련 부분만 주목해서 파악하면

될 것 같다. 하지만 소프트웨어라는 것이 어찌보면 상당히 유기적이기도 한 만큼 생략으로 인해 혼란 스러운

부분도 많을 것이다. 이러한 부분들은 추후 기회를 봐서 더 상세히 다뤄보도록 하겠다.

 

그럼 다음 포스팅에서 digital_write API에 대한 내용으로 이어가겠다.

 

반응형

 

Firmata는 2가지 방식으로 사용이 가능하다.

 

그 한 가지 방법은 아두이노의 스케치에서 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를 하나하나 뜯어보면서 어떻게 동작하는지에 대해 

살펴보도록 하자.

 

반응형

 

 

지난 시간 Firmata에 대해 포스팅한 후 많은 분들이 관심을 가져주시고 또한 의문을 제기해주셨다.

그중에 가장 중요한 것이 바로 다음의 두 가지였다.

 

  1.  왜 Firmata를 사용하는가?

  2. 왜 라즈베리파이와 아두이노를 연동하여 사용하는가?

 

이런 질문을 받고 나니 확실히 어떤 목적으로, 어떤 이점이 있기에 Firmata를 사용하고 라즈베리파이와

아두이노를 연동하려고 했는지 다시 한번 고민해보게 되었다. 아무리 작고 개인적인 프로젝트라 하더라도

역시나 대충, 어물쩡, 설렁설렁 하면 안되겠다고 새삼 깨달았다. 오늘은 위 두 질문에 대한 답을 찾는

것으로 시작을 해보고자 한다.

 

왜 Firmata를 사용하는가?

 

어찌보면 이 질문의 답은 명확하다. 대략 세 가지 정도로 추려보자면

 

1. 검증받은 프로토콜의 사용

지난 시간에도 언급했듯이 디지털 기기간에 통신을 하기 위해서는 프로토콜이라는 규약을 만들고 지킬 

필요가 있다. 하지만 프로토콜을 설계하고 구현한다는 것이 그리 만만한 작업은 아니다. 때문에 이미

검증된 MIDI Message Format을 이용하여 안정적으로 구현된 프로토콜을 사용할 수 있다는 것은

큰 이점이 될 것이다.

 

2. 다양한 기기 및 언어 이용

프로토콜이 명확하게 정의되어있기 때문에 이 프로토콜 구현만 제대로 한다면 어떤 언어로든 아두이노를

제어할 수 있다. Firmata Protocol git 페이지에 가보면 다양한 언어로 구현된 Firmata 클라이언트

링크를 볼 수 있다. 또한 시리얼 통신만 가능하다면 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. 

-

출처 : https://www.tomshardware.com/reviews/raspberry-pi-gpio-pinout,6122.html

 

아두이노 우노의 사양

 

위의 내용을 감안해 본다면 분명히 라즈베리파이와 아두이노를 연동하여 사용해야 하는 것이 더 유용한 경우도 

있을 것이다. 

 

Firmata의 한계

 

하지만 100% 좋은 것이 어디 있으랴.

내 구미에 맞게 커스터마이징해야 하는 경우라면 Firmata는 결코 답이 아니다. 사실 모든 라이브러리나 

프레임워크가 다 동전의 양면이 아니던가? 쉽고 편하게 사용하기 위해 적용은 하지만 정작 중요하게 

커스터마이징해야 할 때 상당히 곤란한 상황에 직면하는 경우가 적지 않다.

 

지난 포스팅에서도 언급했듯이 나역시 PCA9685 PWM 서보모터 드라이버를 Firmata 상에서 사용하려고

했으나 이와 관련된 프로토콜은 정의되어있지 않기 때문에 대안을 찾기 위해 열심히 소스 코드를 뜯어보고 

있다…ㅠ.ㅠ 해결책에 한걸음 다가갔나 싶으면 비트 연산들이 줄줄이 나와 java 프로그래밍을 위주로 해온

나에게는 결코 쉽지 않은 작업이다…ㅠ.ㅠ

 

이 부분은 글을 이어 나가면서 극복해보도록 하겠다. 

 

정리

 

원래 이번 포스팅부터 코드 분석을 들어갈 예정이었으나 위의 질문들은 프로젝트를 수행하는데 있어서 분명

중요한 시사점이 있기 때문에 집고 넘어가는 것이 좋겠다고 생각했다. 그리고 이 글을 정리하기 위해 적잖이

검색을 하고 자료를 찾아보았지만 위와같은 근본적인 질문에 대한 명쾌한 답변은 찾기가 쉽지 않았다.

 

결국 몇몇 글들과 나름의 지식을 기반으로 글을 적기는 했지만 아직도 객관적인고 명확한 근거는 모자라지 

않나 하는 아쉬움이 있다. 이 부분은 앞으로의 숙제가 될 것 같다.

 

이제 예정한대로 다음 포스팅 부터는 Firmata Client와 라이브러리 그리고 예제 코드를 통해 Firmata에 

대해 조금 더 구체적으로 알아보도록 하겠다.

반응형

로봇을 제작하면서 전자부의 구성은 라즈베리파이 + 아두이노를 사용하기로 계획했다.

일단 사양과 활용도가 높은 라즈베리파이로 각종 데이터 분석과 추후 AI를 위한기능들을 구현하고

실제 구동부의 동작은 아두이노로 제어를 하는 구조다. 물론 요즘 새로 출시된 아두이노들은 웬만한

ML 처리는 가능하지만 아무래도 활용도라든지 관리 차원에서는 라즈베리파이가 조금 더 접근하기

쉬운 것은 사실이다.

 

게다가 일반적인 아두이노 호환 컨트롤러들은 워낙에 저가여서 모터나 센서 등을 조합해서 다양한

구동부를 만들고 이것을 라즈베리파이와 연동한다면 다양한 형태의 로봇이나 차량 혹은 드론을

만들 수 있을 것이라 생각했다. 그래서 프로젝트 이름도 MORS(MOdular Robot System)이라고

지었다.

 

계획은 세웠으니 이제 구현만 하면 되는데…라즈베리파이와 아두이노를 어떻게 연결하는 것이 좋을까?

아직 한번도 해본 적이 없는 구성이라 일단 열심히 검색을 해보았다.

 

일단 가장 기본적으로 알 수 있는 방법은 우리가 PC에서 아두이노 스케치를 개발하듯이 라즈베리파이를

호스트 PC로 만들고 거기서 스케치를 코딩하고 아두이노에 업로드하여 동작하도록 하는 것이다. 하지만

이 방식으로는 아두이노의 PIN에 대해 라즈베리파이가 직접 컨트롤 할 수 없다는 문제가 있다. 즉, 라즈베리

파이가 사용자의 신호를 받았을 때 그 신호를 실시간으로 아두이노에게 전달해야 하는데 이 방식으로는

그것이 불가능하다. 

 

사실상 라즈베리파이와 아두이노가 따로 노는 상황

 

그렇다면 라즈베리파이와 아두이노의 역할을 아예 분리하는 방식을 생각해볼 수도 있다. 라즈베리파이는

부하가 큰 데이터나 ML 처리의 연산만 담당하고 아두이노는 모터나 센서등의 동작을 담당하는 식이다.

하지만 이렇게 되면 사용자가 라즈베리파이와 아두이노를 이원화 하여 컨트롤해야 하고 또 다수의 아두이노

기반 구동부를 연결하는 경우 구조가 매우 복잡해진다(사실 첫 번째 경우와 동일한 케이스인데 다만

아두이노쪽에 사용자와 통신할 수 있는 모듈(ESP, 블루투스, nRF 등)을 추가하여 아두이노를 별도로

제어한다는 점이 다를 뿐이다).

 

쌍둥이를 키우는(?) 입장이랄까?

 

그러다 찾아낸 것이 바로 Firmata이다. 앞으로 얼마간 이 Firmata에 대해 알아보고자 한다.

 

바로 이거지~

 

이 글은 비록 개발자로 일하고 있지만 태생이 문돌이인 블로그 주인의 글이니 보다 정확한 자료를 원하시는

분들은 아래 링크로 이동하여 참고하시면 되겠다. 다만 영어의 압박은 감수하셔야…

 

Firmata 문서 Git : GitHub - firmata/protocol: Documentation of the Firmata protocol.

 

Firmata는 프로토콜이다.

 

프로토콜은 잘 아시다시피 하나의 규약이다. 통신을 할 때 지켜야 할 약속으로 우리가 가장 잘 알고있는

프로토콜은 바로 워키토키를 이용하여 통신을 할 때 자신이 할 말을 마쳤으니 상대방이 말하라고 하는

의미의 “오버”를 들 수 있겠다.

 

하지만 디지털 장치로 그 영역을 옮겨오면 그렇게 단순하지는 않다. 위의 예도 사람의 입장에서 적으니

단순하게 보일 뿐 실제 워키토키가 처리하는 것은 디지털화 된 음성 데이터를 두 대의 워키토키 사이에

전달하기 위해 채널을 확인해야 하고 데이터의 순서나 손실 여부를 체크해야 한다. 이 역시 중요한 부분만

언급했을 뿐 실제로는 더 많은 내용이 오고 갈 것이다.

 

아주 간단한 프로토콜

 

이렇게 디지털 기기에서 양자간에 데이터를 주고받기 위한 규칙을 프로토콜이라고 한다. 우리가 잘

아는 TCP/IP, SMTP, HTTP, FTP 등이 모두 프로토콜이며 각각의 목적에 따라 그 규칙이 모두 다르다.

지금 공부하고자 하는 Firmata 역시 이러한 프로토콜이며 Firmata의 목적은 호스트 컴퓨터와 마이크로

컨트롤러간에 직접 통신을 할 수 있도록 하기 위한 것이다.

 

Firmata는 midi message format을 기반으로 만들어졌다고 하는데 일단 midi message format이

무엇인지까지 파고드는 것은 범위가 너무 넓어지므로 관련 링크만 걸어두겠다.

 

Official MIDI Specifications : Specs

 

여기서 midi란 우리가 잘 알고 있는 디지털 악기가 맞다(Musical Instrument Digital Interface)

 

Firmata의 구성 - 소프트웨어 관점에서

 

Firmata는 호스트 PC쪽의 Firmata Client와 마이크로컨트롤러쪽의 Firmata 라이브러리로 구성

되어 있다. 

 

Firmata Client의 경우 웬만한 언어는 모두 사용 가능한데 대표적으로 Python, java, javascript로부터

iOS, Android까지 모두 사용 가능하다. 또한 각 언어로 구현된 클라이언트도 각각의 특성에 따라 여러

버전이 있어 필요에 따라 선택하여 사용 가능하다.

 

아두이노 라이브러리는 말 그대로 아두이노에서 사용 가능한 라이브러리 형태로 되어있으며 가장 기본적인

라이브러리는 아두이노 스케치의 예제에 포함이 되어있다. 파일 > 예제 > Firmata 항목으로 들어가면 

다양한 Firmata 목록을 볼 수 있는데 가장 기본적인 것은 StandardFirmata이다.

 

아두이노 스케치에서 Firmata 라이브러리 불러오기

 

아두이노에 Firmata 스케치를 업로드하고 클라이언트에서 API를 호출하면 Firmata 프로토콜을 통해

아두이노의 해당 API가 호출되는 식이다(자세한 것은 다음에 소스 분석을 통해 알아보겠다).

 

Firmata의 구성 - 프로토콜 관점에서

 

앞서도 이야기 했듯이 프로토콜이란 통신을 위해 지켜야 할 일종의 약속이다. 즉 FirmataClient에서

약속된 형태의 데이터를 전달해야 아두이노의 Firmara 라이브러리에서 데이터를 받아들이고 처리할 수

있다. 이러한 약속들은 아래 문서에 정의되어 있다.

 

Firmata protocol : protocol/protocol.md at master · firmata/protocol · GitHub

 

 

문돌이들의 경우 몇몇 개념이 헷갈릴 수 있으나 이 약속을 지키기 위한 대부분의 처리는 이미 API로 모두

구현이 되어있으며 다양한 샘플도 존재하니 그저 API를 호출해서 사용하면 된다. 조금 더 이해의 폭을

넓히자면 이 약속이라는 것이 그저 API 호출 시 넘겨주어야 할 파라미터를 정의하는 것이라 생각하면

조금 더 이해가 쉬울 것이다.

 

예를들어 위 링크된 문서의 Message Type 중 set digital pin value라는 항목을 살펴보자

테이블을 보면 우선 command 항목은 0xF5이다. 즉, 아두이노의 특정 디지털 핀에 값을 전달하기 

위해서는 0xF5라는 값으로 시작하는 데이터를 보내야 한다. 다음 열은 비어있고 세 번째 열에

1~127 사이의 핀 번호를 입력해야 하고 마지막으로 1 또는 0으로 해당 핀에 전달할 값을 지정해야

한다. 만일 9번 핀에 디지털 신호를 보내기 위해서는 0xF5, 9, 1 또는 0xF5, 9, 0으로 구성된 3개의

데이터를 보내면 되는데 command에 해당하는 값들은 상수로 지정하여 API에서 처리를 해주므로

실제 API를 호출할 때는 command를 제외한 9, 1 또는 9, 0만을 파라미터로 하여 다음과 같이

보내면 된다.

 

digital_pin_write(pin, 1) 
digital_pin_write(pin, 0)

 

이 중에서 특히 중요한 것이 Sysex Message Format인데 이 부분은 다음 포스팅에서 다뤄보기로 하겠다.

 

정리

 

사실 Firmata에 대해 굳이 이렇게까지 알지 않아도 대충 예제를 보면서 구현하는 것은 그리 어렵지 않다.

그런데 로봇을 만들면서 서보모터를 컨트롤하는데 I2C 기반으로 통신을 하는 PCA9685 PWM 서보모터

드라이버를 사용하려고 보니 아두이노에서 전용 라이브러리를 사용해야 하고 Firmata에서는 이러한 특정

라아브러리에 대한 message format까지 모두 지정해놓은 것은 아니라서 현재로서는 사용이 어렵다.

결국 어떻게 커스터마이징 좀 안될까 하고 조금 더 파고들어가 보기로 한 것이다.

 

물론 다른 방법으로는 I2C에 대한 message format은 정의가 되어있으므로 관련 API를 이용해서 제어하는

방법도 있을 것 같은데 그 또한 그리 만만하지는 않을 것 같아서 우선은 Firmata쪽을 보기로 하였다. 만일

이 시도가 실패한다면 아마도 I2C를 이용한 PCA9685 제어에 도전하게 될지도 모르겠다.

 

다음 포스팅에서는 Sysex Message Format에 대해 알아보고 본격적으로 API와 샘플코드에 대한 분석을

진행해보고자 한다. 

 

#블로그/로봇개발

반응형

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요

2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS(4족보행로봇) 프로젝트 #2 Restart

2020/05/30 - [로봇] - 인생 프로젝트 시작 - MORS(4족보행로봇) 프로젝트 #3 Modeling

2020/07/28 - [로봇] - 인생 프로젝트 시작 - MORS(4족보행로봇) 프로젝트 #4 다리 제작

2020/08/16 - [로봇] - 인생 프로젝트 시작 - MORS(4족보행로봇) 프로젝트 #5 조립 완료… 그러나…

 

지난 포스팅으로부터 꽤 많은 시간이 흘렀다. 역시나 3D 프린팅에서 많은 시간을 잡아먹었고 게다가 공차를

고려하지 않아서 재출력을 하거나 조립 중 부품이 파손되어 재출력을 하는 경우가 다수 발생하다보니 더 만은

시간이 걸린 듯하다.

 

몸통 외장 장갑 뿐만 아니라 다리 장갑도 모두 출력되어 입혀야 하는데 워낙 작업 공간이 부족하다보니 일단

기존 작업한 것들을 대충이라도 정리를 해 놔야 조립 마무리를 할 수 있을 것 같다….ㅠ.

 

요것이 나의 작업대…ㅠ.ㅠ (사진 속의 로봇은 4년 전 만들었던 로봇)

 

사실 몸통부에도 꽤 많은 기믹을 구현할 예정이었으나 계획적인 설계 후 만든 것이 아니고 그때그때 주먹구구

식으로 만들다보니 공간의 부족, 기구부간의 충돌 등으로 제대로 구현하지 못했고 겨우겨우 측면과 측면의 

상하단만 열렸다 닫혔다 하는 정도로 구현해보았다.

 

이래 저래 자세한 기록을 남길만한 내용도 없고 해서 오늘은 그냥 쉬어가는 의미로 몇 개의 동영상과 다수의 

사진을 갤러리 처럼 모아봤다. 가볍게 보아주셨으면 한다~

 


 

 

 

유독 예만 유튜브 링크가 안먹어서 티스토리 동영상으로 올림...-.-

 

 

 


 

설계 미스의 참사들...첫 번째 사진은 튀어나온 부분에 렌치볼트 대가리가 걸려서 볼트를 박을 수가 없음...-.-

두 번째 사진은 검은 렌치볼트 위치를 생각못하여 우측의 그릴을 씌우지 못했음...ㅠ.ㅠ

 

 

 


정리

전체적으로 아직 촬영이 서툴다보니 부족한 부분이 많다. 게다가 3D 출력물들도 따로 다듬지를 않다보니 거미줄도

많이 보이고...어찌보면 무슨 유령선 같기도 하고...암튼 여러모로 부족하다...ㅠ.ㅠ

 

일단 기구부는 모두 완성을 했다. 다만 앞서 말한대로 다리 부분의 장갑은 아직 입히지 않은 상태이고 다리와 몸통

또한 아직 연결 전이다. 기구부가 마무리되면 본격적으로 전자부의 구성과 드디어 프로그래밍의 시작이다.

 

프로그래밍이 시작되면 다시 본격적으로 과정을 공유하도록 하겠다. 일단 간단하게 라즈베리파이와 아두이노

그리고 서보모터 드라이버와 스텝다운 컨버터 등을 배치한 사진으로 오늘 포스팅은 마무리 한다(책상 위의 먼지는

양해를...ㅠ.ㅠ).

 

 

 

반응형

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요

 

인생 프로젝트 시작 - MORS 프로젝트 #1 개요

2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart 인생 프로젝트 시작 - MORS 프로젝트 #1 개요를 작성한 것이 19년 8월 5일이었다. 그리고..

mazdah.tistory.com

2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart

 

인생 프로젝트 시작 - MORS 프로젝트 #2 Restart

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 내가 아두이노를 처음 접한 것이 2016년 5월 이었다. 그리고 내 인생에는 새로운 세상이 열

mazdah.tistory.com

2020/05/30 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #3 Modeling

 

인생 프로젝트 시작 - MORS 프로젝트 #3 Modeling

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 내가 아두이노를 처음 접한 것이 2016년 5월 이었다. 그리고 내 인생에는 새로운 세상이 열

mazdah.tistory.com

2020/07/28 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #4 다리 제작

 

인생 프로젝트 시작 - MORS 프로젝트 #4 다리 제작

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart 인생 프로젝트 시작 - M..

mazdah.tistory.com

 

 

이 프로젝트를 RESTART한 것이 올해 5월 17일… 거의 3개월 전이다.

그동안 상당히 많은 부품을 모델링하고 출력하고…또 일부는 출력대행을 맡기고…

테스트를 위해 조립하고… 잘못된 설계를 바로잡고… 다시 출력하고…

 

최근에는 3D 프린터의 유해성(정확히는 3D 프린팅 과정에서의 유해물질 배출) 논란으로 인해

심리적 부담까지 가지면서 나름 열심히 달려왔다. 출력 대행을 맡긴 2개의 부품도 사실은 바로 

이 유해성 논란이 찜찜해서 가장 출력 시간이 긴 2개를 맡긴 것이었다. 그 이후 미봉책으로 소형

공기청정기를 3D 프린터 옆에 두고 프린팅 할 때마다 켜놓고 있기도 하다.

 

아무튼 오랜 시간과 노력을 기울여 드디어 모든 부품이 출력 완료 되었고 이제 조립을 하는 일만

마무리하면 절반 이상은 끝나는 것과 다름 없다.

 

출력된 부품들과 간단한 조립 과정

 

출력된 부품은 대략 100여개이며 출력 후 공차가 맞지 않아 다시 출력한 것들만도 10여개는 될

것같다. 또한 3D 프린팅이 아닌 기성품을 사용한 것으로 서보모터 외에 RC용 링크 로드와 서스펜션을

사용하였다. 아래 사진은 일부 테스트를 위해 조립된 것을 포함한 부품들이고 출력 대행을 맡긴

몸체 2파트는 빠진 부품들이다.

 

 

그리고 출력 대행을 맡긴 부품들은 몸통을 구성할 부품으로 아래와 같다. 상단부는 무게 감소와

다양한 장치를 부착할 수 있도록 트러스 구조 비슷하게 만들어보았다. 구조가 이렇다보니 출력대행을

맡길 때에도 아예 출력을 못하겠다는 곳도 있었고 수 십 만원의 견적을 내는 곳도 있었는데 다행히

좋은 분을 만나 괜찮은 품질로 전달을 받았다(구조상 서포터가 붙은 아래쪽의 품질은 포기할 수밖에

없는 상황이었다).

 

 

애초에 크기가 예상보다 많이 커졌다는 것은 짐작하고 있었지만 모든 부품을 받아보고 나니 더욱

실감이 날 수밖에 없었다…-.-

 

조립은 테스트용 다리 하나가 이미 만들어진 상태에서 동일한 부품들을 조립하는 것이라 초반 진행은

그리 어렵지 않았다. 다만 처음부터 끝까지 힘들게 한 것은 아슬아슬하게 공차가 맞지 않는 부분들을

줄로 갈아내고 드릴로 뚫고 하는 부분이 어려웠고 손잡이가 따로 없는 육각렌치로 작은 나사들을

조이다보니 오른손 엄지와 검지가 고생을 좀 많이 했다…ㅠ.ㅠ

 

 

이렇게 각각의 부분들이 조립된 상태가 아래 사진과 같다.

 

 

조립 완료…그러나…

 

드디어 조립이 완료되었다! 전체 부품이 확보된 이후 조립 완료까지는 하루에 3~4시간 씩 대략 2일 정도가 걸려

그리 오래 걸리진 않았다.

 

 

그래도 제대로 된 설계 없이 하나의 부품을 디자인하고 거기에 연결되는 다음 부품을 디자인하는 식으로 두서

없이 디자인 한 것치고는 제법 그럴싸한 비주얼이었다. 다만 여러차례 언급했듯이 무식하게 커진 크기와 그 

크기에 걸맞는 어마어마한 무게가 걱정스러울 뿐… 무게는 대충 따져도 3Kg을 넘는 무게였다(지난 포스팅

내용 중 다리 하나의 무게가 대략 580g이라는 내용을 적은 적이 있다).

 

포스팅을 공유했을 때 많은 분들이 현재 사용하고 있는 SG90 서보 모터로는 토크가 부족할 것이라고 우려를

해주셨는데 역시나 아무리 각 다리별로 2개씩 총 8개의 서보모터가 지지를 한다고 해도 SG90으로는 택도 

없었다. 결국 로봇은 주저앉았고 한쪽 다리에서는 서보모터 지지대까지 파손되었다…ㅠ.ㅠ

 

 

결국 고관절쪽 서보모터들은 좀 더 상급의 모터로 교체를 할 수밖에 없는 상황이 되었고 그나마 다행인 것은 

새로운 서보모터 브라켓 정도만 새로 모델링하면 큰 수정 없이 보완이 가능하다는 것이다.

 

정리

 

아마도 내가 공돌이 출신이라면 이지경이 되기 전에 보완을 했으련만…결국은 맛을 봐야 똥인지 된장인지

구분할 수 있는 어설픈 실력으로 너무 무리한 프로젝트를 진행한 것은 아닌가 싶은 생각마저 든다.

 

하지만 불행중 다행으로 아무생각 없이 나중에 쓸 곳이 있겠지 하며 뚫어 놓은 나사 구멍 덕분에 보완하는

데는 큰 무리가 없을 것 같아 얼마나 다행인지 모른다.

 

 

일단 체력 소진으로 당장은 진행도 못하겠거니와 새로운 모터 사이즈에 맞는 브라켓을 모델링 해야 하기 

때문에 로봇은 다시 일부 분해하여 수면 모드로 작업 책상(나의 사랑스러운 토마스 책상~)에 올려놓았다.

어서 기력을 회복하여 다음 작업을 진행해야겠다.

 

  1. 2020.10.10 14:47

    비밀댓글입니다

    • 2020.10.11 13:57

      비밀댓글입니다

반응형

 

2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart

 

인생 프로젝트 시작 - MORS 프로젝트 #2 Restart

인생 프로젝트 시작 - MORS 프로젝트 #1 개요를 작성한 것이 19년 8월 5일이었다. 그리고 어언 9개월이 넘게 지나버렸다. 회사일과 가정사로 이래저래 바쁜 나날로 손을 대지는 못했지만 머릿속에서

mazdah.tistory.com

2020/05/30 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #3 Modeling

 

인생 프로젝트 시작 - MORS 프로젝트 #3 Modeling

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 내가 아두이노를 처음 접한 것이 2016년 5월 이었다. 그리고 내 인생에는 새로운 세상이 열

mazdah.tistory.com

https://mazdah.tistory.com/865

 

인생 프로젝트 시작 - MORS 프로젝트 #4 다리 제작

2019/08/05 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #1 개요 2020/05/17 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #2 Restart 2020/05/30 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #3 Model..

mazdah.tistory.com

2020/08/16 - [로봇] - 인생 프로젝트 시작 - MORS 프로젝트 #5 조립 완료… 그러나…

 

인생 프로젝트 시작 - MORS 프로젝트 #5 조립 완료… 그러나…

이 프로젝트를 RESTART한 것이 올해 5월 17일… 거의 3개월 전이다. 그동안 상당히 많은 부품을 모델링하고 출력하고…또 일부는 출력대행을 맡기고… 테스트를 위해 조립하고… 잘못된 설계를 바�

mazdah.tistory.com

 

내가 아두이노를 처음 접한 것이 2016년 5월 이었다. 그리고 내 인생에는 새로운 세상이 열렸고, 나의 학문적
정체성이 그 실체를 드러냈다. 나는 문돌이의 탈을 쓴 공돌이였음을 커밍아웃 할 수밖에 없었다.

 

처음 아두이노를 접한 후 지금까지 꽤나 굵직한 개인 프로젝트를 많이 시도하였다. 하지만 안타깝게도 그나마 성공
했다고 할만한 프로젝트는 2018년 2월달에 만들었던 온도/습도/먼지 측정기 정도이고 그 외에는 모두 끝을 보지
못했다. 대략 다음과 같은 시도들이 있었다.

 

  1. 2016년 6월 ~ 2017년 1월 : 4족 보행 로봇 만들기

  2. 2017년 7월 ~ 2017년 10월 : 싱글콥터 만들기

  3. 2019년 1월 ~ 현재 : 미니 드론 만들기

싱글콥터 만들기를 진행할 때까지는 3D 프린터도 없어서 온갖 잡동사니를 사다가 자르고 붙이고 하면서 참 고생도
많이 했다. 물론 3D 프린터를 마련한 후에도 모자라는 모델링 실력에 사실상 직접 출력해서 쓴 것보다 출력 대행을
맡긴 것이 더 많긴 하지만…ㅠ.ㅠ

 

아무튼 이렇게 진행한 프로젝트들이 비록 그 끝은 보지 못했지만 아마도 이제 시작할 인생 프로젝트의 밑거름이 
되려고 존재했던 것이 아닌 가 하는 생각이 든다. 바로 그런 실패를 통해 오늘부터 시작할 이 프로젝트의 
아이디어가 탄생한 것이나 다름 없으니 말이다(매우 강한 아전인수인가…-.-)

 

물론 이제 시작할 이 프로젝트의 컨셉이나 아이디어가 전혀 새로운 것은 아니다. 하지만 몇몇 측면에서는 분명 
조금은 새로운 시도라 할 수 있을 것 같다(진정한 판단은 이 글을 읽는 분들이 해주시길…^^).

 

프로젝트의 명칭 - MORS

이 프로젝트의 명칭은 MORS이다. MOdular Robot System의 약어이다. 뭔가 그럴듯한 명칭을 생각하고자
했지만 약어를 만들고 보니…Mors란 영어권에서는 “죽음”, “죽음의 신”, 라틴어로는 “죽음”, ”시체”, “끝”, “파멸”
등 굉장히 불길한 의미를 지니고 있었다. 그나마 독일어에서는 “신화”라는 의미가 있으며 라틴어 숙어인 Mors 
sola는 “죽음이 갈라놓을 때까지”라는 조금은 애절한 의미가 있었다. 이 중 쓸만한 것은 역시 독일어의 “신화”가
아닐까. “신화가 될 프로젝트”라…나름 괜찮네…

 

하지만 역시 중요한 것은 풀네임이 의미하는 바이다. 바로 “모듈형 로봇 시스템”이라는 개념 말이다.

 

Concept

핵심 컨셉은 바로 모듈의 “사용자화”와 “공유”이다. 즉, 사용자가 모듈을 직접 만들고 이 것을 오픈소스화 하는
것이다.

 

사실 “모듈형”이라는 개념은 하나도 새로울 것이 없다. 이미 많은 로봇들이 모듈형으로 제작되고 있다. 특히나 
교육용 로봇들의 다수가 모듈형으로 만들어지고 있다. 따라서 “모듈형”이란 개념은 어찌보면 오히려 상투적인 
개념에 더 가깝게 느껴지기까지 한다.

 

다양한 모듈형 로봇들

 

하지만 이런 로봇들은 대체로 모듈들이 ready-made로 이미 생산 당시 각 모듈에 부여된 기능을 활용하는
정도로 확장성은 그리 높지 않다는 아쉬움이 있다. MORS는 바로 이점에 착안하여 모듈들을 사용자가 직접
만들어 활용할 수 있도록하고 또 이렇게 만들어진 모듈을 오픈소스화 하는 것이 주 목적이다.

 

물론 이렇게 할 경우 사용자의 기술적 이해도나 숙련도가 더 높아지는 장애가 발생을 한다. 아두이노에 대한
지식 뿐만 아니라 BASE에서 모듈을 제어하기 위한 프로그래밍도 필요하기 때문에 어느 정도의 프로그래밍
지식도 필요로 하게 될 것이다.

 

디자인

MORS의 핵심은 라즈베리파이가 장착된 BASE이다. 아래의 그림들은 BASE의 구성도이며 다양한 모듈을
다양한 형태로 연결 가능하도록 하기 위해 8각형의 형태로 만들었다.

 

이 BASE는 2개의 레이어로 구성이 되어있으며(물론 추후 확장될 수도 있다), 각각의 레이어는 다음과 같이
구성되어있다.

 

레이어 1

우선 앞서 말한대로 BASE에는 라즈베리파이가 1대 장착되어있고 모듈을 USB로 연결하게 될 경우 기본
4개의 USB 포트를 확장하기 위해 USB 확장 Hat 2개를 배치하였고 모듈을 I2C 방식으로 연결하거나
혹은 모듈에 라즈베리파이를 통해 전원을 공급할 경우를 위해 소형 bread board를 올렸다.

 

 

레이어2

두번째 레이어에는 전원과 관련된 부품들이 배치가 되는데 기본적으로 배터리가 장착이 되고 또 모듈쪽
부품들이 다양한 전압을 사용할 수 있도록 2개의 step-down converter를 두어 2종류의 전압(기본은
6V와 12V)을 사용할 수 있게 하고 다수의 모듈에 전원을 공급하기 위해 2개의 배전반을 배치하였다.

 

 

Connector

마지막으로 BASE와 모듈간의 통신 및 전원 공급을 위한 커넥터 구성이다. 우선 데이터 통신을 위해서는
USB와 I2C 2가지의 방법을 선택 가능하도록 하여 USB 커넥터를 위한 홀과 I2C를 위한 일반 점퍼선용
홀을 두었다. I2C로 연결을 하는 경우에는 모듈쪽 보드에 별도의 전원 인가가 필요하므로 전원 공급용 점퍼
선도 함께 사용한다. 또 모터 등 직접 별도의 전원을 인가해야 할 경우를 위해 2개의 전원 포트용 홀을 두었다.

 

 

Modules

모듈은 전적으로 사용자가 직접 만들게 되므로 고정된 형태가 없다. BASE는 기본적으로 8개의 모듈을
연결할 수 있도록 만들어져 있으나 이 것은 표면적인 형태일 뿐이고 사용하기에 따라서는 작은 모듈 8개를
BASE에 연결하는 형태가 아닌 커다란 모듈에 BASE를 장착하는 형태로 운용할 수도 있다. 모듈은 
기본적으로 아두이노 프로 미니 보드를 베이스로 만들 수 있으며 라즈베리파이와 USB 연결이 가능한
보드라면 어떤 것이든 사용이 가능하다.

 

모듈 제어

라즈베리파이로 아두이노를 제어하는 방법은 일반적으로 2가지를 생각할 수 있다. 

 

첫 번째는 USB로 연결하여 Firmata 라이브러리를 이용하는 방법이다. 이 방법은 라즈베리파이에서 
아두이노의 포트를 직접 제어하는 방식으로 USB로 연결하므로 전원 문제가 자동으로 해결되고 또 모듈쪽에 
별도의 프로그래밍이 필요 없다는 장점이 있다. 다만 라즈베리파이에서 몇개까지의 아두이노를 USB로 
연결하여 제어할 수 있는지는 확인이 필요하다. 일단 8개까지 연결해보려고 계획 중이다. 

 

 

두 번째는 I2C 방식으로의 연결이다. 이 방식은 BASE의 라즈베리파이에서는 사용자의 원격 조종기 신호만
받아들여 이 신호를 모듈쪽으로 전달만 하고 실제 기능은 각 모듈의 프로그램들이 전달된 신호에 따라 수행
하게 되는 방식이다. 일단 상당히 많은 수의 모듈을 연결할 수 있기는 하지만 모듈쪽에 별도의 전원을 인가
해주어야 하고 또 각각의 모듈에 프로그래밍을 해야 한다는 단점이 있다.

 

 

우선은 연결의 편의성과 라즈베리파이에서만 프로그래밍을 하면 된다는 점에서 USB 연결을 우선으로 
생각하고 있다.

 

운용

이미 앞에서 상당부분 언급을 하였지만 MORS는 BASE에 사용자가 직접 만든 모듈을 연결하여 동작을
시키는 방식이다. 모듈은 바퀴가 달린 형태일 수도 있고 로봇 다리의 형태일 수도 있고 드론의 프로펠러가
될 수도 있으며 방수 처리가 된 모듈로 수상용 모듈이 될 수도 있다. 아주 가볍게는 선풍기 모듈 하나 붙여
책상 위에 올릴 수도 있을 것이다^^.

 

더불어 사용자가 원한다면 본인이 개발한 모듈을 공유할 수 있도록 할 것이다. 3D 프린팅된 모듈용 프레임이
있다면 Thingiverse와 같은 플랫폼을 이용하여, BASE나 모듈의 프로그램들은 GitHub와 같은 플랫폼을
통하여 서로 공유할 수 있도록 하는 것이다.

 

공유 프로그램의 경우 라즈베리파이에 소형 모니터를 연결한 후 아래 이미지와 같은 사용자 인터페이스를 통해
온라인으로 직접 업데이트 할 수 있도록 개발할 계획이나 이 부분은 시간이 꽤 걸릴 것 같다. 아울러 여력이 
된다면 npm같은 플랫폼을 직접 만들어보고 싶기도 하다.

 

 

정리

앞서 말했듯이 이 프로젝트는 그동안 진행했던 개인 프로젝트들을 총 집대성한 프로젝트가 될 것이다.
규모 면에서는 상당히 부담이 되지만 그래도 앞서 했던 작업들의 연장선인 만큼 어느 정도 자신감은 있다.
하지만 어디 세상 일이란 것이 그렇게 단순하기만 하던가. 중간에 어떤 문제가 어떻게 닥쳐올 지는 아무도
모르는 일…-.-

 

당장에 BASE 프레임을 3D 출력해야 하는데 내가 가지고 있는 3D 프린터는 너무 사이즈가 작아 출력이 
어렵고(분할하기도 쉽지 않은 형태라) 결국 출력 대행을 해야 하는데 이것 또한 비용이 만만치 않다…ㅠ.ㅠ
그래도 결국은 돈을 쓰고 말겠지만…

 

우선은 BASE를 빨리 만들고 첫 번째 모듈은 4족 보행 로봇용으로 만들려고 한다. 사실 바퀴가 달린 형태가
구현이 훨씬 쉽겠지만 애초에 4족보행 로봇을 재시도 하려다가 탄생한 프로젝트이다보니 일단은 로봇
쪽으로 시작을 해보려고 한다.

 

어쟀든 BASE 프레임을 출력 대행 맡기더라도 다음 주 월요일에나 배송이 가능하다니 다음 포스팅은
다음주 주말이나 될 것 같다. 부디 계획대로 잘 진행되길…

반응형

 

OpenCV 학습을 위한 스테레오 카메라 테스트

다시금 4족 보행 로봇에 관심을 기울이게 되면서 기왕이면 OpenCV를 이용하여 Object Detection을 구현하고
낮은 수준의 자율주행 기능을 포함시키고 싶었다. 하지만 새로운 것을 공부해야 한다는 부담감 때문에 섣불리
손을 못대고 있다가 본능에 따라 한 번 들이대보기로 하였다.

 

아직은 정리할만큼도 학습을 못했기 때문에 실제 OpenCV에 대해서는 언급할 내용이 없고 오늘은 카메라에 대한
이야기나 조금 찌끄려보려고 한다.

 

USB Web CAM을 이용한 스테레오 카메라

사실 라즈베리파이에서 카메라 모듈을 2개 이상(4개까지) 연결하여 사용할 수 있는 모듈이 있다. 하지만 가격이
만만치 않다(6~7만원 선). 게다가 파이 카메라 모듈도 필요하고(그나마 V1.3 만 지원한다고 하니 파이 카메라
가격은 좀 저렴하게 구성할 수 있다), 어쨌든 대충 계산해도 한 10만원은 있어야 스테레오 카메라를 구성할 수
있을 것이다.

 

라즈베리파이에 4개까지 파이 카메라 V1.3을 연결할 수 있는 모듈

 

그래서 생각한 것이 저렴한 USB 웹캠 2개를 이용하면 되지 않을까 하는 생각이었다. 실제로 그렇게 구현한 예도
유튜브에 종종 보이고…그래서 일단 만원짜리 웹캠을 2개 샀다. 그리고 3D 프린터로 작은 홀더를 2개 만들어서
각각 등짝에 하나씩 붙여주고 8∅짜리 스테인레스 파이프로 연결하여 고정해주었다. 

 

 

그리고 라즈베리파이에 USB 웹캠을 연결을 하고 샘플 코드를 돌려보았다.
하지만…언제나 그렇듯이 유튜브에서 보여지는 것은 F1 레이싱카였지만 내가 돌리는 것은 애기들 태우는 전동차
수준…ㅠ.ㅠ 하드웨어 문제인지 뭔가 추가적인 코드가 더 필요한 것인지 영상이 엄청 끊기는데다가 2대의 카메라
영상이 거의 동기화가 되지 않았다…ㅠ.ㅠ

 

 

 

일단 바로 해볼 수 있는 해결 방법은 영상의 크기를 줄이는 방법! 그래서 코드에서 카메라 영상의 크기를 320X240
으로 줄이고 다시 시도해보았다. 역시나 사이즈가 작아지니 영상 끊김도 거의 없고 두 영상이 어느정도 동기화 되는
모습을 볼 수 있었다. 하지만 아직도 만족스러운 정도는 아니었다.

 

 

 

ELP Dual lense USB Camera module

계속해서 스테레오 카메라를 알아보고 있었는데 올 초부터 눈에 밟히는 녀석이 하나 있었다. 물론 전문적인 용도는
아니고 그냥 가볍게 사용해볼 수 있을만한 제품으로 보였다. 그럼에도 가격대는 역시 8만원대로 그리 만만한
녀석은 아니었다.

 

그리고…마침 로봇에 꽂혀 이것 저것 부품을 마련하는데 돈이 필요했던 나는 예전에 빅데이터와 클러스터링을
한번
 공부해보겠다고 어렵게 모은 맥미니 5대 중 3대를 팔아버리고 말았다…ㅠ.ㅠ 물론 가격은 구입 당시의 반토막
…ㅠ.ㅠ

 

그렇게 돈을 마련하고 제일 먼저 알리에서 이 제품을 구매했다. 그리고 생각보다 배송이 일찍되어 오늘 이렇게
라즈베리파이에 연결하여 확인을 해볼 수 있었다.

 

 

일단 하나의 모듈로 만들어진 제품이기 때문에 두 카메라로 입력된 영상이 하나의 영상의로 합쳐져 출력되었고
코드상에서도 하나의 VideoCapture 클래스만 사용하면 되었다. 영상의 동기화도 훌륭하고 기본으로 출력되는
320X240 정도의 크기(두 영상이 합쳐서 나오기 때문에 실제 가로는 640)에서는 영상 프레임도 상당히 좋은
수준을 보여줬다.

 

 

영상 사이즈를 640X480(전체 사이즈 1280X480)으로 출력했을 때도 영상이 끊기는 문제는 있었지만 적어도
프리징 되지는 않았고 동기화는 원활했다.

 

 

다만 사용하다보니 발열이 좀 있었고 하나의 영상의 입력되기 때문에 나중에 여러가지 처리를 하기 위해서는
영상을 둘로 나누는 코드가 필요할
것이다. 설마 이정도 코드 추가가 성능 저하를 일으키진 않겠지…-.-

 

정리

아직 이렇다할 작업 내용이 없어서 오늘은 2대의 웹캠을 이용한 것과 하나의 모듈로 구성된 2대의 카메라를 이용한
것의 영상 출력만을 비교해보았다. 앞으로 어느정도 학습이 진행이 되면 OpenCV에 대한 실습 내용을 위주로
블로그에 정리를 해보도록 하겠다.

  1. raspi 2020.05.06 13:41

    안녕하세요, 저희는 라즈베리파이에 웹캠 한개와 라즈베리파이모듈 한개씩을 각각 사용해서 스트리밍과 영상처리를 나누어서 할 계획을 가지고 있습니다. 혹시 이 작업이 가능할지 여쭤보고 싶네요. 감사합니다.

    • 마즈다 2020.05.06 15:17 신고

      우선 관심 가져주셔서 감사합니다.
      말씀하신 내용은 사실 저도 잘 아는 부분이 아니라서 정확한 답변을 드리긴 어렵지만 일단 OS에서 웹캠(USB)과 라즈베리파이 카메라 모듈(CSI 카메라 포트)는 서로 다른 주소로 인식할테니 각각의 카메라를 서로 다른 용도로 사용한다면 충분히 가능할 것으로 보입니다. 다만 저처럼 스테레오 카메라 구현이라면 두 개의 카메라가 동일한 스펙을 가진 것을 사용하는 것이 좋을 것 같구요.

      별 도움이 못되는 것 같아 죄송합니다^^;

  2. June 2021.05.06 16:29

    안녕하세요!
    혼자 라즈베리파이 취미로 공부하는 사람입니다.
    게시글처럼 usb웹캠2개랑 라즈베리파이로 스테레오카메라 한번 만들어보고 싶은데,
    워낙 초보라서 어떻게 해야 할지 모르겠네요.. 혹시 괜찮으시다면 코드공유해주실수 있으신가요??

    • 마즈다 2021.05.07 16:35 신고

      작업 해본지가 좀 되서 소스를 어디다 뒀는지 찾아봐야겠네요^^;; 찾는대로 공유해드리겠습니다.

반응형



2019/05/13 - [Study/인공지능학습] - [인공지능] coral usb accelerator + Raspberry pi zero w

2019/05/16 - [Study/인공지능학습] - [인공지능] Coral USB Accelerator와 Edge TPU




뭔가 하나를 진득하니 끝내야 하는데…이놈에 호들갑스러운 호기심은 눈밭에 강아지 뛰놀듯한다…
얼른 미니 드론 만들고 다음에 싱글콥터 그 다음에 4족 보행 로봇 최종판을 만들어야 하는데…
도대체 어느세월에...ㅠ.ㅠ


이번 호기심의 대상은 바로 구글에서 엣지 컴퓨팅 보드로 출시한 coral 시리즈 중에 usb 가속기인

coral usb accelerator이다. 페이스북에서 어떤 분이 올려놓으신 엣지 컴퓨팅을 위한 보드들의
성능을 비교한 해외 블로그를 보고 나니 궁금해서 견딜 수가 없었다.


Benchmarking Edge Computing


아직은 책만 줄창 읽어대고 있는 정도의 수준이라 내가 이걸 사서 제대로 써먹을 수나 있을지 의문이었지만
지름의 기본 자세인 '그래도 언젠가는 써먹겠지’라는 마음가짐으로 하나 질렀다.

그리고 다행히도 간단한 테스트 정도는 해볼 수 있었다…^^;



Edge Computing이란?


우선 본론에 들어가기 전에 엣지 컴퓨팅이란 것이 무엇인지 부터 알고 넘어가자.


예전에 메인프레임을 쓸 때는 당연한 일이었고 웹 기반의 시스템이 자리를 잡은 후에도 대부분의 중요한

연산은 서버에서 처리를 하고 클라이언트 기기(PC)에서는 자료를 입력하는 정도의 기능만 수행을 했다.

그만큼 서버는 강력한 기능을 갖고 있었고 클라이언트로부터 전송되는 데이터들은 서버가 감당하기에

충분한양이었다.


하지만 스마트폰이 지배하는 세상이 되면서 클라이언트의 종류와 그로부터 전송되는 데이터는 점점 증가를

하게 되었고 급기야 IoT라는, 모든 것을 인터넷에 연결시키겠다는 야심찬 시도가 진행되면서 이제 클라이언트는

그 종류를 한정할 수 없게 되었고 그 수많은 클라이언트들로부터 올라오는 데이터의 양은 서버가 감당하기 어려운

상황이 되어버렸다.


한편, 스마트폰을 시작으로 한 클라이언트 기기들은 소형화와 고성능화로 인해 웬만한 작업은 그 기기 내에서 처리를

할 수 있게 되었다. 예전같으면 워크스테이션에서나 가능했을 동영상 편집을 스마트폰에서도 할 수 있게 된 것이다.

상황이 이렇다보니 클라이언트 기기를 단순히 서버로 데이터를 전송하는 용도로만 사용하는 것이 엄청난 자원의 낭비가

된 것이다.


결국 가능한 한 클라이언트에서 처리할 수 있는 부분은 처리를 하고 서버에서는 연산에 높은 성능이 필요한 부분을

처리하려는 시도를 하게 되었고 이 과정에서 클라이언트 측에서 처리되는 부분을 Edge Computing이라고 부르게

되었다.


하지만 아무리 클라이언트 기기들의 성능이 좋아졌다고 해도 머신러닝이나 딥러닝을 처리하기에는 한계가 있다보니

이러한 고성능의 연산을 보조해주기 위한 많은 장치들이 개발되었다. 그 자체로 보드형태를 가지고 나온 제품과

기존의 소형 컴퓨터 보드인 라즈베리파이 등에 연결해서 사용 가능한 USB 기기 형태로 나온 제품들이 있는데

처음 소개한 블로그에서는 바로 이러한 기기들의 성능을 벤치마킹하고 있는 것이다.



무엇을 선택할 것인가?


이 블로그를 보면 몇가지 대상에 대해 상당히 많은 항목을 벤치마킹 하고 있다. 하지만 영어 울렁증의 도움으로

아주 가볍게 한 가지를 선택할 수 있었다. 바로 제일 처음 나오는 추론 속도에 대한 벤치마킹을 기준으로 가장

가성비가 높은 제품을 고르기로 한 것이다.


일단 추론 속도에 대한 벤치마킹 그래프를 보면 구글의 coral 시리즈가 매우 성능이 좋다. 그래서 1차로 coral 
시리즈를 구입하기로 결정을 했다(사실 인텔의 NCS2를 더 먼저 알게 되었고 디자인도 NCS2의 사이버틱한

디자인이 더 마음에 들었지만 벤치마킹 결과와 가성비에서 탈락했다).


라즈베리파이가 없었다면 완전체인 coral dev board를 구입했겠지만 이미 예전에 빅데이터 클러스터 공부한다고

깝죽대면서 사다놓은 라즈베리파이가 수두룩 빽빽하게 있어 비용면에서 더 저렴한 coral usb accelerator로

선택을 했다.





구매는 MOUSER에서 했으며 가격은 $74이다. 배송은 DHL로 약 2일만에 도착을 했고 배송비는 무료다!

생각보다 빨리, 안전하게 도착해서 참 다행이었다(가장 최근의 해외 직구는 banggood에서 주문한 모터를

2달 만에 받은 것이었다…-.-).



장비 연결


현재 집에 있는 라즈베리파이는 3B 모델이 5대, zero w 모델이 2대 있다. 그런데 생각해보니 3B 5대는

클러스터 만들면서 스택 케이스에 다 연결하고 배선까지 꽁꽁 묶어놓아서 도무지 풀어서 사용할 엄두가

나질 않았다.


결국 zero w를 사용하기로 결정을 했는데 이게 또 만만치 않다.라즈베리파이 zero w의 경우 USB포트가

마이크로 USB타입으로 꼴랑 2개 있다. 그나마 하나는 전원공급용이므로 실제 사용 가능한 것은 1개 뿐이다.

게다가 w가 무색하게 빌트인 된 Wi-Fi 모듈은 잘 붙는공유기 찾기가 하늘의 별따기라 어쩔 수 없이 USB 형태의

무선 랜카드를 별도로 사용해야 한다.


이렇게 해서 일단 필요한 USB장비는 벌써 2개가 되었다. USB 랜카드와 coral…여기서 또 한가지 생각지 못한

문제가 생겼다. 라즈베리파이 사 모을 때 파이 카메라 V2 버전을 같이 산 것이 있어 그 걸 사용하려고 했는데…

아뿔싸! 라즈베리파이 3B와 zero w의 카메라용 필름 케이블 소켓 크기가 다르다…OTL 변환 케이블이 있긴 있는데…
돈도 돈이고, 배송도 배송이고…ㅠ.ㅠ


다행히 아이들이 학습용으로 사용하던 USB CAM이 있어서 그걸 사용하기로 했다.
이렇게해서 총 3개의 USB장치를 붙여야 하는 상황이 되었다. 결국 USB 허브까지 하나 구매해서 어찌어찌

연결은 성공하였다~^^ (사진은 카메라 연결 전이다.)


사실 최대한 부피를 줄인 장치를 만들어보고 싶어서 보드로 zero w를 선택한 것인데 결과적으로는 배보다 배꼽이 

더 큰 상황이 되어버렸다. 나중에 기회가 된다면 하우징들은 다 벗겨버리고 연결을 해서 작은 통합 기기를 만들어
보고 싶다(하지만 성능 때문에 zero w는 못쓸 듯…-.-).






라이브러리 설치 및 테스트


공식적으로 coral usb accelerator가 지원하는 기기는 라즈베리파이 2와 3의 모델 B와 B+ 뿐이다.

즉, zero w는 공식적으로는 지원하지 않는 보드이다. 하지만 비공식적으로 설치 가능한 라이브러리가 존재하고

공식 사이트에서도 소개하고 있다. zero w에 대한 라이브러리 설치 방법은 아래 링크에서 확인 가능하다.


support for Raspberry Pi Zero


설치 방법은 매우 간단하며, 압축된 라이브러리 패키지를 다운로드 받고 압축을 해제한 후 install.sh를 실행하면

끝이다. 그리고나서 공식 사이트에 있는 설명대로 데모 프로그램을 돌려보면 된다.우선 설치 후 2개의 데모 프로그램을 

실행해보았다. 이미지 분류와 얼굴 인식을 하는 데모였는데 정상적으로결과가 나오는 것을 확인하였다. 


특히 얼굴 인식의 경우 구글에서 받은 이미지로도 테스트를 해보았는데 대체로잘 인식이 되었으나 특정 사진 한장은 전혀 

인식을 못하였다. 다양한 요인이 있을 것 같은데 정확한 이유는 모르겠다. 맨 처음 사진은 Mobilenet SSD v2에서 제공

되는 사진이고 다음 3장은 구글에서 가져온 사진인데 맨 마지막 사진은 인식에 실패했다.









성능의 문제

사실 지금까지 라즈베리파이를 사용해오면서 딱히 성능을 확인해야 할만큼 부하가 걸리는 작업을 해본 적이
없기에 굳이 따져보지 않았는데 막상 coral usb accelerator를 연결하여 이미지 분류나 인식 작업을 해보니
라즈베리파이 3 모델 B와 zero w 사이에는 엄청난 성능의 차이가 있었다.

라즈베리파이 3 모델 B에서 데모 프로그램 실행 시 얼마나 시간이 걸리는지 측정한 자료가 거의 없어 확인이
쉽지 않았는데 유튜브에서 발견한 동영상 하나에서 대략 5초 정도 걸리는 것을 확인했다.

그런데 zero w 모델에서는 15초 정도의 시간이 걸렸다. 처음에는 coral usb accelerator가 연결되지 않았는지
의심도 해보았으나 연결되지 않은 경우에는 실행 시 오류가 발생을 하였고 또 연결되었을 때 연산 시 LED가 
점멸하는 것도 확인을 하였으니 분명 coral usb accelerator가 작동을 하고 있는 것이었다.






이러한 성능의 문제는 다음에 테스트한 동영상에서의 사물 인식의 경우에 더 심각하게 나타났다.


카메라 테스트


일단 앞서 말한대로 파이 카메라를 사용할 수가 없어 USB Cam을 사용하게 되었다. 
그리고…이미 사물 인식을 하기도 전에 영상은 너무 끊겨서 적당한 수양의 과정을 거치지 않고서는 참고 
보아줄 수 없는수준이었다.

그래도 나름 참는데는 일각연이 있는 터라 테스트를 진행해보았다. 2개의 다른 소스로 진행을 해보았다.
처음 테스트는 아래의 블로그를 참고하였다.

Hands-on with the Google Coral USB Accelerator


사용한 모델 및 실행 코드 정보가 모두 위의 블로그에 정리되어 있다. 그저 다운로드 받고 실행하기만 하면
되는 생각보다 간단한 과정으로 진행할 수 있다. 아래 동영상을 보면 알겠지만 일단 상당히 느리다…ㅠ.ㅠ
인식률 또한 상상했던 것보다 좋지 않은데 두 번째 테스트와 종합적으로 봤을 때 사람과 차에 대한 인식룰은
기가 막히다. 특히 사람은 팔뚝만 슬쩍 보여도 사람으로 인식한다.

하지만 드라이버와 커터는 인식을 못했고 귤은 사과로 인식하거나 햄스터를 새, 개 등으로 인식하고 있었다.
두 번째 동영상은 유튜브에서 영상을 하나 틀어놓고 그 영상을 찍으면서 인식을 시킨 것인데 버스나 차를 대체로
잘 인식하는 것 같았다.






두 번째 테스트는 다음의 2개 사이트를 참고했다.

프미케의 낙서장
https://qiita.com/PINTO/items/dd6ba67643bdd3a0e595


사용한 모델은 첫 번째 테스트와 동일하지만 실행코드가 조금 다르다. 두 번째 테스트용 실행코드에는 초당 프레임

정보가 나오는데 위 링크한 블로그들의 프레임 수와 비교하면 형편없는 수준이다. 그리고 특이한 것이 처음 테스트에서는 
커터를 인식하지 못했는데 두 번째 테스트에서는 커터를 인식하였다.





정리


대체로 뭔가 하나에 관심을 갖게 되어 실행을 해볼 때까지 구입, 준비, 실행의 과정이 꽤나 길었는데 이번에는
관심을 갖게된 후 이 블로그 포스팅을 작성하기까지 정확히 5일 걸렸다. 거의 일사천리로 진행이 된 것이다.
그거 하나로도 위안이 된다…^^;;

하지만 앞에서도 강조했듯이 라즈베리파이 zero w는 작다는 것 외에는 그닥 쓸모가 없다는 것이 판명났다.
적어도 엣지 컴퓨팅에서는…최소한 라즈베리파이 3 모델 B 이상은 되어야 암에 걸릴 확률을 낮출 수 있을것이다.
아직은 내 수준이 남들 발바닥 언저리에서 노는 수준이다보니 근거는 없지만 뭔가 모델들이 사람이나 차량에
특화된 것 같은 느낌이다. 자율주행 RC카 같은 것 만들어보면 재밌을 것 같다.

물론 조금 더 실력이 된다면 수화 번역기 같은 것을 한 번 만들어보고 싶다.

어쨌든 이렇게 또 하나의 가능성을 맛본 것으로 만족하면서 포스팅을 마친다.


반응형






Cluster : The Beginning - Raspberry pi에 HAProxy 설치하기


지난시간 까지 2대의 라즈베리파이에 EMQ를 클러스터링하여 설치하는 작업을 진행하였다.
그런데 한 가지 문제가 생겼다. 일반 가정집에서 공유기를 통해 접속을 하다보니 2대 중 한대로만 접속이 이루어진다는
점이다. 혹시나 해서 공유기의 트래픽 관리에 외부 포트는 동일하게, 내부 서버는 서로 다르게 설정을 해보아도 역시나
가장 마지막에 등록된 정보로만 통신이 이루어졌다.


이렇게 되면 세션은 공유가 되지만 만약 외부에서 접속되는 한 대의 서버가 다운되면 전체 시스템을 사용할 수 없게
되는 것이다. 결국 접속을 분산시킬 필요가 생겼다. 그리고 그렇게 해서 HAProxy라는 솔루션을 찾아내었다.


더불어 애초에 EMQ 2대 Apache Kafka 3대로 클러스터를 구성할 생각이었는데 부랴부랴 HAProxy를 위한 
1대의 라즈베리파이를 추가로 구입하여 총 6대의 클러스터가 되었다.



Clustering과 Load Balancing


일반적으로 성능 향상을 위해 여러대의 서버에 동일한 구성을 하여 함께 운영하는 경우가 많다.
그리고 이렇게 구성하는 경우 Clustering이라든가 Load Balancing이라는 용어가 많이 쓰이는데 과연 
이 둘의 차이는 무엇일까?


가장 단순하게 표현하자면 Clustering은 여러대의 컴퓨터 그룹이 같은 정보를 가지고 함께 일을 하는 것이고
Load Balancing이라는 것은 각각의 컴퓨터가 공평하게 일할 수 있도록 일감을 나누어 주는 것이라고 생각하면
되겠다. 이렇게 정의할 때 Clustering은 같은 정보에 방점이 찍히고 Load Balancing은 나누어 주는
방점이 찍히게 된다. 즉, Clustering은 그룹을 지어 함께 일을 하는 서버간의 관계에 대한 개념이고 Load Balancing은
클라이언트에서 서버로 접속하는 과정에서의 처리에 대한 개념이라고 보면 되겠다.

 



여러 대의 컴퓨터를 대상으로 한다는 것 외에는 서로 다른 개념이고 종속관계를 나눌 수는 없지만
최근 Clustering을 지원하는 솔루션들은 내부적으로 Load Balancing을 함께 지원하는 경향이 있다. 하지만 
Load Balancing을 위한 솔루션이 Clustering을 지원하는 경우는 거의 없다.


예를 들어 1대의 서버로 서비스를 제공할 경우 그 서버가 다운되면 서비스 자체를 제공하지 못하므로 2대의 서버에 
각각 Tomcat을 설치하여 동일한 서비스를 제공하고 싶다고 하자. 각각을 Tomcat1, Tomcat2라고 했을 때
Tomcat1로만 또는 Tomcat2로만 접속이 가능하다면 2대의 서버를 구성한 의미가 없어진다. 그래서 서버 앞단에
Load Balancing을 위한 장비를 두고 적절한 알고리즘을 통해 각각의 클라이언트들이 Tomcat1과 Tomcat2에
적절하게 분산되어 접속하도록 조정을 해주는 것이다.


그런데 Load Balancing만 하는 경우에는 문제가 발생할 수 있다. 아래 그림을 보면 client1과 client2가 Load 
Balancer를 통해 각각 Tomcat1과 Tomcat2로 적절하게 나누어 요청을 수행하고 있다. client1은 Tomcat1에
문서 파일인 B를 업로드 했고 client2는 Tomcat2에 그림파일인 A를 업로드 했다. 그런데 Tomcat2 서버에
장애가 발생하여 접속이 불가능하게 되면 Load Balancer는 이후 모든 접속을 정상 작동 중인 Tomcat1로 보낸다.
이 때 client2의 입장에서는 Load Balancer 이후의 구조에 대해서는 알지 못하므로 분명 자신은 그림 파일 A를
서버에 업로드 했는데 그 업로드한 파일을 찾을 수 없게 된다.




이러한 문제 때문에 보통 Load Balancing을 하면 Clustering도 함께 고려를 해야 한다. 물론 간단하게 Tomcat은
이중화를 하되 DB는 1대만 설치하여 중요 정보만 공유하는 것도 고려해볼 수 있을 것이다.


결론적으로 Clustering과 Load Balancing은 다음과 같이 구분지어 볼 수 있을 것이다.



HAProxy


EMQ의 경우 clustering을 통해 상태 공유는 되지만 클라이언트와 접속하는 지점에서의 load balancing은 별도의
작업을 해주어야 한다. 일반적으로 널리 알려진 load balancing 장비는 L4 Switch라는 하드웨어 장비가 있다.
하지만 개인이 그런 비싼 네트워크 장비를 사용할 수도 없을뿐더러 사용할 필요도 없다…우도살계인 격이다.


그러다가 발견한 것이 HAProxy라는 솔루션이다. S/W적으로 load balancing을 지원해주는 솔루션이었다.
홈페이지는 다음과 같다.

http://www.haproxy.org


사실 HAProxy는 전체 구성요소에서 고려하고 있지 않던 부분이라 아주 기초적인 부분만 확인했다.
자세한 내용은 HAProxy로 검색하면 많은 정보를 얻을 수 있으니 여기서는 간단하게 설치와 설정 그리고
모니터링 웹 콘솔에 대해서만 살펴보겠다.


설치


역시나 소스를 컴파일하는 설치 방법이 많이 검색되는데 나는 심플하게 apt-get으로 설치하였다.


$sudo apt-get install haproxy 


홈페이지에는 현재 최종 릴리즈 버전이 1.9인데 apt-get으로 설치하니 1.7.5-2 버전이 설치되었다.
이렇게 설치하고 나면 /etc아래에 haproxy 디렉토리가 생성되고 그 아래 설정파일인 haproxy.cfg가
위치한다.


설정


HAProxy의 설정은 몇개의 영역으로 구분되는데 간단하게 살펴보면 다음과 같다.


  • global : 이름 그대로 전역 변수에 대한 설정으로 로그, 상태, 소유자 및 소유 그룹 SSL 관련 설정들이 있다.
  • default : 아래 내오는 세부 설정에서 명시적으로 추가하지 않은 설정은 이 default 설정을 따른다.
  • frontend : 클라이언트가 접속하는 것과 관련된 설정이다. 기본적으로 외부에서 접속할 포트 정도 지정한다.
  • backend : frontend와 짝을 지어 설정하며 frontend 설정을 통해 접속한 클라이언트가 실제 요청을 보낼 서버를 지정한다.
  • listener : frontend와 backend를 한번에 설정할 때 사용한다. 주로 TCP 연결에 사용한다.


각 설정의 뒤에는 설정을 대표하는 이름을 적게 된다. 그 이름은 모니터링 화면에 표시하여 구분할 수 있도록 한다.
앞서 EMQ에서와 마찬가지로 내가 설정한 부분만 간단하게 살펴보자. global과 default에서는 유의미한 수정이
없으므로 frontend와 backend 그리고 listener 설정만 보도록 하겠다.


#frontend와 backend 설정은 EMQ의 모니터링을 위한 웹 접속 설정이다.
#외부에서 8080포트로 접속을 하게 되면 backend에 설정된 172.30.1.9 서버와 172.30.1.25 서버의
#18083 포트로 접속을 하게 되는데 이 때 클라이언트를 두 서버로 분배하는 알고리즘은 roundrobin이다.
#mode는 프로토콜을 설정하는 항목인데 여기서는 지정을 안했기 때문에 default 설정에 있는 http 접속을
#처리한다.
frontend http-in 
	bind    *:8080
	default_backend servers

backend servers
	balance roundrobin
	server server1 172.30.1.9:18083 maxconn 2048
	server server2 172.30.1.25:18083 maxconn 2048

#아래 설정은 클라이언트로부터 EMQ boker에 접속하기 위한 설정이다.
#HAProxy를 사용하게 된 것도 바로 이 설정이 필요했기 때문이다.
#외부에서 1883 포트로 접속을 하게 되면 두 서버의 1883 포트로 접속이 분배되며 역시
#분배 알고리즘은 roundrobin이다. 프로토콜을 설정하는 mode는 tcp로 설정한다.
listen  tcp-in
        balance roundrobin
        bind    *:1883
        log     global
        mode    tcp
        option  tcplog
        server  mtqqserver1     172.30.1.25:1883
        server  mtqqserver2     172.30.1.9:1883

#아래 설정은 위 설정과 동일한데 개인적으로 위 내용은 모바일이나 IoT로부터의 접속에 사용하기 위해
#설정하였고 아래 내용은 kafka에서 EMQ broker의 메시지를 subscribe하기 위해 따로 설정하였다.
listen  mqtt-kafka
        bind    *:1884
        mode    tcp
        option  clitcpka
        timeout client 3h
        timeout server 3h
        option  tcplog
        balance leastconn
        server  mtqqserver1     172.30.1.25:1883        check
        server  mtqqserver2     172.30.1.9:1883         check

#이 설정은 HAProxt의 모니터링을 위한 웹 콘솔에 접근하기 위한 설정이다.
#이 설정의 이름은 stats로 정해져 있으며 uri의 경우 주소:포트 뒤에 따라올 문자열로
#임의로 설정하면 된다.
listen  stats
        bind    *:8081
        mode    http
        stats   enable
        stats   uri     /haproxy?stats
        stats   auth    guest:guest
        stats   refresh 5s


기본적으로 이정도만 해도 원하는 바는 얻을 수 있다. 좀 더 세밀한 설정을 위해서는 보다 많은 내용을 추가해야 하지만
앞서 말했듯이 HAProxy는 가볍게 지나가도록 하겠다.


실행


실행과 종료는 간단하게 다음과 같이 하면 된다.


$/etc/init.d/haproxy start
$/etc/init.d/haproxy stop


설정이 바뀌면 다음과 같이 재시작 한다.


/etc/init.d/haproxy restart

모니터링


위 설정에서 이미 모니터링을 위한 웹 콘솔에 대한 설정은 마친 상태이다. 설정에 적힌 포트로 접속만 하면 된다.
설정에 따라 브라우저에 아래 주소를 입력하면 모니터링 화면이 뜬다.


http://localhost:8081/haproxy?stats




테스트


별다른 스크린샷은 올리지 않겠지만 지난 번 포스팅에 설명했던 MQTT 클라이언트로 connect와 disconnect를
반복해보면 EMQ 모니터링 화면의 Clients 메뉴에 한 번은 172.30.1.9 서버로, 한 번은 172.30.1.25 서버로
번갈아 접속되는 모습을 볼 수 있다.


정리


이렇게 해서 갑작스럽게 설치하게 된 HAProxy에 대한 정리를 마친다.
물론 집에서 개인 프로젝트용으로 구성하는 서버에 뭘 이런 것까지 설치해야 하나 싶지만 기왕에 다수의 장비를
구성하게 된 김에 곁들여서 한 번 설치를 해보았고 나름 작동하는 모습을 보니 재미는 있다^^;;;

다음 포스팅에서는 나머지 3대의 라즈베리파이에 Apache Kafka를 설치하고 설정 및 실행시키는 과정을
정리해보도록 하겠다.







  1. 1234 2020.03.13 17:25

    여러대의 라즈베리파이로 로드밸런싱 구현을 찾아보던중 여쭤볼게 있어 댓글을 달게 되었습니다.
    저는 마즈다님이 진행하시는 프로젝트와 비슷하게 3대의 라즈베리파이를 두어 1대의 상위 라즈베리파이(로드밸런서)에 haproxy를
    설치하고 2대의 하위 tomcat서버로 request가 분산되는 웹 스토리지(N클라우드, 구글 드라이브 같은)를 구축하려고 하는중에 있습니다.
    스토리지 주소를 입력했을때 하위1,2 서버중 한곳으로 라운드로빈 방식으로 요청을 분산시키지만 1번 서버에 장애가 생기면 자연적으로 2번 서버로 접속하게 하는 클러스터링까지도 구현이 완료된 상태입니다.
    그러나 사용자가 파일을 업로드하면 데이터 싱크를 어떻게 맞춰야 하는지 잘 모르겠습니다.
    1TB하드웨어를 하위 서버 2개에 각각 연결해둔 상태인데 1번서버에서 사용자가 데이터를 업로드 한다면, 분명 2번 서버와 연결된
    하드웨어에서도 그 데이터가 존재해야 하는데 말입니다... 하드웨어를 하나만 두고 그 하드웨어에 서버 1,2가 함께 연결되어야 하는 걸까요??
    IT전공자가 아니다보니 많은지식이 없어 어려움에 처해있습니다ㅠ

    • 마즈다 2020.03.13 19:32 신고

      우선 보잘 것 없는 글에 관심을 가져주셔서 감사합니다. 죄송하지만 문의를 주신 내용은 상당히 고급 기술을 요하는 분야라 저도 만족스러울만한 답변을 드리기가 어렵네요. 일반적인 데이터의 경우 DB 서버를 단일화 하거나 이중화 하더라도 DBMS에서 제공하는 동기화 메커니즘을 통해 어느정도 처리할 방벙이 있지만 물리적인 대용량 파일의 경우 빅데이터를 염두에 둔 파일 시스템(HDFS와 같은)이나 NoSQL쪽이 아니라면 업로드를 중단시키지 않으면서 동기화 할 수 있는 방법은 그리 많지 않아보입니다. 이는 대형 업체 납품되는 실무용 솔루션에도 구현되어있지 않은 경우가 많습니다. (업로드는 어느 한쪽 서버로 이루어지고 실제 파일의 동기화는 수작업...-.-). 도움이 되는 답변을 드리지 못해 죄송합니다만 저도 지식의 한계가 있으니 양해 부탁드립니다^^;

    • 1234 2020.05.15 10:49

      보잘 것 없는 글이라뇨ㅠ
      너무 좋은정보 얻고가는것 같아서 감사할 따름입니다!
      두대의 서버간 파일의 동기화를 구현하는 것은 사실상 불가능에 가깝다는 결론을 내렸습니다..
      NFS를 새로 구축해서 공유 디렉토리에 접근하는 방식으로 전략을 변경했습니다.
      항상 좋은일만 가득하시길 바라겠습니다!

    • 마즈다 2020.05.15 13:08 신고

      댓글 감사합니다. 어쨌든 방법을 찾으셨다니 다행입니다. 좋은 결과 있기를 바랍니다~^^

+ Recent posts