이 글은 우분투 20.04, 도커, CUDA 11.0 기반 NVIDIA-DOCKER, Tensorflow, Pytorch 설치 및 사용 방법을 간단히 설명한다. NVIDIA-DOCKER를 이용하면, 딥러닝 프레임웍 설치 시 많은 문제를 일으키는 패키지 의존 에러 없이, 미리 설치된 딥러닝 관련 패키지를 이용해 편리하게 딥러닝 모델을 개발할 수 있다. 또한, 아마존, 구글 같이 비싼 기계학습 딥러닝용 클라우드를 사용하지 않고도, 직접 여러사람이 네트워크로 접속할 수 있는 서버를 개발할 수 있다.
NVIDIA DOCKER 아키텍처 구조
여기서는 엔비디아 CUDA및 도커는 알고 있다는 전제하에 진행한다. 관련 내용은 다음 링크를 참고한다.
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 3x3 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
아래와 같이 출력되면 딥러닝 모델이 정상 실행된 것이다.
이제, 복잡한 딥러닝 패키지를 설치하고 설정할 필요없어, 도커 기반으로 딥러닝 모델을 손쉽게 개발할 수 있다.
이 글은 IoT 연결을 위한 언리얼기반 인터넷 서버 데이터 실시간 획득 방법을 간략히 설명한다. 이 글에서 사용하는 방법을 이용해 IoT와 언리얼을 연결해, 물리 세계와 가상 세계를 연결하는 간단한 메타버스, 디지털 트윈 앱을 개발할 수 있다.
물리 세계 - 센서 - 아두이노 - 시리얼포트 - 인터넷 서버 - 언리얼 - 가상 디지털 세계
언리얼 네트워크 통신 방법은 다양하다. 제일 편한 방법은 누군가 만들어놓은 플러그인을 사용하는 것이다. 그럼 코딩없이 블루프린트에서 데이터를 교환할 수 있다. 직접 개발하려면 C++을 사용해야 한다. 이 경우, 라이센스 비용 없이 기능을 구현할 수 있고, 실무적으로는 이런 방식이 복잡한 문제인 경우 유연성이 높아 자주 사용한다.
IoT 장치와 연결하기 위해서는 다양한 데이터소스와 연결하는 프로토콜을 구현해야 한다. 데이터소스는 Serial port, Socket, Websocket, HTTPsocket, MQTT, TCP/IP, UDP 외에 MySQL, MongoDB와 같은 데이터베이스가 될 수도 있다. C++로 구현할 경우, 언리얼 개발사에서 제공하는 예제가 그리 많지 않다. 그래서, 소스코드를 확인하고, 개발 문서를 체크해야 한다.
여기서는 대표적인 방법을 설명한다.
SocketIOClient 방법
이는 기본 제공하는 SocketIOClient 컴포넌트를 블루프린트에서 개발하는 방법이다. 개발 방법을 이해하기 쉽게, IoT에서 데이터를 받아 그 값을 인터넷으로 방송하는 new wind서버를 만든다. new wind 토픽의 값은 아두이노 센서에서 얻은 데이터로 한다. 개발 순서는 다음과 같다.
1. 아두이노를 연결하고 펌웨어 설치(파일>예제>Firmata>StandardFirmataPlus 선택 후 업로드)(참고)
2. Socket이란 이름의 블루프린터를 액터를 파생받아 생성
3. 블루프린트를 다음과 같이 코딩함
4. 서버 개발을 위한 폴더 생성 후 node 패키지 설치
npm install --save express
npm install johnny-five --save
npm install serialport
npm i socket.io
npm i http
npm i util
5. 서버를 server.js이름으로 코딩
해당 패키지의 메뉴얼(Johnney-five manual)을 참고해, 아두이노에서 센서 데이터를 가져와 변수로 저장한 후, 코드에서 io.emit() 에 해당하는 부분에 해당 변수값을 넣어준다.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var util = require('util');
var five = require('johnny-five');// nw.require('nwjs-j5-fix').fix();
이 글은 아두이노와 언리얼(Unreal) 게임엔진을 연결하는 간단한 방법을 설명한다. 이 글은 실시간으로 시리얼 통신에서 얻은 센서값을 언리얼의 액터 메쉬의 머터리얼(재질) 색상에 직접 설정하는 방법을 포함한다. 센서와 언리얼 연결 방법을 보여주기 위해, 별도 상용 언리얼 플러그인을 사용하지 않고, C++에서 통신 모듈을 간단히 개발해 사용한다. 이 글에서 설명하는 같은 방식으로 텍스처, 광원, 메쉬 위치 등을 센서 값에 따라 실시간으로 변경할 수 있다.
이 글의 개발 결과는 다음과 같다. 화면에서 각 큐브는 물리적 센서에서 얻은 데이터값을 색상으로 표시한다. 이를 잘 이용하면, 언리얼을 이용해 IoT와 연결된 디지털트윈(Digital Twin)이나 메타버스(Metaverse)를 쉽게 개발할 수 있다.
언리얼에서 파일을 사용하려면 먼저 임포트해야한다. 콘텐츠 브라우저로 이동하여 가져 오기를 클릭한다. 파일 브라우저를 사용하여 다운로드한 파일이 있는 폴더를 찾는다. 두 파일을 모두 드래그하여 선택 하고 열기를 클릭 한다. Unreal은 .fbx 파일에 대한 몇 가지 가져 오기 옵션을 제공한다.
모델 임포트 옵션
참고로, 파일을 가져올 때 명시적으로 수행할 때까지 실제로 프로젝트에 저장되지 않는다. 다음과 같은 방법으로 파일을 저장할 수 있다 마우스 오른쪽 단추로 클릭 해당 파일을 선택하고 저장을 File>Save All을 선택하여 모든 파일을 한 번에 저장할 수 있다.
Unreal에서는 모델을 메시라고 한다. 메시를 만들었으니 레벨에 배치 할 차례이다.
레벨에 메시를 추가하려면 콘텐츠 브라우저에서 모델을 왼쪽클릭하고 뷰포트로 드래그 한다. 왼쪽 클릭을 해제 하면 메시가 배치된다. 레벨의 개체는 이동 , 회전 및 크기 조정이 가능 한다. 이들의 키보드 단축키는 W , E 및 R 이다.
재질 만들기
모델을 자세히 살펴보면 전혀 노란색이 아님을 알 수 있다. 모델에 약간의 색상과 디테일을 부여하려면 재질을 만들어야한다 .
재질은 표면이 어떻게 보이는지 결정한다. 기본 수준에서 재료는 다음 네 가지를 정의한다.
기본 색상: 표면의 색상 또는 질감이다.
금속성: 표면이 얼마나 "금속과 같은지"를 설정한다. 순수한 금속은 최대 금속 값을 가지며 직물은 0 값을 갖는다.
반사광: 비금속 표면의 광택을 제어한다. 예를 들어 세라믹은 Specular 값이 높지만 점토는 그렇지 않다.
거칠기: 최대 거칠기를 가진 표면은 광택이 없다. 바위와 나무와 같은 표면에 사용된다.
머티리얼을 생성하려면 콘텐츠 브라우저로 이동하여 녹색 새로 추가 버튼을 클릭한다.
생성 할 수 있는 자산 목록 메뉴가 나타난다. 재료를 클릭한다. 재질 이름을 Bulb_Material 로 지정한 다음 파일을 두 번 클릭하여 재질 편집기에서 연다.
재료 편집기는 5개 주요 패널로 구성된다.
그래프: 이 패널는 모든 노드와 결과 노드가 포함된다.
세부 정보: 선택한 모든 노드의 속성이 여기에 표시된다.
뷰포트: 머티리얼을 표시할 미리보기 메시가 포함되어 있다.
팔레트: 재료에 사용할 수 있는 모든 노드 목록이다.
노드는 재료 대부분을 구성한다. 다양한 유형의 노드를 사용할 수 있으며 다양한 기능을 제공한다. 노드는 화살표가있는 원으로 표시되는 입력 및 출력을 가질 수 있다. 입력은 왼쪽에 있고 출력은 오른쪽에 있다.
모델에 색상과 디테일을 추가하려면 텍스처가 필요한다. 텍스처는 2D이미지이다. 일반적으로 3D모델에 투영되어 색상과 세부 정보를 제공한다.
텍스처링하려면 앞서 미리 다운로드한 텍스쳐 jpg 파일을 사용한다. TextureSample과의 노드는 물질 내에서 텍스처를 사용할 수 있다.
Palette 패널로 이동하여 TextureSample을 검색한다. 왼쪽 클릭을 누른 상태에서 그래프로 드래그하여 노드를 추가한다.
디테일 패널로 이동하여 Texture 오른쪽에 있는 드롭 다운을 클릭한다. Texture를 선택 한다. 미리보기 메시에서 텍스처를 보려면 Result 노드에 연결해야 한다. 도구 모음에서 적용을 클릭 하여 재료를 업데이트하고 재료 편집기를 닫는다.
블루 프린트로 사물 객체 만들기
블루 프린트를 사용하면 개체에 대한 사용자 지정 동작을 만들 수 있다. 물체는 물리적인 것 이거나 시스템과 같은 추상적인 것 일 수 있다. 머티리얼과 마찬가지로 블루 프린트는 노드 기반 시스템을 사용한다. 즉, 노드를 만들고 연결하기만 하면 된다. 코딩이 필요하지 않다. 코드 작성을 선호하는 경우 대신 C ++를 사용할 수 있다.
블루 프린트는 사용하기 쉽지만 C ++ 코드만큼 빠르지는 않다. 따라서 복잡한 알고리즘과 같이 계산량이 많은 것을 사용해야하는 경우 C ++ 사용을 고려해야 한다.
콘텐츠 브라우저로 이동하여 새로 추가를 클릭 한다. 목록에서 Blueprint Class를 선택한다. 부모 클래스를 선택하라는 창이 나타난다. 블루 프린트는 선택한 부모 클래스의 모든 변수, 함수 및 구성 요소를 상속한다. 액터를 선택하고 새 파일 이름을 Bulb_Blueprint로 지정 한다.
더블 클릭하여 Bulb_Blueprint 연다. 블루 프린트 편집기 열기를 클릭한다. 블루 프린트 에디터에는 4개 메인 패널이 있다.
My Blueprint : 이 섹션은 주로 그래프, 함수 및 변수를 관리하는 데 사용된다.
세부 정보: 현재 선택한 항목 속성을 표시한다.
그래프: 모든 노드와 로직이 여기에 입력된다.
뷰포트: 메인 에디터 뷰포트와 동일한 컨트롤을 사용하여 이동하고 둘러 볼 수 있다.
컴포넌트: 블루 프린트가 자동차라면 컴포넌트는 자동차를 구성하는 빌딩 블록이다. 구성 요소는 물리적 개체에 국한되지 않는다. 예를 들어 자동차를 움직이기 위해 이동 구성 요소를 추가 할 수 있다. 비행 부품을 추가하여 자동차를 날릴 수도 있다.
여기서 테이블을 만들것이다. 테이블은 두 가지 구성 요소를 사용한다.
원통: 단순한 흰색 원통이다. 이것은 모델이 앉는 기초가 될 것이다.
스태틱 메시: 이 컴포넌트는 모델 메시를 표시한다.
컴포넌트 패널에서 빈 영역을 마우스 왼쪽 버튼으로 클릭하여 Cylinder 구성 요소를 선택한다. 다음으로 Add Component를 클릭하고 목록에서 Static Mesh를 선택한다.
전구 모델을 표시하려면 스태틱 메시 컴포넌트를 선택한 다음 디테일 탭을 클릭한다. Static Mesh 오른쪽에 있는 드롭 다운을 클릭하고 전구 Model을 선택한다.
블루 프린트 노드에는 실행핀이라는 특수 핀이 있다. 왼쪽의 핀은 입력이고 오른쪽의 핀은 출력이다. 예를 들어, 노드 A 와 노드 B 는 입력 핀이 연결되어 있기 때문에 실행된다. 스크립팅을 시작하려면 이벤트 그래프 탭으로 다시 전환한다.
그래프의 빈 공간을 마우스 오른쪽 버튼으로 클릭 하여 사용 가능한 노드 메뉴를 표시한다. AddLocalRotation을 검색한다 . 베이스와 바나나를 회전해야하므로 루트 구성 요소만 회전하면된다. AddLocalRotation (DefaultSceneRoot)을 선택하라. 회전 값을 설정하려면 Delta Rotation 입력으로 이동 하여 Z 값을 1.0으로 변경한다. 그러면 블루 프린트가 Z 축을 중심으로 회전한다. 값이 클수록 턴테이블이 더 빨리 회전한다.
턴테이블을 계속 회전하려면 매 프레임마다 AddLocalRotation 을 호출해야한다. 매 프레임마다 노드를 실행하려면 Event Tick 노드를 사용하라. Event Tick 노드 의 출력 핀을 AddLocalRotation 노드 의 입력 핀으로 드래그한다.
다음과 같이 해당 모델을 선택해, 디테일에서 Movable 옵션을 설정한다.
마지막으로 툴바로 이동하여 컴파일(F7)을 클릭 하여 블루 프린트를 업데이트한 다음, 블루 프린트 편집기를 닫는다. 그럼 회전하는 모델을 확인할 수 있다.
키보드 등 입력 이벤트 처리
3차원 객체와 상호반응하기 위해 이벤트를 처리한다. 언리얼은 모든 입력처리를 UInputComponent에서 처리한다. 모든 액터는 입력 컴포넌트를 가진다. 이 컴포넌트가 있으면 입력 이벤트와 바인딩하는 방법은 동일하다.
언리얼은 다양한 입력과 호환성을 고려해 입력 바인딩 과정을 먼저 거쳐야 입력 이벤트를 액터에 전달할 수 있다.
편집>프로젝트 세팅 메뉴>입력 메뉴를 선택하여 다음과 같이 입력 이벤트 이름과 해당 입력 조합을 설정한다. 아래 경우는 키보드 B를 맵핑하였다.
다음과 같이 앞서 만든 액터 중 하나를 선택해 블루프린트를 편집한다. 우선 액터의 입력을 활성화하고, 입력 이벤트 발생할 때마다 화면에 메시지를 출력한 후 정해진 축으로 이동하도록 하였다.
실행 결과는 다음과 같다.
블루프린트로도 작업할 수 있지만, 내부 동작을 이해하기 위해 C++의 코드를 확인해 보겠다. 예를 들어, Pawn을 기본 클래스로 하는 C++ 클래스인 경우, 다음과 같이 입력이 발생할 경우 특정 함수를 호출해 액터 등을 동작을 시킬 수 있다.