Jetson Nano, PCA9685, L298N, DC-Motor 4륜구동 자동차

Jetson Nano를 얹어서 완성된 자동차

Jetson Nano를 충동적으로 구입하고 (http://practical.kr/?p=125) 테스트를 해본 후 뭘 만들까 고민을 하다가 자동차를 만들어 볼까? 생각을 했다. 전년도에 라즈베리파이를 이용해서 자동차를 만들려고 구매해 두었던 4륜 자동차 바디가 그대로 있어서 여기에 젯슨나노를 얹어 보기로 했다.

실패한이유

근데 생각해 보니 그때 라즈베리 기반의 자동차를 만들다 그만둔 이유가 라즈베리파이의 Digital I/O 의 숫자가 부족하다는걸 알고 나서였다. 바퀴하나를 구동하려면 모터드라이브 컨트롤러를 써야하는데 주로 L298N을 많이 사용한다. 대략 아래와 같은 연결로 모터 두개를 움직일 수 있다.

전원부를 제외하고 모터 4개를 구동하려면 12개의 DIO를 사용해야 한다. 라즈베리에서 IO를 모두 끌어쓰면 대충 맞긴한데 이러면 다른 작업을 전혀 할 수가 없다. 그래서 주로 2륜 구동으로 자동차를 만드는게 아닌가 하는 생각을 하고 바퀴를 새로 사기가 싫어서 그냥 그만 두고 말았다.

PCA9685

이렇게 생겼다

그래도 젯슨은 뭔가 좀 다르겠지 싶었지만 라즈베리와 젯슨나노의 IO Pin 구조는 거의(?) 동일하다. 그런데 스터디중 Nvidia – Jetbot 에서는 PCA9685 라는 컨트롤러를 쓰고 있었다. 젯슨과 I2C로 통신을 하고 16개의 dio 채널을 가진 PWM 컨트롤러 였다. 대략 아래의 그림과 같이 문어발(?)을 만들 수 있다.

https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf

이걸 활용하면 젯슨나노에서는 4핀의 I2C 입출력 만으로 4개의 모터를 구동할 수 있고 나머지핀들은 다른용도로 활용할 수도 있다. 택배비 포함해서 2개에 만원정도니까 비싸지도 않다.

검색을 해보면 주로 서보모터를 운용할때 사용하는데 DC모터를 제대로 컨트롤 하려면 아래 그림과 같이 L298N과 함께 연결해줘야 한다.

자동차의 하체

하체의 중간에 PCA9685를 놓고 앞뒤로 L298N 모터 컨트롤러를 연결했다.모터 구동을 위한 별도의 전원은 9V 건전지를 사용했는데 조립중 테스트에서는 별 문제 없이 바퀴가 잘 돌았는데 다 조립하고 중량이 무거워지니 전후진이 안되었다. 그래서 9V 건전지 두개를 병렬로 연결했다. 움직이긴 하는데 힘이 없었다. 역시 전기 자동차는 배터리가 중요하다. 한개 더 달던지 해야 한다.

하체 조립중
하체 조립완료

차체조립

조립후

짧은 목표가 조립 & 이동 이었기 때문에 가지고 있던 이런저런 부품들을 모아 모아 모니터, 젯슨나노 & 모니터 전원을 위한 충전 배터리 향후 사용할 카메라까지 조립을 했다.

소프트웨어

검색을 해보니 PCA9685를 활용하는 대부분의 셈플들이 서보모터 구동용이라 찾기가 쉽지는 않았는데 다행히 원래 보드 제작사에서 만든 파이썬용 라이브러리가 있었다. 링크참조

https://github.com/adafruit/Adafruit_CircuitPython_Motor

https://cdn-learn.adafruit.com/downloads/pdf/adafruit-16-channel-servo-driver-with-raspberry-pi.pdf

위 라이브러리를 기반으로 아래와 같은 코드를 만들어 자동차를 움직여 봤다. 잘 움직인다.

import time
import busio

from board import SCL, SDA
from adafruit_pca9685 import PCA9685
from adafruit_motor import motor

i2c = busio.I2C(SCL, SDA)

pca = PCA9685(i2c, address=0x40)
pca.frequency = 100

pca.channels[0].duty_cycle = 0xFFFF
pca.channels[5].duty_cycle = 0xFFFF
pca.channels[6].duty_cycle = 0xFFFF
pca.channels[11].duty_cycle = 0xFFFF

motor1 = motor.DCMotor(pca.channels[1], pca.channels[2])
motor2 = motor.DCMotor(pca.channels[3], pca.channels[4])
motor3 = motor.DCMotor(pca.channels[8], pca.channels[7])
motor4 = motor.DCMotor(pca.channels[10], pca.channels[9])

print("Forwards slow")
motor1.throttle = 0.5
motor2.throttle = 0.5
motor3.throttle = 0.5
motor4.throttle = 0.5
time.sleep(3)

print("Forwards")
motor1.throttle = 1
motor2.throttle = 1
motor3.throttle = 1
motor4.throttle = 1
time.sleep(3)

print("Backwards")
motor1.throttle = -1
motor2.throttle = -1
motor3.throttle = -1
motor4.throttle = -1
time.sleep(3)

print("Backwards slow")
motor1.throttle = -0.5
motor2.throttle = -0.5
motor3.throttle = -0.5
motor4.throttle = -0.5
time.sleep(3)

print("Stop")
motor1.throttle = 0
motor2.throttle = 0
motor3.throttle = 0
motor4.throttle = 0
time.sleep(3)

print("Spin freely")
motor1.throttle = None
motor2.throttle = None
motor3.throttle = None
motor4.throttle = None

pca.deinit()

위 소스와 키입력으로 전, 후, 좌, 우로 이동하는 소스를 아래 링크에 공유 했다.

https://github.com/bipark/jetson-nano-car

실제 젯슨 나노의 GPU를 기반으로 자동차와 함께 할 수 있는 일들이 많을것 같은데 그건 다음에 해보기로 하고 오늘은 여기까지…

Jetson Nano, 전원, Wifi, Case, Setup, Inference

엔비디아의 Jetson Nano를 구입했다. 최근 좀 저렴한 2GB 모델의 릴리즈 소식을 들었지만 기존 4GB 모델을 구입했다. 충동적으로 구입했기 때문에 특별한 사용 목적은 없고 그저 어떤 성능이 나오는지 Development Kit 수준이 어느정도 인지 알고 싶었다.

구입은 아래 링크에서 했는데, 그동안 읽었던 글들에서 기본 모델에는 Wifi 없고 4A짜리 전원 공급장치가 필요하다고 해서 구입하는김에 Wifi 장비와 별도의 4A 전원공급장치, 그리고 케이스도 구입했다. 케이스를 별도로 구입한 이유는 아래에 보면 알게 되겠지만 Wifi 안테나가 꽤 큰 사이즈인데 케이스가 없으면 마땅히 붙여놓을 곳이 없어보여서 였다.

https://www.devicemart.co.kr/goods/search?keyword_log_flag=Y&search_text=jetson&q=jetson&x=0&y=0

위와 같이 두개의 디바이스로 분리 할 수 있다. 나중에 조립을 마치고 MicroSD를 좀 더 큰 사이즈로 바꾸려고 보니 완전히 다시 분해를 해야만 했다. 라즈베리파이가 그렇게 쉽게 SD를 바꿀수 있는것에 비해 너무 불편했다. 설계 미스!

그리고 아래 Wifi 모듈 커넥터는 위의 두개 디바이스 사이에 숨겨져 있다. 이것도 설계 미스! 너무 불편하다.

아래 동영상을 참조해서 Wifi를 연결했다. 다행히 소프트웨어 셋팅을 해줄 필요는 없다. 자동으로 인식 했다. 다른 블로그에서는 다이소에 가면 오천원짜리 USB 랜카드를 연결해도 된다고 했다. 그게 나을 수도 있다. 랜카드와 안테나 가격이 3만원이다.

조립을 하는 김에 가지고 있던 라즈베리파이 카메라를 연결했다. 아무래도 동영상 인식에는 카메라가 꼭 필요할것 같다.

조립을 마치고 Micro-USB에 전원을 연결해서 부팅을 했는데 계속해서 전원이 부족하다는 메세지가 나와서 배럴잭으로 4A 아답터를 통해서 전원을 연결했는데도 연결이 되지 않았다. 알고보니 배럴잭 뒤에 있는 J48 핀을 연결해야 배럴잭을 통해서 전원이 연결되는 것이었다. 4A 아답터 연결후에는 전원이 부족하다는 메세지는 나오지 않았다.

배럴잭이 있는 별도의 직류전원 장치 4A – 7200원

메탈 케이스 28,000원 – 후면

케이스가 그리 효율적이지 않은 구조였지만

조립해놓고 나니 그래도 케이스를 사길 잘했다는 생각이 들었다.

일단 조립완료 하고 부팅을 해봤다.

아래 링크에서 시작하면 된다. 순서대로 따라서 MicroSD를 준비해서 디바이스에 꽂고 부팅하면 우분투 화면을 만날 수 있다.

https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit

실제 DNN 기반의 추론테스트를 위한 라이브러리 셋팅은 아래 링크에서 코드와 같이 진행해야 하는데 전체 설치까지 대략 1시간이 넘게 걸린다. 학습모델 선택을 바꾸면 좀 더 걸릴 수도 있다.

https://github.com/dusty-nv/jetson-inference/blob/master/docs/building-repo-2.md

$ sudo apt-get update 
$ sudo apt-get install git cmake libpython3-dev python3-numpy 
$ git clone --recursive https://github.com/dusty-nv/jetson-inference 
$ cd jetson-inference 
$ mkdir build $ cd build 
$ cmake ../ 
$ make -j$(nproc) 
$ sudo make install 
$ sudo ldconfig

설치가 완료되면 아래 링크의 코드를 이용해서 실제 추론 테스트를 해볼 수 있다.

https://github.com/dusty-nv/jetson-inference/blob/master/docs/imagenet-example-python-2.md

셈플로 제공되는 곰돌이 이미지는 당연히 잘 인식되었고 다른 이미지를 이용해서 추론을 해보니 꽤 빠르게 잘 인식되었다.

ESP32, MicroPython, MQTT

아래와 같은 삽질을 했다. 삽질은 기록이라고… 대충 정리함

ESP32에서 MicroPython을 언젠가 한번 사용해봐야지 하고 있었는데 기왕 하는 김에 MQTT까지 연결해봤다.

  1. ESP32 에 MicroPython을 설치한다.
  2. ESP32에서 MicroPython으로 MQTT Client를 만든다
  3. 맥북에 MQTT 서버를 설치한다
  4. 맥북에서 Python을 이용해서 MQTT Client를 만든다.
  5. ESP32 – 맥북사이의 MQTT Client와 데이터를 주고 받기 테스트

ESP32에 MicroPython 설치하기

ESP32에 MicroPython을 설치하려면 firmware를 다운 받아서 설치해야 한다. 다운로드는 아래 링크에서 받을 수 있다. 가지고 있는 하드웨어에 맞게 버전을 선택해서 다운 받으면 된다.

https://micropython.org/download/esp32/

다운받은 파일을 설치하기 위해서는 esptool을 사용할 수 있다.

$ pip install esptool

https://github.com/espressif/esptool

디바이스가 연결된 포트를 확인하고 (ex: /dev/tty.SLAB_USBtoUART) 아래와 같이 firmware를 설치한다.

기존데이터를 지우고
$ esptool.py --port /dev/tty.SLAB_USBtoUART erase_flash 

설치한다
$ esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 esp32-idf3-20200902-v1.13.bin

ESP32에 MQTT Client 만들기

개발은 VSCode를 이용해서 Pymakr 라는 Extension을 설치하고 코드를 쓰고 업로드와 실행 테스트를 할 수 있다. 설치하면 VSCode 아래 메뉴바에 Run, Upload등의 메뉴를 확인할 수 있다.

Pymakr은 아래 링크 참조
https://marketplace.visualstudio.com/items?itemName=pycom.Pymakr

아래 코드는 https://randomnerdtutorials.com/micropython-mqtt-esp32-esp8266/ 에서 참고 했다. boot.py는 ESP32 가 시작할때 Wifi에 연결하고 main.py에서 MQTT Client를 생성해서 데이터를 수신한다.

boot.py

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
import micropython
import network
import esp
esp.osdebug(None)
import gc
gc.collect()

ssid = 'SSID'
password = 'PASSWORD'
mqtt_server = '192.168.0.7'
client_id = ubinascii.hexlify(machine.unique_id())
topic_sub = b'hello'
topic_pub = b'notification'

station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
   pass

print('Connection successful')
print(station.ifconfig())

main.py

def sub_cb(topic, msg):
    print((topic, msg))

def connect_and_subscribe():
    global client_id, mqtt_server, topic_sub
    client = MQTTClient(client_id, mqtt_server)
    client.set_callback(sub_cb)
    client.connect()
    client.subscribe(topic_sub)
    print('Connected to %s MQTT broker, subscribed to %s topic' % (mqtt_server, topic_sub))
    return client

def restart_and_reconnect():
    print('Failed to connect to MQTT broker. Reconnecting...')
    time.sleep(10)
    machine.reset()

try:
    client = connect_and_subscribe()
    client.publish(topic="hello", msg="Send By ESP32")
except OSError as e:
    restart_and_reconnect()

while True:
    try:
        new_message = client.check_msg()
        if new_message != 'None':
            client.publish(topic_pub, b'received')
        time.sleep(1)
    except OSError as e:
        restart_and_reconnect()

맥북에 MQTT 서버 설치

MQTT 서버는 상세정보는 아래 링크에서 참조 할 수 있고 주로 IOT 장비의 네트웍을 구성하는데 많이 사용한다. 나는 brew를 이용해 모스키토(Mosquitto)를 설치했다.

https://medium.com/@jspark141515/mqtt%EB%9E%80-314472c246ee

서비스 설치
brew install mosquitto

서비스 실행
brew services start mosquitto

서비스 중지
brew services stop mosquitto

맥북에서 송, 수신을 확인 할 수 있는 MQTT Client

Python용 MQTT 패키지를 먼저 설치한다.

https://pypi.org/project/paho-mqtt/

$ pip install paho-mqtt

Subscribe.py

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
print ("Connected with result code " + str(rc))
client.subscribe("hello")

def on_message(client, userdata, msg):
print ("Topic: ", msg.topic + '\nMessage: ' + str(msg.payload))

client = mqtt.Client() 
client.on_connect = on_connect 
client.on_message = on_message 
client.connect("192.168.0.7", 1883, 60) 

client.loop_forever()

Publish.py

import paho.mqtt.client as mqtt

mqttc = mqtt.Client("python_pub") 
mqttc.connect("192.168.0.7", 1883) 
mqttc.publish("hello", "Hello Billy")
mqttc.loop(2)

Publish.py를 실행하면 Subscribe.py와 ESP32에서 “Hello Billy” 메세지를 수신하는 것을 확인 할 수 있었다.