Build Cartographer on ROS1 Noetic

Melodic에선 패키지로 잘 제공되는 Cartographer가 Noetic에선 바이너리 패키지로 제공되지 않는다. 귀찮게도 소스를 받아서 빌드해야 하는데, 다음과 같은 과정으로 설치하면 깔끔하게 처리 가능.

필요한 툴 설치

$ sudo apt-get install -y python3-wstool python3-rosdep ninja-build stow

워크스페이스에 디렉토리를 만들고 필요한 소스를 받아온다.

$ cd ~/catkin_ws/src
$ mkdir cartographer
$ cd cartographer
$ git clone https://github.com/cartographer-project/cartographer.git
$ git clone https://github.com/cartographer-project/cartographer_ros.git

빌드에 필요한 패키지를 자동으로 설치한다.

$ pwd
/home/<user-name>/catkin_ws/src/cartgrapher
$ rosdep install --from-paths . --ignore-src -r -y

libabseil 설치.

$ cd catrographer/scripts
$ ./install_abseil.sh

이제 빌드하면 에러없이 정상적으로 빌드 가능.

$ catkin build

끝.!

socat 사용 예시

socat은 리눅스에서 사용할 수 있는 다목적 릴레이 프로그램이다. 쉽게 얘기해서 Source, Sink 간에 두 개의 단방향 채널을 열어, 서로 연결해준다.

설치는 간단히 apt를 이용하여 설치가 가능하다.

$ sudo apt install socat

사용방법은

$ socat [option] <source> <sink>

와 같이 사용하며, 옵션은 socket -h 를 이용해 확인 가능. 많이 사용하는 것으로는 -d 옵셩는 로그 메시지 출력, -u, -U를 이용해 단방향, 역방향 설정 등이 있다.

source 및 sink에는 linux에서 사용하는 거의 모든 인터페이스들이 적용 가능하다.

  • Files
  • Pipes
  • Devices (serial line, pseudo-terminal, etc)
  • `Sockets (UNIX, IP4, IP6 – raw, UDP, TCP)
  • SSL sockets
  • Proxy CONNECT connections
  • File descriptors (stdin, etc)
  • The GNU line editor (readline)
  • Programs
  • Combinations of two of these

실제 사용 예를 들어본다면,

  • STDIN <-> STDOUT: 키보드 입력, 화면 출력
$ socat -dd STDIN STDOUT
  • TCP 2000포트로 연결하면 자동으로 TCP 22 포트로 연결
$ socat -dd TCP-LISTEN:2000,reuseaddr,fork TCP:0.0.0.0:22
  • TCP 8080포트로 연결하면 자동으로 data.txt 파일을 생성하고 수신받은 내용 저장
$ socat -dd -u TCP-LISTEN:8080,reuseaddr,fork open:data.txt,create,append

이외에도 시리얼포트 등도 가능하여. 간단히 시리얼포트-이더넷 기능 구현 가능하고, .UDP를 TCP로 변환, 그 반대 역시 가능하여, 활용 방법은 무궁무진한다.

하나의 디스크에 여러가지 버전의 Ubuntu 설치하기

ROS를 사용하여 개발을 하다보니, ROS의 버전에 따라 Ubuntu의 배포판이 달라져야 하는 경우가 생긴다. 예를 들자면, ROS2의 현재 최신 LTS버전은 Humble로 Ubuntu 22.04에서 설치가 되어야 하고, ROS1의 최종 LTS버전은 Noetic으로 Ubuntu 20.04에서 지원이 멈춰있다 (이제 더이상 배포판 업데이트 지원 불가).

따라서 이 두가지 경우를 모두 사용하기 위해선, 각각 따로 개발용 PC를 구비하던가 아니면 외장 SSD를 이용하는 방법이 있긴 하지만, 번거로운건 어쩔수 없다.

하나의 SSD에 GRUB 부트로더를 하나만 사용하고, 스왑 파티션도 공용으로 사용하고, 루트 파티션만 분리하여 설치하면 위와 같은 문제가 깔끔이 해결된다. 여전히 재부팅이 필요한 건 어쩔수 없지만..^^

기록 차원에서 설치 순서를 간단히 정리해본다. 윈도우와 듀얼부팅으로 사용할 때도 적용 가능. 물론 배포판의 버전이 달라도 상관없음.

먼저, 가장 최신 버전의 배포판을 설치한다. 이건 여느때와 마찬가지로 그냥 설치하면 됨.

설치할 때, 디스크의 파티션을 설정하는 부분이 있는데, 이때 다음과 같은 구조로 만들어준다.

/dev/sda1    /boot    EFI     2000MB
/dev/sda2    /        ext4    200000MB
/dev/sda3    None     ext4    200000MB
/dev/sda4    swap     swap    32768MB

앞쪽 디스크 이름은 각자의 개발 환경에 따라 다를테니, 유념하시면 되고, 기존과 다른 점은 /boot 파티션을 따로 분리한 것과 루트 (/) 파티션을 만들고 또 동일한 크기 혹은 원하는 크기로 또 하나의 파티션을 만들어 놓은 것이다.

이제 설치를 완료 (루트 파티션을 /dev/sda2로 선택)하고.. 부팅이 제대로 되는 것을 확인한 다음…

다음으로 추가로 설치할 배포판 설치를 진행한다. 이때는 바로 설치를 진행하지 말고, Try Ubuntu 등 일단 임시로 사용할 수 있는 모드로 진입.

여기에선 부트로더 설치를 하지 않고, 배포판 설치를 진행하야 하므로, 터미널을 열고

$ ubiquity -b

와 같이 실행하여 설치를 진행한다. 디스크 설정 시, 설치할 파티션을 위에서 만든 /dev/sda3로 선택한다. 설치가 완료되면, 그냥 재부팅.

재부팅하면, 기존에 설치한 최신 배포판으로 부팅이 될텐데, 완료되면 터미널을 열고,

$ sudo update-grub2

를 하면, 자동으로 방금 전에 설치한 추가 배포판의 커널을 인식하여 GRUB의 부트 엔트리에 등록해준다. 또 기본값으로는 GRUB가 후다닥 지나가버리게 설정되어 있으므로, /etc/default/grub 파일을 열어서 다음과 같이 수정해준다.

# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

이제 끝!

Ubuntu에서 DDM 기능 대체하기

Dell 모니터 중 KVM 기능을 지원하는 제품의 경우, Windows나 macOS에서는 DDM (Dell Display Manager)라는 프로그램을 통해서 모니터를 키보드를 통해 제어할 수 있다.

이를 이용하면 유용한 점이, 모니터를 직접 조작하지 않고도, 입력 소스를 선택할 수 있어 두 대의 PC를 오가며 쉽게 작업할 수 있다는 점이다.

다만 아쉽게도 아직까지 Ubuntu에서 동작하는 DDM은 없는 상황이다. 하지만 구글링을 해보면 쉽게 이를 대체할 수 있는 방법이 나온다.

기본적으로 DDM의 역할은 모니터에 연결된 특정칩에 명령을 전달하는 것인데, 이 명령이 DDC/CI 기능을 이용한다. 아마 게임기나 셋탑박스를 연결할 때, 케이블을 꼽기만 해도 해당 입력소스로 자동으로 전환되는 것을 경험해보셨다면, 그 모니터가 DDC/CI 기능을 지원한다는 의미이다.

Ubuntu에도 이러한 기능을 수행하는 커맨드가 존재하는데, ddccontrol 이 그것이다.

설치는 다음과 같이

$ sudo apt install ddccontrol

설치하면 되고..

이제 실행을 해보면, 먼저 모니터를 인식하는지를 확인해보면…

$ ddccontrol -p
ddccontrol version 0.6.0
Copyright 2004-2005 Oleg I. Vdovikin (oleg@cs.msu.su)
Copyright 2004-2006 Nicolas Boichat (nicolas@boichat.ch)
This program comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of this program under the terms of the GNU General Public License.

Detected monitors :
 - Device: dev:/dev/i2c-6
   DDC/CI supported: Yes
   Monitor Name: VESA standard monitor
   Input type: Digital
  (Automatically selected)
Reading EDID and initializing DDC/CI at bus dev:/dev/i2c-6...
I/O warning : failed to load external entity "/usr/share/ddccontrol-db/monitor/DEL426A.xml"
Document not parsed successfully.
I/O warning : failed to load external entity "/usr/share/ddccontrol-db/monitor/DELlcd.xml"
Document not parsed successfully.

EDID readings:
	Plug and Play ID: DEL426A [VESA standard monitor]
	Input type: Analog

...
= VESA standard monitor
...
> Input settings
	> Input sources
		> id=inputsource, name=Input Source Select (Main), address=0x60, delay=-1ms, type=2
		  Possible values:
			> id=analog - name=Analog, value=1
			> id=digital - name=Digital, value=3
		  supported, value=3855, maximum=14
...

와 같이 연결된 모니터 정보가 인식된다.

이중, 중요한 것이, 연결되어 있는 디바이스 정보인데, 여기에선 /dev/i2c-6으로 되어 있다. 또, Input sources를 설정하는 레지스터 주소가 0x60으로 되어 있는 것을 알 수 있다.

이제 0x60에 특정한 값을 넣으면, 해당되는 입력 소스로 전환할 수 있다는 것인데… 이 특정한 값이 무엇인지를 파악해야 하는데, 역시 검색해보면 답이 바로 나온다.

  • Display Port : 0x0f (15)
  • USB-C : 0x1B (27)
  • HDMI : 0x11 (17)

이다.

즉 터미널에서 다음과 같이 입력하면, USB-C 입력소스로 즉시 전환된다.

$ ddccontrol -r 0x60 -w 27 dev:/dev/i2c-6

이제, 이 명령을 키보드 단축키로 지정하면 쉽게 사용 가능.

설정에서 키보드 > 키보드 단축키 설정 > 사용자 단축키 설정

이제, 위 단축키를 이용하면, 모니터의 입력소스를 조정하여 쉽게 전환이 가능하다.

Dell U2723QE 모니터 구입

새로 만든 개발용PC에 사용할 모니터를 고민하다가, Dell U2723QE 모니터로 결정하고 구입하였음.

Dell UltraSharp 27 4K USB-C 허브 모니터 – U2723QE

출처: Dell.com

화질이야 어짜피 IPS 패널 정도면, 큰 무리는 없을듯 하고… 27인치냐 32인치냐를 한참 고민했었는데… 27인치에서 4k를 Native 해상도로 사용하기엔 살짝 무리가 있고, 32인치는 Native 해상도로 사용하기엔 큰 문제가 없는데, 맥북 등에서 레티나 해상도로 사용하면 글자가 엄청나게 커지니… 또 책상의 크기도 반영하여 27인치로 결정

또 하나 재밌는 기능이 포함되어 있는데, KVM 기능이 내장되어 있음. 모니터 하나를 가지고 2대의 PC에서 사용할 수 있는 기능인데, 모니터의 입력 소스를 선택하면 해당되는 PC로 키보드와 마우스를 자동으로 전환해 줌. 따라서 편하게 메인PC를 사용하다가 노트북을 붙여서 사용할 때도 큰 무리없이 함께 사용 가능. Good!

출처: Dell.com

가격은 공식 홈페이지에선 이상한 가격으로 되어 있는데, 대충 799,000원이 공식 가격인듯… (할인은 얄짤도 없다는…)

작업용 PC 조립

노트북으로는 살짝 사양이 부족하여, 집에서 개인적으로 사용할 PC를 조립하였습니다. 살짝 공돌이 마인드가 발생하여 최적화된 케이스를 찾다가 FormD사의 T1 v2 모델을 선정하였습니다.

Mini ITX보드만 들어갈 수 있고, SFX 파워, 그래픽 카드 등을 넣으면 거의 빈 공간이 없는 작은 사이즈의 케이스인데, 도전하는 마음으로..^^ 국내에는 팔지 않아, 해외 주문을 하였고, 며칠 후 관세 납입하라는 문자가 와서 수령하고 조립 시작..

대충 들어간 부품 목록은

  • CPU: Intel Core i9-12gen 12900KF (엘더레이크)
  • M/B: ASUS ROG STRIX B660-I GAMING WIFI
  • RAM: 삼성전자 DDR5-4800 (32GB) * 2EA
  • POWER: FSP DAGGER PRO 850W GOLD Full Modular (SFF)
  • GPU: COLORFUL 지포스 RTX 3090 Ti 토마호크 EX D6X 24GB
  • AIO: [FRACTAL DESIGN] Lumen S24
  • CASE: FormD T1 – SANDWICH KIT – TITANIUM COLOR
  • Cooler: Noctua NF-A12x15 PWM Premium Quiet Slim Fan 4-Pin x 2EA

케이스는 그야 말로 부품레벨로만 제공되고, 따로 조립 메뉴얼로 첨부되지 않습니다. 유투브의 여러 영상으로 통해 참고하는 수 밖에는 없었고, 조립을 하다가 여러번 나사를 풀고 조이는 작업을 반복해야 합니다. (상황에 따라)

꾸역꾸역 부품 집어넣고, 케이블 정리하고, 부팅까지 되는걸 확인하고 하니, 꽤나 만족스럽습니다. 수냉 쿨러 특성상 열이 발생해도 소음은 조용한 수준입니다.

본체 크기는 저 정도입니다. 아주 작은 크기이고, 사방으로 타공되어 있어 공기 순환도 아주 잘되는 편입니다.

Unityhub 설치 on Ubuntu 22.04

Unity를 사용하기 위해 Ubuntu 22.04에서 Unityhub 설치 과정을 진행해도 Unityhub가 실행되지 않는다.

검색해본 결과, libssl 버전이 안맞아서 그런 것인데, Ubuntu 22.04에 설치되는건 libssl3이고 Unityhub가 사용하는건 libssl1 이다.

간단히 libssl1을 받아서 설치해주면 해결됨.

$ wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
$ sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb

Unityhub 설치 과정은…

$ sudo sh -c 'echo "deb https://hub.unity3d.com/linux/repos/deb stable main" > /etc/apt/sources.list.d/unityhub.list'
$ wget -qO - https://hub.unity3d.com/linux/keys/public | sudo tee /etc/apt/trusted.gpg.d/unityhub.asc
$ sudo apt update
$ sudo apt install unityhub

WSL2에서 외부네트웍에 브릿지하여 사용하기

WSL2에서 Ubuntu 등을 설치하면 기본적으로는 내부 프라이빗 네트워크망을 사용한다. 즉, 아이피 대역이 아예 다른 서브네트웍 망을 생성하여 사용한다는 얘기.

따라서 ipconfig를 이용하여 할당된 IP를 확인해보면,

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.24.175.35  netmask 255.255.240.0  broadcast 172.24.175.255
        inet6 fe80::215:5dff:fea8:1061  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:a8:10:61  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 7  bytes 586 (586.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

와 같이 172.24.175.35 등으로 할당된다.

여러가지 이유로 윈도우에서 접속되어 있는 외부네트웍을 접속하고, 또 외부에서 WSL2 안의 시스템에 접속하고 싶을 때가 있는데, 이때는 WSL2의 Bridge 기능을 사용하면 된다.

먼저 Hyper-V 매니저를 설치하고, 실행한다. Windows 추가 기능 관리자를 사용하여 설치할 수 있다.

설치하고, 재부팅을 완료한 이후에, Hyper-V 매니저를 실행하면, 다음과 같이 보일텐데…

메뉴의 Action > Virtual Switch Manager를 실행한다.

이제 New virtual network switch를 클릭고, 외부 네트웍에 브릿지 해야 하므로 External를 선택한 후, Create Virtual Switch를 클릭한다.

이름은 적당히 입력하고 (나중에 사용해야 하므로 적당히…), Connection Type에서 External Network를 선택하고, 현재 외부망에 연결되어 있는 네트웍카드를 선택한다. (예시의 경우엔, 현재 노트북을 이용하여 무선랜으로 외부망에 접속중이므로, 무선랜카드를 선택하였음.)

Apply를 눌러 적용한다.

자, 이제 현재 사용자의 홈디렉토리에 .wslconfig 파일을 생성하고, 다음과 같이 입력한 후, 저장한다.

[WSL2]
networkingMode = bridged
vmSwitch = New Virtual Switch

vmSwitch의 이름은 아까 전 단계에서 생성했던 Virtual Network Switch 이름을 입력하면 된다.

PowerShell를 관리자 권한으로 실행하여 다음과 같이 WSL2를 아예 종료한다.

PS C:\WINDOWS\system32> .\wsl.exe --shutdown

이제 WSL2를 다시 실행한 다음, ifconfig를 실행해 보면,

~$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.100.193  netmask 255.255.255.0  broadcast 192.168.100.255
        ether 5c:bb:f6:9e:ee:fa  txqueuelen 1000  (Ethernet)
        RX packets 2  bytes 684 (684.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 684 (684.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

정상적으로 외부 네트웍망의 아이피를 할당받았음을 알 수 있다.
끝!

WSL2에서 윈도우에 연결된 USB 장치 사용하기

공식적으론 지원하지 않음. 하지만 오픈소스로 개발된 툴을 설치하면 윈도우에 연결된 USB 장치를 WSL2 내부에서 사용 가능.

과정 정리.

일단 WSL2는 설치하였고, 여기에선 Ubuntu 20.04.4 LTS를 설치하였음.

먼저 usbipd (https://github.com/dorssel/usbipd-win) 설치

https://github.com/dorssel/usbipd-win/releases/latest 에서 최신 버전의 설치 파일을 받아서 설치. 설치후 재부팅 필요.

윈도우에서 Power Shell을 관리자 권한으로 실행. 터미널 화면에서,

PS C:\WINDOWS\system32> usbipd wsl list
BUSID  VID:PID    DEVICE                                                        STATE
2-1    27c6:533c  Goodix fingerprint                                            Not attached
2-5    0c45:6a0c  Integrated Webcam                                             Not attached
2-13   10c4:ea60  Silicon Labs CP210x USB to UART Bridge (COM3)                 Not attached
2-14   8087:0026  Intel(R) Wireless Bluetooth(R)                                Not attached
3-2    2188:6533  CalDigit Thunderbolt 3 Audio, USB Input Device                Not attached
4-2    0853:0148  USB Input Device                                              Not attached
4-8    2188:0747  USB Mass Storage Device                                       Not attached
9-1    046d:c539  USB Input Device                                              Not attached
9-2    413c:b080  Dell DA20 Adapter                                             Not attached

와 같이 현재 윈도우PC에 연결된 USB 장치 리스트들이 보임. 오른쪽에는 현재 WSL에 연결되어 있는지 여부가 표시됨.

WSL2의 Ubuntu 20.04를 실행하고, 다음과 같이 usbipd 클라이언트 패키지를 설치한다.

$ sudo apt install linux-tools-virtual hwdata
$ sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20

이제 다시 파워셀로 돌아가서, 다음과 같이 WSL2에 연결될 디바이스를 attach 해줌.

PS C:\WINDOWS\system32> usbipd wsl attach --busid=2-13

버스아이디 번호는 위 리스트에서 참고.

WSL2의 Ubuntu에서 dmesg로 연결을 확인해보면,

...
[  981.156497] vhci_hcd vhci_hcd.0: pdev(0) rhport(0) sockfd(3)
[  981.157731] vhci_hcd vhci_hcd.0: devid(131085) speed(2) speed_str(full-speed)
[  981.159973] vhci_hcd vhci_hcd.0: Device attached
[  981.438826] vhci_hcd: vhci_device speed not set
[  981.508832] usb 1-1: new full-speed USB device number 3 using vhci_hcd
[  981.588445] vhci_hcd: vhci_device speed not set
[  981.658522] usb 1-1: SetAddress Request (3) to port 0
[  981.730925] usb 1-1: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice= 1.00
[  981.731956] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  981.732837] usb 1-1: Product: CP2102 USB to UART Bridge Controller
[  981.733628] usb 1-1: Manufacturer: Silicon Labs
[  981.734221] usb 1-1: SerialNumber: 0001
[  981.744934] cp210x 1-1:1.0: cp210x converter detected
[  981.751507] usb 1-1: cp210x converter now attached to ttyUSB0

와 같이 정상적으로 연결되고, /dev에 장치가 생성됨을 볼 수 있음. 연결을 해제하려면 파워쉘에서

PS C:\WINDOWS\system32> usbipd wsl detach --busid=2-13

를 입력하면,

[ 1048.211316] vhci_hcd: connection closed
[ 1048.211480] vhci_hcd: stop threads
[ 1048.212982] vhci_hcd: release socket
[ 1048.213452] vhci_hcd: disconnect device
[ 1048.214003] usb 1-1: USB disconnect, device number 3
[ 1048.214792] cp210x ttyUSB0: cp210x converter now disconnected from ttyUSB0
[ 1048.215669] cp210x 1-1:1.0: device disconnected

정상적으로 연결 해제.

이제 실제 리눅스 시스템에서와 마찬가지로 장치 사용 가능.

덧.

장치를 attach 하는 도중 다음과 같은 에러가 발생하면,

usbipd: error: WSL kernel is not USBIP capable; update with 'wsl --update'.

위에서 제시한 해결 방법대로, wsl를 업데이트 해주면 됨.

참고링크:

Ubuntu 콘솔모드 부팅시 /dev/ttyUSB0가 저절로 끊기는 현상

Ubuntu를 콘솔모드로 부팅할 경우, USB Serial 장비 중 /dev/ttyUSB0를 사용하는 장치가 저절로 끊기는 현상이 발생함.

부팅 이후 USB 장치를 다시 연결하면 정상적으로 사용할 수 있지만, 부팅시 항상 연결 후 끊기는 현상이 발생하여 커널 메시지를 확인한 결과….

[    3.840033] usbcore: registered new interface driver cp210x
[    3.840041] usbserial: USB Serial support registered for cp210x
[    3.840063] cp210x 3-3.4:1.0: cp210x converter detected
[    3.840244] mei_hdcp 0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04: bound 0000:00:02.0 (ops i915_hdcp_component_ops [i915])
[    3.841142] usb 3-3.4: cp210x converter now attached to ttyUSB0

...

[    4.542479] usb 3-3.4: usbfs: interface 0 claimed by cp210x while 'brltty' sets config #1
[    4.543277] cp210x ttyUSB0: cp210x converter now disconnected from ttyUSB0
[    4.543298] cp210x 3-3.4:1.0: device disconnected

대략 살펴보면, brltty라는 녀석이 실행되면서 USB 인터페이스 0번을 선점하려고 함. 이로 인해 연결이 끊기는 결과를 보임.

간단히 해결 방법은 brltty라는 녀석을 지우면 됨.

brltty가 하는 역할을 살펴보니, Ubuntu 콘솔모드로 부팅시 시각 장애인을 위해 화면 내용을 읽어주는 braille display라는 장비를 운용하기 위한 프로그램임.

$ sudo apt purge brltty

끝.!

RPi SD 카드 백업

RPi 보드에 SD카드를 삽입하고 작업을 진행한 이후, 특정 상태에서 백업을 해놓으면 추후에 반복되는 작업 시간을 줄일 수 있음.

방법은 사용하는 운영체제에 따라 다름.

Windows

Windows에서는 Win32 Disk Imager 사용을 권장함. 해당 링크에서 프로그램을 다운 받아 원본 SD 카드를 넣고 이미지 파일로 백업. 추후 새로운 SD 카드를 넣고 백업해 놓은 이미지 파일을 이용해 쓰기를 하면 끝.

Ubuntu

ubuntu에서는 터미널에서 dd 명령어를 이용하는게 편함. SD카드를 삽입하고, 마운트 된 파티션을 umount 해준 다음,

$ sudo umount /dev/mmcblk0
$ sudo dd if=/dev/mmcblk0 of=~/backup_sdcard_lrcharger1500w.img bs=1M status=progress

이렇게 하면 파일로 백업, 다시 새로운 카드에 쓰려면,

$ sudo umount /dev/mmcblk0
$ sudo dd if=~/backup_sdcard_lrcharger1500w.img of=/dev/mmcblk0 bs=1M status=progress

끝!

Ubuntu에서 udev를 이용한 장치 관리

udev는 Linux 서브시스템으로 장치 관리를 위해 사용한다.

udev는 udev rules를 이용해 설정이 가능한데, 이를 이용하면 사용자의 입맛에 맞게 장치의 이름 및 초기화 기능을 수행할 수 있다.

udev rules 파일은 /etc/udev/rules.d에 위치하면 된다. 파일 이름의 형식은 00-<udev_rule_name>.rules로 구성되는데, 앞의 두 숫자는 우선 순위를 나타내고 숫자가 낮을수록 먼저 실행된다.

룰 파일 내부에서는 라인별로 실행이 되며, 대략 내용을 살펴보면

KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="ttyPIO"
KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_USB_INTERFACE_NUM}=="00", SYMLINK+="ttyCHARGER"
KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="ttyHMI"
#KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyCHARGER%E{ID_USB_INTERFACE_NUM}"

와 같다.

자세한 문법은 https://www.thegeekdiary.com/beginners-guide-to-udev-in-linux/를 참고하면 될듯.

팁.

USB 장치의 경우, idProduct, idVendor 등의 번호가 동일한 제품이 존재할 수 있는데, 이때는 ID_USB_INTERFACE_NUM를 사용하면, USB 허브 및 포트에 꼽힌 순서대로 인덱싱 번호를 얻을 수 있다. 이를 이용하면, 해당되는 장치에 대한 설정 가능

KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ENV{ID_USB_INTERFACE_NUM}=="01", SYMLINK+="ttyPIO"

끝.

SES Pro Screwdriver

예전부터 이상하게 이쁜 공구만 나오면 수집하는 취미가 있는데, 인터넷 서핑 중 이쁜 드라이버 세트를 발견! 게다가 토크 조절도 가능하단다!!?!? 원래는 킥스타터 등 클라우드 펀딩에서 성공적으로 데뷔하고, 현재는 ARRORMAX에서 판매하고 있음. 주문하면 거의 그 즉시 쉬핑되긴하는데, 코로나 시국이라 그런지 DHL 배달이 살짝 느린 기분.

https://www.arrowmax-rc.com/xcart/arrowmax-ses-electric-screwdriver-with-alu-case-34-in-1-space-gray-am-199912-g-.html

가격이 살짝 나가긴 하지만 일단 주문하고, 기다렸다가 얼마전에 받고 개봉 완료.. 외부 포장 케이스는 아래와 같고,

실제로 갖고 다닐 케이스는 회색 계열의 알루미늄 케이스이고, 드라이버가 살짝 보이게끔 되어 있음.

드라이버 비트는 총 34개가 들어 있고, 특성상 큰 크기의 비트는 존재하지 않음. 기계쪽보단 보드나 작은 전자제품 용도로만 사용해야 할 듯. 전원을 키고, 회전 동작에 따라 드라이버 비트가 회전하고 (처음에 살짝 헷갈림), 정해진 토그 이상이 부하 걸리면 멈춤.

끝!

워드프레스 테마 변경

얼마전 워드프레스가 5.9 버전으로 바뀌면서, 전체 싸이트 구조를 기존 테마와는 달리 블록으로 사용자가 직접 디자인 할 수 있도록 변경되었다.

이 블로그도 Twenty Seventeen 테마를 살짝 변형하여 사용하고 있었는데, 지금까지 귀차니즘에 변경하지 않고 버텨왔는데 삽질하는게 나름 취미라…

Twenty Twenty Two 테마로 변경하고 하나씩 살펴보고 있는 중. 귀찮은 면도 있긴한데 하나씩 바꿔가면서 변경되는게 재밌긴하다. HTML, CSS에 대한 지식이 별로 없어도 마우스로 클릭클릭해가면서 디자인하는 것을 보면, 참 세상이 좋아졌구나~ 라는 생각도.

슬슬 또 내용을 채워나가야 할듯.

Ubuntu를 Console/Text 모드로 부팅하기

로봇에 설치된 Ubuntu의 경우, 기존과 같이 Gnome으로 부팅하면 부팅시간도 오래 걸릴뿐 아니라, 기본 상태에서의 램 소모량도 크기 때문에 Console/Text 모드로 부팅하는 것이 유리하다.

이를 변경하기 위해선, 간단히 Terminal을 열고 다음과 같이 입력하고 재부팅한다.

$ sudo systemctl set-default multi-user.target

다시 Graphical Mode로 원상복구 하려면, Terminal에서 다음과 같이 입력하고 재부팅한다.

$ sudo systemctl set-default graphical.target

끝.

Set CPU governor policy always “Performance” on Ubuntu

$ sudo apt-get install cpufrequtils

/etc/default/cpufrequtils 파일 생성. 있으면 수정.

$ sudo nano /etc/default/cpufrequtils

GOVERNOR="performance"
MIN_SPEED="2000MHz"

MIN_SPEED는 각자의 CPU 성능에 따라 설정함.

ondemand 서비스를 비활성화

$ sudo systemctl disable ondemand

파일이 생성되면, cpufrequtils 서비스를 재실행

$ sudo systemctl restart cpufrequtils

이제 cpufreq-info를 통해 확인해보면…

$ cpufreq-info                                                                                                                                                         
cpufrequtils 008: cpufreq-info (C) Dominik Brodowski 2004-2009                                                                                                                                          
Report errors and bugs to cpufreq@vger.kernel.org, please.                                                                                                                                              
analyzing CPU 0:                                                                                                                                                                                        
  driver: intel_pstate                                                                                                                                                                                  
  CPUs which run at the same hardware frequency: 0                                                                                                                                                      
  CPUs which need to have their frequency coordinated by software: 0                                                                                                                                    
  maximum transition latency: 4294.55 ms.                                                                                                                                                               
  hardware limits: 800 MHz - 5.10 GHz                                                                                                                                                                   
  available cpufreq governors: performance, powersave                                                                                                                                                   
  current policy: frequency should be within 3.00 GHz and 5.10 GHz.                                                                                                                                     
                  The governor "performance" may decide which speed to use                                                                                                                              
                  within this range.                                                                                                                                                                    
  current CPU frequency is 4.91 GHz.                                                                                                                                                                    
analyzing CPU 1:                                                                                                                                                                                        
  driver: intel_pstate                                                                                                                                                                                  
  CPUs which run at the same hardware frequency: 1                                                                                                                                                      
  CPUs which need to have their frequency coordinated by software: 1                                                                                                                                    
  maximum transition latency: 4294.55 ms.                                                                                                                                                               
  hardware limits: 800 MHz - 5.10 GHz                                                                                                                                                                   
  available cpufreq governors: performance, powersave                                                                                                                                                   
  current policy: frequency should be within 3.00 GHz and 5.10 GHz.                                                                                                                                     
                  The governor "performance" may decide which speed to use                                                                                                                              
                  within this range.                                                                                                                                                                    
  current CPU frequency is 4.73 GHz.           

위와 같이 performance 모드로 셋팅되어 있고, CPU frequency가 올라가 있음을 확인할 수 있음.

Ubuntu 20.04에서 Blender 최신 버전 설치

추가:

아래의 PPA는 2.92가 최종빌드임. 현재 LTS 버전인 3.3.1을 설치하기 위해선 현재로선 snap를 사용하여 설치하는게 최선인듯.

또다른 PPA를 추가하는 방법이 있으나, 다른 패키지와의 의존성이 아주 많이 발생하여 추가로 에러가 발생함.


Blender PPA 추가

$ sudo add-apt-repository ppa:thomas-schiex/blender

설치

$ sudo apt update
$ sudo apt install blender

최초 설치시 라이브러리 관련 문제로 실행되지 않음. 의존 라이브러리 설치 필요

$ sudo apt install libllvm6.0

설치 완료.

chrony를 이용한 로컬 네트웍 기기 간 시간 동기화

리눅스 시스템에서 시간 동기화를 위해 ntp를 많이 사용한다. ntp를 이용하여 기기간 시간 동기화를 할 수도 있고, 인터넷에 많은 방법이 나와 있다.

chrony는 ntp의 개선 버전이다. 사용 방법도 ntp와 유사하고, 심지어 ntp와 호환도 된다.

로봇 시스템의 특성상 인터넷에 연결되어 있지 않고, 로컬 네트웍 망만 구축되어 있는 경우, 하나의 PC를 기준으로 삼아 다른 기기의 시간을 동기화 할 수 있다.

작업 순서


기준 PC

chrony 설치

$ sudo apt install chrony

서버 설정

$ sudo vi /etc/chrony/chrony.conf 

pool <기준PC IP주소> iburst maxsources 1

keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift

local stratum 10
allow 192.168.0.0/16

logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1 3

chrony 데몬 서비스 시작

$ sudo systemctl restart chronyd.service

chrony 서비스 시작

$ sudo systemctl restart chrony.service

chrony 서비스 동작 확인

$ watch chronyc tracking

Every 2.0s: chronyc tracking

Reference ID    : C0A80B36 (byeongkyu-XPS-17-9700)
Stratum         : 11
Ref time (UTC)  : Wed Apr 14 06:19:50 2021
System time     : 0.000000000 seconds slow of NTP time
Last offset     : +0.000002941 seconds
RMS offset      : 0.000002941 seconds
Frequency       : 7.161 ppm slow
Residual freq   : -0.063 ppm
Skew            : 9.612 ppm
Root delay      : 0.000010132 seconds
Root dispersion : 0.000493094 seconds
Update interval : 0.0 seconds
Leap status     : Normal


슬레이브 PC

chrony 설치는 동일.

설정파일

$ sudo vi /etc/chrony/chrony.conf

pool <기준PC IP주소> iburst maxsources 1

keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift

logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1 3

chrony 데몬 서비스 시작

$ sudo systemctl restart chrony.service

chrony 동작 확인

$ watch chronyc tracking

Every 2.0s: chronyc tracking
 
Reference ID    : C0A80B36 (192.168.11.54)
Stratum         : 12
Ref time (UTC)  : Wed Apr 14 06:26:28 2021
System time     : 0.000805050 seconds fast of NTP time
Last offset     : +0.001296174 seconds
RMS offset      : 0.005739228 seconds
Frequency       : 2.246 ppm slow
Residual freq   : +0.130 ppm
Skew            : 2.884 ppm
Root delay      : 0.009987777 seconds
Root dispersion : 0.003204302 seconds
Update interval : 260.5 seconds
Leap status     : Normal

끝.!

screen 사용 방법 정리

원격의 로봇에 ssh로 접속하여 작업을 수행할 경우,

  • 여러 개의 터미널이 필요하다면?
  • 어떤 작업을 수행해 놓고, ssh 접속을 끊고 싶다면? (물론 작업은 계속 수행하도록)

여러개의 터미널은 tmux로도 해결이 가능하지만, 작업을 수행해 놓고 ssh 접속만 끊고 싶다면 screen이 해결 방법이 될 수 있음.

설치

$ sudo apt install screen

별다른 설정은 필요하지 않고, 단순히 명령만 입력하면 실행. 옵션이 다음과 같이 있지만, 몇 개만 알면 사용하는데는 문제 없음.

$ screen -h
 Use: screen [-opts] [cmd [args]]
  or: screen -r [host.tty]
 Options:
 -4            Resolve hostnames only to IPv4 addresses.
 -6            Resolve hostnames only to IPv6 addresses.
 -a            Force all capabilities into each window's termcap.
 -A -[r|R]     Adapt all windows to the new display width & height.
 -c file       Read configuration file instead of '.screenrc'.
 -d (-r)       Detach the elsewhere running screen (and reattach here).
 -dmS name     Start as daemon: Screen session in detached mode.
 -D (-r)       Detach and logout remote (and reattach here).
 -D -RR        Do whatever is needed to get a screen session.
 -e xy         Change command characters.
 -f            Flow control on, -fn = off, -fa = auto.
 -h lines      Set the size of the scrollback history buffer.
 -i            Interrupt output sooner when flow control is on.
 -l            Login mode on (update /var/run/utmp), -ln = off.
 -ls [match]   or
 -list         Do nothing, just list our SockDir [on possible matches].
 -L            Turn on output logging.
 -Logfile file Set logfile name.
 -m            ignore $STY variable, do create a new screen session.
 -O            Choose optimal output rather than exact vt100 emulation.
 -p window     Preselect the named window if it exists.
 -q            Quiet startup. Exits with non-zero return code if unsuccessful.
 -Q            Commands will send the response to the stdout of the querying process.
 -r [session]  Reattach to a detached screen process.
 -R            Reattach if possible, otherwise start a new session.
 -s shell      Shell to execute rather than $SHELL.
 -S sockname   Name this session .sockname instead of ...
 -t title      Set title. (window's name).
 -T term       Use term as $TERM for windows, rather than "screen".
 -U            Tell screen to use UTF-8 encoding.
 -v            Print "Screen version 4.08.00 (GNU) 05-Feb-20".
 -wipe [match] Do nothing, just clean up SockDir [on possible matches].
 -x            Attach to a not detached screen. (Multi display mode).
 -X            Execute  as a screen command in the specified session.

실행시 screen 세션 이름 설정하기. 기본으로 부여되는 이름이 있지만 외우기 어려우므로, 사용자가 직접 지정 가능.

$ screen -S <session-name>

실행되어 있는 상태에서 세션과의 연결 끊기

Ctrl + A, d

실행되어 있는 세션에 다시 연결하기

$ screen -r <session-name>

실행되어 있는 세션 리스트 보기

$ screen -ls

팁.

screen를 실행해서 들어가면, 터미널 설정이 변경되어 PS1이 리셋되는 현상이 있는데, 이를 방지하려면 ~/.screenrc 파일에 다음과 같이 입력

$ vi ~/.screenrc

term screen-256color

screen를 실행하면, 이전 화면을 보는게 지원안됨. screen 내부 명령으로 해결해야 되는데,

Ctrl + A, Esc

이 상태에서, PgUp/PgDown, Up/Down으로 스크롤 가능.

끝!.

Install Ubuntu 20.04 on Windows 10

지난번 WSL2에서 좀더 업데이트 해서…

새로 인스톨 하는 경우. Windows PowerShell을 Administrator 권한으로 실행.

> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

위 명령을 실행하면, WSL 기능이 활성화 되고 자동으로 재부팅.

다음으로 VirtualMachinePlatform 기능 활성화. 마찬가지로 Windows PowerShell을 Administrator 권한으로 실행해야함.

> Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

Windows Store에서 Ubuntu 20.04를 선택하여 설치.

설치된 Ubuntu 20.04를 wsl2로 변환

> wsl --set-version Ubuntu-20.04 2

시간이 좀 흐르면…

> wsl --set-version Ubuntu-20.04 2                                                                                                                                                                                        Conversion in progress, this may take a few minutes…                                                                                                                                                                                          For information on key differences with WSL 2 please visit https://aka.ms/wsl2                                                                                                                                                                  Conversion complete.                  

와 같이 나오고 종료. 이제 시작메뉴에서 Ubuntu 20.04를 실행하면,

와 같이 나오고, 이제 실제 Ubuntu 사용하는 것과 동일하게 사용 가능.

STM32CubeIDE에서 printf를 USART와 연결하기

printf 출력을 USART와 연결하여 사용 가능. 디버깅 시 상당한 이점을 가질 수 있음.

main.c 파일에 다음의 함수를 추가.

이때 uart의 인스턴스를 설정해야 하는데, STM32F746G-DISCO 보드에선 USART1번이 ST-Link의 Virtual Com Port와 연결되어 있음. 따라서 huart1를 선택.

int __io_putchar(int ch) 
{
     (void) HAL_UART_Transmit(&huart1, (uint8_t*) &ch, 1, 100);
     return ch;
}

프로젝트 설정에서 printf 문에 float 구문을 사용할 수 있도록 설정.

덧.

위 방법을 사용할 때, FreeRTOS 환경 내에선 알 수 없는 이유로 동작이 되지 않음. 메모리 관련 (heap 등등)의 문제로 추정된다는데… 이를 해결하기 위해선 경량화 되어 구현된 printf 함수를 사용하면 됨.

https://github.com/mpaland/printf

이 Repository의 printf.h, printf.c 파일을 해당 프로젝트에 추가하고, printf.h를 인클루드하여 사용.

사용자의 환경에 따라 _putchar 함수를 구현하면 됨.

void _putchar(char c)
{
    /* Place your implementation of fputc here */     
    /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */     
    HAL_UART_Transmit(&huart1, (uint8_t) &c, 1, 0xFFFF);
}

끝!

Set static IP on Jetson Nano/Jetpack

Install netplan

$ sudo apt install netplan.io

Add yaml to /etc/netplan/config.yaml

network:
   version: 2
   renderer: networkd
   ethernets:
     eth0:
       addresses:
         - 192.168.10.99/24
       gateway4: 192.168.10.1
       nameservers:
           addresses: [8.8.8.8, 8.8.4.4]

Save, and apply.

$ sudo netplan apply

That’s it.

No Sound, XPS 17 9700 on Ubuntu 20.04

업데이트

문제가 해결된 커널 패키지가 제공되므로, 간단히 설치하면 하면 문제가 해결됨.

$ sudo apt-get install linux-oem-20.04b

새로 구입한 XPS 17 9700 노트북에 Ubuntu 20.04를 설치하여 사용하면 사운드 카드가 지원되지 않아 소리를 재생 할 수 없다.

구글링 해보니 같은 문제로 문의하는 사람이 엄청하게 많음.

해결방법

  • linux-oem-20.04 패키지 설치
$ sudo apt install linux-oem-20.04

기존 설치된 sof 펌웨어 삭제

$ sudo rm -rf /usr/lib/firmware/intel/sof /usr/lib/firmware/intel/sof-tlpg

새로운 펌웨어 받아오기

$ wget https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/sof-firmware-1.6.1-1-any.pkg.tar.zst

압축을 풀고 sof, sof-tlpg 디렉토리를 원래의 위치로 복사

$ cd /usr/lib/firmware/intel
$ sudo cp -r ~/Downloads/sof-firmware-1.6-2-any.pkg/usr/lib/firmware/intel/* .

기존 sof-soundwire 파일 삭제

$ cd /usr/share/alsa/ucm2/sof-soundwire
$ sudo rm -rf *

새로운 sof-soundwire 파일 추가

압축을 풀고 나온 파일을 기존 디렉토리에 복사

$ cd /usr/share/alsa/ucm2/sof-soundwire
$ sudo cp ~/Downloads/sof-soundwire/* .

작업이 완료되면, 재부팅.

/dev/tty* and /dev/ttyUSB* 등에 대한 퍼미션 설정

Ubuntu에서 시리얼 통신이나 기타 USB 디바이스를 이용할 경우, 기본 권한 및 퍼미션이 슈퍼 유저만 사용 가능하도록 설정되어있다. 따라서 매번 sudo 명령을 이용해 사용자 프로그램을 실행하거나, sudo chmod 명령으로 일반 사용자도 읽고 쓸수 있도록 설정해줘야 한다.

먼저 /dev/tty* 에 대한 권한 및 퍼미션 해결 방법. 사용자 계정을 dialout, tty 그룹에 추가해 준다.

$ sudo usermod -a -G dialout $USER
$ sudo usermod -a -G tty $USER

다음으로 USB 디비이스에 대한 해결 방법.

udev 룰을 추가해준다.

/etc/udev/rules.d 디렉토리에 안에 임의의 룰 파일을 생성해준다. 보통 앞의 숫자는 우선 순위이고, 확장자는 .rules 를 사용한다.

예) /etc/udev/rules.d/50-ttyusb.rules

KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403"

또, USB 디바이스가 연결될 때, 특정 작업을 수행하려면, RUN 옵션을 추가한다.

KERNEL=="ttyUSB[0-9]*", MODE="0666", ATTRS{idVendor}=="0403" ATTRS{idProduct}=="6001" RUN+="/bin/setserial -v /dev/%k low_latency"

idVendor나 idProduct는 기기에 따라 다르므로, 이를 확인하려면 https://ahnbk.com/?p=296 포스트를 참고.

ROS1에서 launch 파일을 부팅시에 자동으로 실행하기

개발을 완료하고, launch 파일을 부팅시에 자동으로 실행하도록 하려면 systemd 서비스를 사용해야 한다.

먼저 실행 스크립트를 작성한다(스크립트의 위치는 임의의 위치여도 상관없음)

~/test.sh

#!/bin/bash
source /home/byeongkyu/.bashrc
source /opt/ros/noetic/setup.bash
roslaunch roscpp_tutorials talker_listener.launch

다음으로 systemd 서비스 파일을 생성한다. 서비스 파일의 위치는 /etc/systemd/system 내에 위치한다.

/etc/systemd/system/bringup_ros.service

[Unit]
Description=Bringup ROS launch Test

[Service]
ExecStart=/home/byeongkyu/test.sh
Restart=on-abort

[Install]
WantedBy=multi-user.target

이제 systemd의 데몬을 재시작해주고,

$ sudo systemctl daemon-reload

생성한 서비스를 시작한다.

$ sudo systemctl start bringup_ros.service

서비스의 상태를 확인하면,

$ sudo systemctl status bringup_ros.service
● bringup_ros.service - Bringup ROS launch Test
Loaded: loaded (/etc/systemd/system/bringup_ros.service; disabled; vendor preset: enabled)
Active: active (running) since Tue 2020-10-20 13:55:10 KST; 14s ago
Main PID: 35949 (test.sh)
Tasks: 26 (limit: 18700)
Memory: 57.9M
CGroup: /system.slice/bringup_ros.service
├─35949 /bin/bash /home/byeongkyu/test.sh
├─35982 /usr/bin/python3 /opt/ros/noetic/bin/roslaunch roscpp_tutorials talker_listener.launch
├─35990 /usr/bin/python3 /opt/ros/noetic/bin/rosmaster --core -p 11311 -w 3 __log:=/root/.ros/log/6ea0106a-1290-11eb-804a-ffcb70481544/master.log
├─36000 /opt/ros/noetic/lib/rosout/rosout __name:=rosout __log:=/root/.ros/log/6ea0106a-1290-11eb-804a-ffcb70481544/rosout-1.log
├─36003 /opt/ros/noetic/lib/roscpp_tutorials/listener __name:=listener __log:=/root/.ros/log/6ea0106a-1290-11eb-804a-ffcb70481544/listener-2.log
└─36005 /opt/ros/noetic/lib/roscpp_tutorials/talker __name:=talker __log:=/root/.ros/log/6ea0106a-1290-11eb-804a-ffcb70481544/talker-3.log
Oct 20 13:55:23 byeongkyu-XPS-15-7590 test.sh[36003]: [ INFO] [1603169722.778710976]: I heard: [hello world 116]
Oct 20 13:55:23 byeongkyu-XPS-15-7590 test.sh[36003]: [ INFO] [1603169722.878691038]: I heard: [hello world 117]
Oct 20 13:55:23 byeongkyu-XPS-15-7590 test.sh[36003]: [ INFO] [1603169722.978902069]: I heard: [hello world 118]

정상적으로 실행되고 있음을 확인할 수 있다.

이제 부팅시에 시작하도록 하려면, 서비스를 enable 해주면 끝.

$ sudo systemctl enable bringup_ros.service

이제 부팅할 때, 서비스가 자동으로 실행됨.

Install CUDA, cuDNN, Nvidia Driver on Ubuntu 20.04 for Tensorflow2

기존에 설치되어 있는 것들 제거. Ubuntu 20.04에는 기본으로 Nvidia Driver가 설치되어 있으므로 이를 지워주어야 함.

$ sudo apt-get purge nvidia*
$ sudo apt-get autoremove
$ sudo apt-get autoclean

CUDA 설치

1. 레포지토리 키 등록

$ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/7fa2af80.pub

2. apt에 레포지토리 주소 등록

$ sudo bash -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 /" >> /etc/apt/sources.list.d/cuda.list'
$ sudo bash -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" >> /etc/apt/sources.list.d/cuda.list'

3. CUDA, Nvidia Driver 설치 (CUDA를 설치하면 자동으로 이에 의존된 드라이버 자동으로 설치)

$ sudo apt update
$ sudo apt install cuda
$ sudo apt install cuda-10-1

4. cuDNN 설치를 위한 repo 패키지 설치

$ wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu2004/x86_64/nvidia-machine-learning-repo-ubuntu2004_1.0.0-1_amd64.deb
$ sudo dpkg -i nvidia-machine-learning-repo-ubuntu2004_1.0.0-1_amd64.deb

5. cuDNN 레포지토리 추가

$ sudo bash -c 'echo "deb http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /" >> /etc/apt/sources.list.d/nvidia-machine-learning.list'

6. cuDNN 설치 (CUDA 버전에 맞는 버전으로 설치해야 함)

$ sudo apt install libcudnn7-dev=7.6.5.32-1+cuda10.1 libcudnn7=7.6.5.32-1+cuda10.1

추후 업데이트시 최신버전으로 업데이트 되는 것을 방지

$ sudo apt-mark hold libcudnn7 libcudnn7-dev
libcudnn7 set on hold.
libcudnn7-dev set on hold.

이와 같이 설치를 완료하고, 재부팅한 다음… 드라이버가 잘 설치되어 있는지 확인

$ nvidia-smi


TensorFlow2 설치

$ sudo apt install python3-pip
$ sudo pip3 install -U tensorflow-gpu

환경변수 셋업

$ echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.1/lib64:/usr/local/cuda-10.2/lib64:/usr/local/cuda-10.1/lib64' >> ~/.bashrc
$ source ~/.bashrc

실행 및 설치 확인

$ python3
Python 3.8.5 (default, Jul 28 2020, 12:59:40)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
import tensorflow as tf
2020-10-08 15:02:50.803732: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
tf.config.list_physical_devices('GPU')
2020-10-08 15:03:01.717511: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcuda.so.1
2020-10-08 15:03:01.733336: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-10-08 15:03:01.733585: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1716] Found device 0 with properties:
pciBusID: 0000:01:00.0 name: GeForce GTX 1650 computeCapability: 7.5
coreClock: 1.56GHz coreCount: 16 deviceMemorySize: 3.82GiB deviceMemoryBandwidth: 119.24GiB/s
2020-10-08 15:03:01.733603: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
2020-10-08 15:03:01.734679: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcublas.so.10
2020-10-08 15:03:01.735901: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcufft.so.10
2020-10-08 15:03:01.736061: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcurand.so.10
2020-10-08 15:03:01.737163: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusolver.so.10
2020-10-08 15:03:01.737773: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusparse.so.10
2020-10-08 15:03:01.740221: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudnn.so.7
2020-10-08 15:03:01.740295: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-10-08 15:03:01.740563: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-10-08 15:03:01.740768: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1858] Adding visible gpu devices: 0
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

Windows 10에서 Raid에서 AHCI 모드로 변경하기

Intel RST 모드를 사용하게 되면, Ubuntu 20.04 설치시에 다음과 같은 창이 나타나면서 더이상 설치가 진행이 되지 않는다.

그렇다고, 막바로 바이오스 설정에서 AHCI로 변경을 하게 되면, 그 유명한 블루스크린이 발동하면서 윈도우 부팅이 진행되지 않는다. 다음과 같은 절차로 진행하면 쉽게 AHCI 모드로 변경이 가능하다.

먼저 RST (Raid) 상태에서, 윈도우10을 부팅하고, 커맨드 창을 관리자 모드로 연다 (커맨드 창 아이콘에서 오른쪽 버튼을 눌러 관리자 모드로 열면 됨).

다음과 같이 입력하고 재부팅.

bcdedit /set {current} safeboot minimal

이때 PC의 바이오스 설정으로 들어가서 (F2 or Del 등 제조사마다 다름). IDE 모드를 Raid에서 AHCI로 변경한다.

이제 다시 윈도우10으로 부팅하면, 안전모드로 부팅이 되게 되고 다시 커맨드 창을 관리자 모드로 연 다음, 다음과 같이 다시 입력

bcdedit /deletevalue {current} safeboot

그런 다음 재부팅.

이제 정상적으로 부팅이 되는 것을 확인할 수 있다.

장치관리에서 IDE 부분을 확인해보면,

정상적으로 적용이 되어 있음을 볼 수 있다.

Ubuntu에서 한글 및 한자 입력하기

Ubuntu 20.04든 그 하위 버전이든 모두 적용됨.

fcitx 한글 입력기 설치

$ sudo apt install fcitx-hangul

설정 > 지역 및 언어 (Region & Language) > Manage Installed Languages 버튼을 눌러 언어 설정으로 이동.

키보드 입력 방법 시스템 (Keyboard input method system)에서 fcitx를 선택.

혹시 한글 관련 폰트나 그런 것들이 설치되어 있지 않은 경우, Install/Remove Languages 버튼을 눌러, Korean 선택.

시스템을 재부팅하고 나면, 상단 트레이바에 fcitx가 실행되어 있음을 볼수 있음.

아이콘을 눌러, Configure Current Input Method 선택.

하단의 + 버튼을 눌러 Add input method 창을 띄운 뒤, Only Show Current Language를 체크 해제하고 Hangul을 입력해 한글 입력기 추가.

다음으로 하단 키보드 모양을 클릭하여, 기본 키보드 레이아웃을 Korean – Korean (101/104 compatible)으로 선택.

다음으로 Global Config 탭으로 이동. Trigger Input Method의 왼쪽 버튼을 클릭한 후, 한영키 입력.

다시, Input Method로 이동, Hangul을 더블클릭하면,

Word Commit 활성화, 한자모드 토글키의 왼쪽 버튼을 클릭, 한자키를 입력.

이제 한영키로 한글 입력 활성화, 한글 입력 후 한자키를 누르면 한자 입력 창 나옴.

Installation Docker on Ubuntu 18.04

Install the packages for dependencies.

$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

Add Docker’s official GPG key

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
OK

Add repository for installation

$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Install

$ sudo apt-get install docker-ce docker-ce-cli containerd.io

Verify the installation

$ sudo docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete 
Digest: sha256:6a65f928fb91fcfbc963f7aa6d57c8eeb426ad9a20c7ee045538ef34847f44f1
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Add user to docker group for execute without sudo command

$ sudo usermod -aG docker `whoami`

Reboot!

$ groups
<user-name> adm tty dialout cdrom sudo dip plugdev lpadmin sambashare docker

Now, you can run docker without sudo command

$ docker run hello-world

Ubuntu에서 HEIC 이미지를 jpg로 변환

HEIC 이미지는 아이폰에서 고효율로 설정하여 사진을 찍게되면 생성되는 파일입니다. 내보내기를 할때 호환성 모드로 하면 자동으로 png나 jpg로 변환되긴 하는데, 파일을 직접 전송해서 사용하는 경우 HEIC 확장자의 이미지로 보내지게 됩니다.

Ubuntu에서는 아직 HEIC 이미지를 지원하지 않아서, 이를 보기 위해선 jpg로 변환해서 봐야 합니다. 변경할 수 있는 툴은 다음과 같이 설치가 가능합니다.

$ sudo apt install libheif-examples

다음으로 HEIC 이미지가 있는 디렉토리로 이용하여

$ heif-convert IMG_6326.HEIC IMG_6326.HEIC.jpg

와 같이 변경할 수 있습니다.

변경해야 할 이미지가 여러장이라면, 간단한 쉘 스크립트를 사용하면 됩니다.

$ find *.HEIC | xargs -n1 -I {} /bin/bash -c 'heif-convert {} {}.jpg'

이렇게 하면 현재 디렉토리 내에 있는 HEIC 파일을 전부 읽어들여, jpg 이미지로 변경할 수 있습니다.

끝!.

STM32F746G-DISCO with TouchGFX 프로젝트 생성

보드를 구입한 본래의 목적이었던 TouchGFX를 사용하기 위해서 프로젝트 생성. 여러가지 유투브나 문서를 찾아봤지만, 한번에 매끄럽게 되는건 없는듯하다. ST에서 TouchGFX를 인수하였다곤 하지만 하나의 개발도구로 합치는데는 꽤 시간이 걸릴듯 한데… 뭐 그거야 나중 일이고.. 일단은 프로젝트를 생성하고 개발하기 위한 준비단계까지는 되어야 하니….

프로젝트 생성은 예전 데모와 마찬가지로 File > New > STM32 Project 로 시작한다. 보드도 역시 현재 보유한 32F746GDISCOVERY를 선택하고 다음으로 진행.

프로젝트 이름을 입력하고, 개발 언어는 C++로 선택. Finish를 선택하여 생성 시작. 이후 나오는 다이얼로그창에선 모두 Yes로 답하면 됨.

예전 데모와 같이 디바이스 설정을 위한 화면이 나타나면… 이제부터 설정 시작. (앞선 포스팅에선 편하다고 했었는데, 편하긴 개뿔… 좀더 한방에 되면 얼마나 좋아….)


(1) System Core > CORTEX_M7

(2) Connectivity > ETH

(3) Connectivity > QUADSPI

(4) Multimedia > LTDC

(5) TouchGFX 관련

이건 먼저, Additional Software를 클릭하고, 다음의 화면과 같이 TouchGFX를 선택.

그러면, 좌측에 Additional Software 탭과, TouchGFX 관련 설정 항목이 생성됨. Graphics Application를 체크하고 다음과 같이 설정.

(6) Middleware > FREERTOS

스택크기만 4096으로 변경.


이제 Project > Generate Code (Alt+K)를 실행하여 코드 생성. 생성이 완료되면, 프로젝트 창이 다음과 같이 되어야 함.

이제 TouchGFX의 ApplicationTemplate.touchgfx.part를 더블클릭하여 TouchGFX 디자이너를 실행한다. 만약 기존에 설치가 안되어있을 경우, 홈디렉토리 아래의 다음 주소를 가보면, 설치 파일이 존재한다.

C:\Users\bkahn\STM32Cube\Repository\Packs\STMicroelectronics\X-CUBE-TOUCHGFX\4.13.0\Utilities\PC_Software\TouchGFXDesigner

설치를 완료하고 다시 저 파일을 더블클릭하면…

와 같이 디자이너가 실행되고, UI를 디자인할 수 있음. 일단은 Blank UI를 선택한다. 다음과 같이 빈 UI 화면이 나오고 왼쪽의 위젯 화면에서 원하는 컴포넌트를 가져다 붙이면 됨.

테스트용도이니 대충 디자인하고… (이 툴 또한 익숙해져야 하는 것이기에….)

다 되었으면, 오른쪽 상단의 Generate Code를 클릭한다. 아래쪽 상태바에 Generate Done이 뜨면, 다시 CubeIDE로 이동한다. 프로젝트에서 오른쪽 버튼을 누르고 Refresh (F5)를 클릭하면, 파일이 추가됨을 볼 수 있다.

이제 몇가지 코드를 추가 및 수정 필요.

(2-1) STM32F746NGHX_FLASH.ld를 수정

그래픽 등 리소스가 제법 큰 크기를 차지하므로, FLASH 영역에는 다 들어가지 못함. 따라서 보드에 부착된 외장 플래시에 저장해야 하는데, 이를 위해 저 파일을 수정해야 함. 다른 부분만 표시하면…

...
/* Memories definition */
MEMORY
{
  RAM     (xrw)  : ORIGIN = 0x20000000,  LENGTH = 320K
  FLASH   (rx)   : ORIGIN = 0x8000000,   LENGTH = 1024K
  QUADSPI (r)    : ORIGIN = 0x90000000,  LENGTH = 16M
}

...

.ARM.attributes 0 : { *(.ARM.attributes) }

  ExtFlashSection :
	{
		*(ExtFlashSection ExtFlashSection.*)
		*(.gnu.linkonce.r.*)
        . = ALIGN(0x4);
	} >QUADSPI

  FontFlashSection :
	{
		*(FontFlashSection FontFlashSection.*)
		*(.gnu.linkonce.r.*)
        . = ALIGN(0x4);
	} >QUADSPI

  TextFlashSection :
	{
		*(TextFlashSection TextFlashSection.*)
		*(.gnu.linkonce.r.*)
        . = ALIGN(0x4);
	} >QUADSPI

...

(2-2) Drivers에 BSP와 Components 추가.

해당 파일은 TouchGFX 디자이너에서 빈프로젝트를 생성하면 얻을 수 있음. 추가한 다음 C++ 빌드 옵션에 해당 디렉토리 추가.

총 4개의 디렉토리를 추가해야 함.

(2-3) main.c 파일 수정

Core > Src > main.c 를 열고 다음과 같이 추가.

...

/* USER CODE BEGIN Includes */
#include <stm32746g_discovery_qspi.h>
/* USER CODE END Includes */

...

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define REFRESH_COUNT        1835

#define SDRAM_TIMEOUT                            ((uint32_t)0xFFFF)
#define SDRAM_MODEREG_BURST_LENGTH_1             ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2             ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4             ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8             ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL      ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED     ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2              ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3              ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD    ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE     ((uint16_t)0x0200)
/* USER CODE END PD */

...

/* USER CODE BEGIN PV */
static FMC_SDRAM_CommandTypeDef Command;
/* USER CODE END PV */



static void MX_QUADSPI_Init(void)
{
...
  /* USER CODE BEGIN QUADSPI_Init 2 */
  BSP_QSPI_Init();

  BSP_QSPI_MemoryMappedMode();
  HAL_NVIC_DisableIRQ(QUADSPI_IRQn);
  /* USER CODE END QUADSPI_Init 2 */
}


static void MX_FMC_Init(void)
{
...
  /* USER CODE BEGIN FMC_Init 2 */
  __IO uint32_t tmpmrd = 0;

      /* Step 1: Configure a clock configuration enable command */
      Command.CommandMode            = FMC_SDRAM_CMD_CLK_ENABLE;
      Command.CommandTarget          =  FMC_SDRAM_CMD_TARGET_BANK1;
      Command.AutoRefreshNumber      = 1;
      Command.ModeRegisterDefinition = 0;

      /* Send the command */
      HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

      /* Step 2: Insert 100 us minimum delay */
      /* Inserted delay is equal to 1 ms due to systick time base unit (ms) */
      HAL_Delay(1);

      /* Step 3: Configure a PALL (precharge all) command */
      Command.CommandMode            = FMC_SDRAM_CMD_PALL;
      Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
      Command.AutoRefreshNumber      = 1;
      Command.ModeRegisterDefinition = 0;

      /* Send the command */
      HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

      /* Step 4: Configure an Auto Refresh command */
      Command.CommandMode            = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
      Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
      Command.AutoRefreshNumber      = 8;
      Command.ModeRegisterDefinition = 0;

      /* Send the command */
      HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

      /* Step 5: Program the external memory mode register */
      tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 | \
               SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL    | \
               SDRAM_MODEREG_CAS_LATENCY_3            | \
               SDRAM_MODEREG_OPERATING_MODE_STANDARD  | \
               SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;

      Command.CommandMode            = FMC_SDRAM_CMD_LOAD_MODE;
      Command.CommandTarget          = FMC_SDRAM_CMD_TARGET_BANK1;
      Command.AutoRefreshNumber      = 1;
      Command.ModeRegisterDefinition = tmpmrd;

      /* Send the command */
      HAL_SDRAM_SendCommand(&hsdram1, &Command, SDRAM_TIMEOUT);

      /* Step 6: Set the refresh rate counter */
      /* Set the device refresh rate */
      HAL_SDRAM_ProgramRefreshRate(&hsdram1, REFRESH_COUNT);

      //Deactivate speculative/cache access to first FMC Bank to save FMC bandwidth
      FMC_Bank1->BTCR[0] = 0x000030D2;

  /* USER CODE END FMC_Init 2 */
}


void StartDefaultTask(void const * argument)
{
...
  /* USER CODE BEGIN 5 */
  MX_TouchGFX_Process();
...
}

이제 저장하고 프로젝트 빌드 Project -> Build Project. 에러없이 빌드가 완료되면 다음과 같이 각 메모리 영역의 사용량 등이 나옴.

Run > Run을 눌러 보드에 다운로드하고 실행.

이때, Debugger 항목에서 External Loader를 체크하고, 현재보드에 연결된 외장 FLASH 모델을 선택. Apply하고 확인을 눌러 계속 진행.

다운로드가 완료되고 자동으로 보드가 리셋이 되면서 아까 디자인 했던 그대로 화면이 나오는 것을 확인할 수 있음.

이렇게 일단은 셋업을 해놓고 하나씩 붙여나가면 될듯. 끝!

STM32F746G-DISCO 개발환경 구축

메인 개발도구는 윈도우, 맥, 리눅스 등을 지원하고 있지만, TouchGFX 관련한 툴은 아직까진 윈도우만 지원합니다. 그래서 어쩔수 없이 윈도우에서 개발을 진행해야 할 듯 합니다. (윈도우 싫어요~~)

ST에선 자사의 다양한 MCU 및 보드들의 개발을 위해 통합 개발 도구를 지원하고 있습니다. (꾸준히 업데이트 되고 있는 듯 합니다.) https://www.st.com/en/development-tools/stm32cubeide.html 에 가보시면 받으실 수 있고, 메일을 통한 인증을 하셔야 합니다. 다운로드한 en.st-stm32cubeide_1.3.0_5720_20200220_1053_x86_64.exe.zip 파일을 압축을 해제하고 설치를 진행합니다.

특별한 설정없이 다음, 다음 진행하면서 설치하면 끝납니다.

바탕화면의 아이콘이나 시작메뉴에서 설치한 프로그램을 실행하면 처음엔 프로젝트들이 위치할 곳을 선택하도록 나오는데, 적절한 곳을 입력해주시고요,

시작화면이 짜잔 나타납니다. 이클립스를 기반으로 만들어진 툴인듯한데… 불편하네요..ㅎㅎ

어쨌든 툴의 자동 업데이트도 지원해주고 있습니다.

프로젝트를 하나 만들어보겠습니다.

상단의 메뉴에서 File > New > STM32 Project를 누르시거나 시작화면에서 Start new STM32 Project를 누르셔서 프로젝트를 만들어봅니다.

그러면 요렇게 창이 하나 뜹니다. 저는 STM32F746G-DISCO 보드를 사용할 예정이므로, 상단의 탭에서 Board Selector를 선택하고 STM32F746G-DISCO 보드를 찾아 클릭하고 다음으로 진행.

프로젝트의 이름을 입력하고, 개발언어는 C++로 선택합니다. 이렇게 만들어진 프로젝트는 처음 실행시 설정하였던 workspace 디렉토리 아래에 추가됩니다.

모든 주변기기들의 설정을 기본모드로 할것인지를 물어보는데, 당연히 Yes.

현재 개발툴 (이클립스)의 뷰를 STM32CubeMx 모드로 보여줄건지를 물어봅니다. 요것도 당연히 Yes.

이것저것 준비를 하면서 프로젝트 생성 중.

이렇게 짠 하고 나타납니다. 개발자들은 이렇게 기본 밥상이 차려진 상태에서 필요한 부분만 추가하여 빌드하고 이를 보드에 다운로드해서 사용하면 됩니다. (음.. 일단은 좀 많이 편해진듯…)

보드가 동작되는지 확인용으로… LED를 한번 깜빡여줘보겠습니다…

기본적으론 FREERTOS가 돌아가고 있는 상태이므로 LED 깜빡이용 Task를 하나 생성해줍니다.

프로젝트 파일을 선택하고, Middleware > FREERTOS를 선택합니다. 그런 다음 Task and Queues를 선택하면, 현재는 하나의 Task만 생성되어 실행되고 있습니다. Add 버튼을 누르고, 다음과 같이 입력한 후, 메뉴 > Project > Generate Code (Alt+K)를 실행합니다.

프로젝트의 main.c 파일을 가보면,

StartTaskBlinky 함수가 만들어져 있는데, 이곳에 위와 같이 코드를 입력해줍니다.

  /* USER CODE BEGIN StartTaskBlinky */
  GPIO_InitTypeDef gpioInitStructure;
  gpioInitStructure.Pin = GPIO_PIN_1;
  gpioInitStructure.Mode = GPIO_MODE_OUTPUT_PP;
  gpioInitStructure.Pull = GPIO_PULLUP;
  gpioInitStructure.Speed = GPIO_SPEED_HIGH;
  HAL_GPIO_Init(GPIOI, &gpioInitStructure);

  /* Infinite loop */
  for(;;)
  {
    osDelay(200);
    HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_1);
  }
  /* USER CODE END StartTaskBlinky */

대략, 200ms 마다 GPIOI의 1번 핀을 토글 시켜주라는 건데, 현재 보드에는 그 핀에 LED가 연결되어 있습니다.

이제 프로젝트를 빌드하고… Run > Run를 클릭하면 자동으로 보드에 빌드된 펌웨어를 다운으로 하고 보드를 리셋해줍니다. 보드가 리셋되고, FREERTOS가 가동되는데 살짝의 시간이 지나고나면, 보드 뒷면의 LED가 깜빡임을 볼 수 있습니다.

Device name : STM32F74x/STM32F75x
Flash size  : 1 MBytes
Device type : MCU
Device CPU  : Cortex-M7



Memory Programming ...
Opening and parsing file: ST-LINK_GDB_server_a10660.srec
  File          : ST-LINK_GDB_server_a10660.srec
  Size          : 76388 Bytes
  Address       : 0x08000000 


Erasing memory corresponding to segment 0:
Erasing internal memory sectors [0 2]
Download in Progress:


File download complete
Time elapsed during download operation: 00:00:01.944



Verifying ...




Download verified successfully 


Debugger connection lost.
Shutting down...

끝! 잘되네요..^^

STM32F746G-DISCO 평가보드

이미지출처: https://www.st.com/en/evaluation-tools/32f746gdiscovery.html

로봇용 간단한 UI 모듈을 개발하는데 괜찮을듯 하여 주문한 제품, 깊고 복잡하게 들어가긴 좀 귀찮아 가장 예제가 많은 보드를 골라서 주문하였습니다. (후딱 개발하고 끝내기) 가격은 대략 8~9만원 정도이고, 흔하게 구할 수 있습니다. 거의 대부분 기능을 테스트 할 수 있도록 주변 소자들도 많이 들어있고, 전면부에는 큼지막한 5인치 터치 LCD가 붙어 있는데, 해상도가 480×272라서 약간 아쉬운 면은 있습니다. 뭐.. 필요하면 교체해서 써도 될듯 하고요…

가장 사용해보고 싶은 부분은 TouchGFX라는 녀석인데, ST에서 얼마전(아니면 한참전?)에 인수하여, 현재는 개발툴을 통합하는 과정에 있는듯 합니다. (검색해보니 버그도 많긴 합니다만.. 뭐 그런거야 개발자에겐 흔한….)

Dell XPS 15 7590 (2020) Ubuntu 스크린 밝기 조정 에러 수정

업뎃: 아래와 같이 수정을 해도 제대로 로딩되지 않은 상황 발생. 문제는 이게 OLED다 보니 기존 스크린 밝기를 조정했던 백라이트 조정과는 차이가 있음. (OLED는 백라이트가 없고, 각각의 소자들의 값을 조정하여 밝기를 조정함.)

수동으론 조정 가능

$ xrandr --output eDP-1-1 --brightness .5

지난 포스팅에서 델의 XPS 15 7590 (2020)에 Ubuntu를 설치하는 방법을 설명하였는데, 설치 완료 후 스크린 밝기가 조정이 안되는 문제가 있었습니다.

인터넷엔 능력자들이 많기에 검색을 해보니, 역시나 문제에 대한 분석 및 해결 방법이 있었습니다. 신형 노트북 모니터 특히 OLED를 사용한 모니터와 기존 스크린 밝기를 조정하는 부분이 뭔가 호환이 안되는듯 합니다.

몇가지 귀찮은 작업을 해주면 사용 가능한데, 먼저 내 노트북의 디스플레이 이름을 확인합니다

$ xrandr --listmonitors
Monitors: 1
 0: +*eDP-1-1 3840/344x2160/194+0+0  eDP-1-1

eDP-1-1” 이 모니터 이름입니다. 터미널에서 명령을 입력하여 밝기가 조정이 되는지 확인해봅니다.

$ xrandr --output eDP-1-1 --brightness 0.5

정상적으로 밝기가 반정도 어두워지는 것을 확인할 수 있습니다. 0으로 하면 완전 검은색으로 변하니 주의하시고요. 이제 이것을 스크린 밝기를 조정하는 키와 맵핑을 해줘야 합니다.

두 개의 파일을 만들어줍니다.

$ sudo vi /etc/acpi/events/dell-brightness-up
event=video/brightnessup BRTUP 00000086 00000000
action=/etc/acpi/dell-brightness.sh up

$ sudo vi /etc/acpi/events/dell-brightness-down
event=video/brightnessdown BRTDN 00000087 00000000
action=/etc/acpi/dell-brightness.sh down

그런 다음 dell-brightness.sh 를 위 경로에 만들어줍니다.

$ sudo vi /etc/acpi/dell-brightness.sh
#!/bin/bash
DISPLAYNAME=$(xrandr --listmonitors | awk '$1 == "0:" {print $4}')

OLED_BR=`xrandr --verbose | grep -i brightness -m 1 | cut -f2 -d ' '`
CURR=`LC_ALL=C /usr/bin/printf "%.*f" 1 $OLED_BR`

MIN=0
MAX=1.0

if [ "$1" = "up" ];
    then
        VAL=`echo "scale=1; $CURR+0.1" | bc`
    else
        VAL=`echo "scale=1; $CURR-0.1" | bc`
fi

if [ `echo "$VAL < $MIN" | bc -l` = "1" ];
    then
        VAL=$MIN
elif [ `echo "$VAL > $MAX" | bc -l` = "1" ];
    then
        VAL=$MAX
else
    `xrandr --output $DISPLAYNAME --brightness $VAL` 2>&1 >/dev/null | logger -t oled-brightness
fi



# Set Intel backlight to fake value
# to sync OSD brightness indicator to actual brightness
INTEL_PANEL="/sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-eDP-1/intel_backlight/"
if [ -d "$INTEL_PANEL" ]; then
    PERCENT=`echo "scale=4; $VAL/$MAX" | bc -l`
    INTEL_MAX=$(cat "$INTEL_PANEL/max_brightness")
    INTEL_BRIGHTNESS=`echo "scale=4; $PERCENT*$INTEL_MAX" | bc -l`
    INTEL_BRIGHTNESS=`LC_ALL=C /usr/bin/printf "%.*f" 0 $INTEL_BRIGHTNESS`
    echo $INTEL_BRIGHTNESS > "$INTEL_PANEL/brightness"
fi

저장하고, 이 스크립트 파일을 실행 가능하도록 수정

$ sudo chmod u+x /etc/acpi/dell-brightness.sh

그런 다음 재부팅하면 수정 완료.

참고링크: https://github.com/TillmannBerg/Ubuntu-Dell-XPS-15-2019

Dell XPS 15 7590 (2020) Ubuntu 설치 관련

최근 Dell XPS 15 7590을 업무용으로 사용하게 되었는데, 기존 외장 USB 드라이브에 Ubuntu를 설치하고 사용하는데 약간의 차이가 생겨, 이에 대한 기록 차원에서 정리.

가장 큰 차이는 더이상 델의 최신 버전 노트북에선 Legacy External Boot가 지원되지 않는다고 합니다. 대략 찾아보니 보안 관련, 32bit에 대한 지원 종료 (64bit만 사용) 등의 이유로 그렇게 된 듯 합니다. 따라서 BIOS 화면에서도 이에 관한 옵션이 사라져 있습니다. 델 상담원이랑 채팅을 통해 상담했는데 그렇다고 하네요.

이에 따라 Grub를 이용해서 부팅하던 USB 설치 스틱도 써먹을 수가 없었는데, 이는 Ubuntu에 내장된 Startup Disk Creator 어플리케이션을 사용하여 쉽게 새로운 설치 스틱을 만들수 있습니다.

이와 같은 상황으로 기존 BIOS에서 Secure Boot 옵션을 껐던 적이 있었는데, 이젠 그것은 그냥 켜두시면 됩니다. (보안에 좋답니다. ㅎㅎㅎ)

NVIDIA 드라이버를 정상적으로 사용하시려면 여전히 Secure Boot 옵션은 꺼둬야 합니다. 살짝 해맸네요..

Ubuntu 설치디스크로 부팅하시고 설치는 그냥 진행 하시되, 파티션 설정 부분에서 기존 /boot로 설정했던 파티션을 EFI 파티션으로 변경해야 합니다. 부트로더 설치할 디스크는 여전히 사용할 외장 USB 드라이브를 선택하시면 되고요. 그렇게 되면 파티션 설정은

Device     Boot     Start       End   Sectors   Size Id Type
/dev/sda1  *        65535   3932099   3866565   1.9G ef EFI (FAT-12/16/32)
/dev/sda2         3932100 456385739 452453640 215.8G 83 Linux
/dev/sda3       456385740 488366819  31981080  15.3G 82 Linux swap / Solaris

와 같이 될겁니다.

이후 설치가 완료되고, 재부팅을 한 다음 로고가 나올때 F12키를 연타해보면, UEFI 리스트에 외장 USB 드라이브의 이름이 나오고, 이를 선택하면 잠시 검은색 화면이 나오고 재부팅되는데….

다시 F12키로 부팅리스트를 보면 ubuntu라는 UEFI가 추가로 생성됨을 볼수 있습니다. 이제 이것을 선택하면 정상적으로 부팅됩니다.

이제 사용하면 끝.!

덧:

  • 화면 밝기가 조정이 안됩니다. 하지만 이는 버그인듯하여 쉽게 수정이 될듯 합니다.
  • 기존 말썽을 부리던 Killer WiFi 카드의 드라이버가 그냥 잡힙니다. 하지만 성능은 여전히 구립니다.
  • 이전에 사용하던 Dell XPS 15 9570에서 고질적으로 발생하던 스크린 Flickering 현상은 새로운 모델에선 나타나지 않습니다.

Ubuntu 앱에 QT_AUTO_SCREEN_SCALE_FACTOR 적용하기

4K 등 HiDPi 환경에서 Ubuntu에 apt나 Ubuntu Software를 이용해 앱을 설치한 후 이를 실행을 하게 되면 레이아웃이 보기 싫게 깨지는 경우가 있다. Gtk 환경의 앱은 어느 정도 자체적으로 지원이 되는듯 한데, Qt 환경의 앱은 그러지 않는데, QT_AUTO_SCREEN_SCALE_FACTOR 환경 변수를 설정하면 쉽게 적용이 가능하다.

예를 들어, 스크린 녹화 앱인 vokoscreen의 경우 설치한 후에 실행을 하게 되면,

와 같이 되는데 (스크린샹으론 잘 구별이 되진 않지만….), QT_AUTO_SCREEN_SCALE_FACTOR 환경 변수를 설정한 후 실행하면,

와 같이, UI 레이아웃의 비율이 원래와 같이 나옴을 볼 수 있다.

수정하는 방법은 각 앱의 desktop 파일을 검색하고, (보통은 /usr/share/applications 디렉토리에 있음) 편집기를 이용해 해당되는 앱의 desktop 파일을 다음과 같이 수정한다.

[수정전]

[Desktop Entry]
Comment=screencast
Exec=/usr/bin/vokoscreen
Icon=vokoscreen
Name=vokoscreen
StartupNotify=false
Terminal=false
Type=Application
Categories=AudioVideo;Recorder;
Keywords=Audio;Video;Recorder;Screencast;

[수정후]

[Desktop Entry]
Comment=screencast
Exec=env QT_AUTO_SCREEN_SCALE_FACTOR=1 /usr/bin/vokoscreen
Icon=vokoscreen
Name=vokoscreen
StartupNotify=false
Terminal=false
Type=Application
Categories=AudioVideo;Recorder;
Keywords=Audio;Video;Recorder;Screencast;

저장하고, 예전과 같이 앱을 실행하면 됨.

Ubuntu 커스텀 설치 이미지 만들기 (Make the custom ISO image of Ubuntu)

설명

cubic 설치

$ sudo add-apt-repository ppa:cubic-wizard/release
$ sudo apt install cubic

설치 이미지 다운로드 (주의! Live 이미지는 자동설치 지원 안됨.)

$ wget http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-18.04.4-server-arm64.iso

cubic 실행 (실행시 root 권한이 필요하므로 계정 암호 입력하여 진행)

작업에 사용할 디렉토리 생성하고 선택, 다음으로 진행

다운로드한 설치 이미지를 선택, 그러면, 다음과 같이 정보들이 자동으로 입력됨. Original ISO… 부분은 절대 수정하면 안되고, Custom ISO… 부분은 작업 디렉토리를 제외한 나머지 부분은 수정 가능.

다음으로 진행.

자동으로 파일 및 디렉토리를 복사한 후, 작업을 위한 chroot 환경의 터미널이 뜸.

이제 이 터미널 내에서 원하는 작업을 해주면 됨. 프로그램 설치는 apt를 이용해서 설치. 스크립트나 기타 등등의 작업도 가능.

작업이 완료되었으면, Next를 눌러 다음으로 진행

여기에선 설치하고 싶지 않은 패키지들을 선택 가능. 필요없다고 생각하는 것들을 선택하면 됨.


3번째 탭으로 이동. 설치 자동화 스크립트 파일을 하나 추가한다. seed 파일의 문법은 https://help.ubuntu.com/lts/installation-guide/amd64/apbs04.html을 참고한다. 파일을 추가하기 위해서 하단의 + 버튼을 누르고 오른쪽 편집창에 스크립트를 입력한다. 이번 과정에서 사용한 seed 파일은 다음과 같다.

### Automatic Installation
d-i auto-install/enable boolean true
d-i debconf/priority select critical


### Localization
d-i debian-installer/locale string en_US.UTF-8
d-i localechooser/supported-locales multiselect en_US.UTF-8
# Keyboard
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/xkb-keymap select us


### Network
d-i netcfg/choose_interface select auto
d-i netcfg/disable_autoconfig boolean true
d-i netcfg/use_autoconfig boolean false
d-i netcfg/disable_dhcp boolean true
#d-i netcfg/dhcp_failed note
d-i netcfg/dhcp_options select Configure network manually

# Static network configuration.
d-i netcfg/get_ipaddress string 192.168.1.11
d-i netcfg/get_netmask string 255.255.255.0
d-i netcfg/get_gateway string 192.168.1.1
d-i netcfg/get_nameservers string 192.168.1.1
d-i netcfg/confirm_static boolean true

d-i netcfg/get_hostname string ubuntu
d-i netcfg/get_domain string


### Mirror settings
d-i mirror/country string manual
d-i mirror/http/hostname string archive.ubuntu.com
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string


### Account setup
d-i passwd/root-login boolean false
d-i passwd/make-user boolean true

d-i passwd/user-fullname string ubuntu
d-i passwd/username string ubuntu
d-i passwd/user-password password ubuntu
d-i passwd/user-password-again password ubuntu
d-i user-setup/allow-password-weak boolean true


### Clock and time zone setup
d-i clock-setup/utc boolean true
d-i time/zone string Asia/Seoul
d-i clock-setup/ntp boolean true


### Partitioning
d-i partman-auto/init_automatically_partition select biggest_free
d-i partman-auto/method string regular
d-i partman-swapfile/size string 4096

d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

### Update
d-i pkgsel/update-policy select none

### Bootloader
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev  string default


# Verbose output and no boot splash screen.
d-i debian-installer/quiet	boolean false
d-i debian-installer/splash	boolean false
d-i finish-install/reboot_in_progress note

이상하게, 몇개의 특정 스크립트 이외에 다른 스크립트들은 거의 반영이 안된다. 완전 자동화 설치는 아니고, 부분부분은 사용자가 개입을 해줘야한다. 버그인듯한데… 좀더 살펴봐야 할듯.

다음으로 이 seed 파일을 사용하기 위해, 부트 메뉴 등을 설정해야 한다. 네번째 탭인 ISO Boot Configuration으로 이동, grub.cfg 파일에 다음과 같은 엔트리 하나 추가.

menuentry "Automatic Install Ubuntu Server" {
	set gfxpayload=keep
	linux	/install/vmlinuz  boot=casper file=/cdrom/preseed/auto-cli.seed auto=true priority=critical locale=en_US netcfg/disable_autoconfig=true automatic-ubiquity noprompt noshell quiet ---
	initrd	/install/initrd.gz
}

다음으로 isolinux 설정. 역시 엔트리를 하나 추가하고, 기본 선택을 자동설치로 변경. (isolinux/txt.cfg 파일 수정)

default auto-install
label auto-install
  menu label ^Automatic Install Ubuntu Server
  kernel /install/vmlinuz
  append  boot=casper file=/cdrom/preseed/ubuntu-server.seed vga=788 initrd=/install/initrd.gz auto=true locale=en_US priority=critical netcfg/disable_autoconfig=true automatic-ubiquity noprompt noshell quiet ---

수정이 완료 되어 있으면, Generate 버튼을 눌러 다음으로 진행.


이제 사용자의 ISO 파일을 생성함. 종료하고 작업 디렉토리로 가보면, 사용자 설치 이미지가 생성되어 있는 것을 볼수 있음.

이걸 가지고 USB등 설치 이미지를 만들어 설치하면 끝!.

참고링크

Scratch 3.0 개발 환경 구축 (renew)

기존 Scratch 3.0 개발 환경 구축 (https://ahnbk.com/?p=366) 포스팅이 있는데, 이번 macOS 카탈리나에서 새로 개발 환경을 구축하면서 약간 달리지는 부분에 대해 새로 간단히 정리합니다.


  • Node.js 설치

macOS에선 기존과 마찬가지로 homebrew를 먼저 설치하고, 이를 이용하여 설치하는 것이 간편함. 현재 LTS 버전은 12.6.1이지만, scratch의 경우 여전히 10버전까지만 지원함. 따라서 다음과 같이 입력하여 설치.

$ brew install node@10

  • Scratch 소스 받아오기

개발에 사용될 임의의 디렉토리를 하나 만들고, Scratch 각 파트별로 소스 clone.

$ mkdir scratch_dev
$ cd scratch_dev
$ git clone https://github.com/LLK/scratch-gui.git
$ git clone https://github.com/LLK/scratch-vm.git
$ git clone https://github.com/LLK/scratch-l10n.git

사용자가 직접 Interaction 할 수 있는 GUI ( scratch-gui), Scratch의 실행 (Back-end)을 담당 (scratch-vm), 한글 관련 작업 (scratch-l10n).


  • 빌드하기

[scratch-l10n]

$ cd scratch-l10n
$ npm install
$ npm run-script build
$ npm link

[scratch-vm]

$ cd scratch-vm
$ npm install
$ nom link

[scratch-gui]

$ cd scratch-gui
$ npm install
$ npm link scratch-l10n scratch-vm

빌드 완료


  • 실행
$ cd scratch-gui
$ npm start

사파리를 띄우고, http://0.0.0.0:8601로 접속.

끝! 여전히 잘됨..^^

Tips about Zsh on macOS

macOS가 Catalina로 버전업이 되면서, 터미널의 기본 shell이 Zsh가 기본값으로 변경되었습니다. 이에 따른 몇가지 팁들 정리.


  • 환경설정 파일은 .zshrc

기존 bash shell의 경우, .bashrc 파일을 사용했다면, Zsh의 경우 .zshrc 파일을 사용함


  • 터미널에서 색상 지원

기본 설정이 색상지원이 안되도록 되어 있어, 밋밋한 흰색만 나옴. 색상 지원이 가능하게 하려면, .zshrc 파일 내에 다음의 문구 추가하면 됩니다.

autoload -U colors && colors
export CLICOLOR=1

  • 프롬프트 변경

Shell의 프롬프트는 PS1이라는 환경변수를 통해 설정 가능, 기본 값은 색상값 없이 정보만 표시하도록 되어 있어 다음과 같이 보이는데,

이를 색상과 같이 좀 이쁘게 설정하려면, .zshrc 내에 PS1 값을 다음과 같이 설정하면 됩니다.

export PS1="%{%F{green}%}%n@%m%f: %{%F{blue}%}%1~ %f%# "

다시 쉘을 실행해보면,

Zsh의 경우 다양한 플러그인을 통해 다양한 정보를 프롬프트에 표시할 수 있습니다만, 전 복잡한건 딱 질색인 타입이라, 위 상태만으로도 만족합니다..^^


  • root shell 변경

root의 shell은 또 bash가 기본값으로 설정되어 있습니다. macOS에선 root shell을 사용할 일이 거의 없는지라 상관없지만, 변경하고자 한다면,

$ sudo dscl . -change /Users/root UserShell /bin/sh /bin/zsh

와 같이 입력하면 root에서도 zsh를 기본값으로 사용할 수 있습니다.

Using Gazebo11 with ROS1 Melodic

Gazebo 11이 지난 달에 릴리즈 되었습니다. 이 버전이 Gazebo의 마지막 릴리즈 버전이고, 이후론 지원 기간까지 새로운 기능보단, 주로 버그 수정 등만 지원됩니다. 새로운 Ignition Gazebo가 나올 예정인데, 이는 Ignition Robotics라는 곳에서 업데이트 등을 지원할 예정입니다.

현재의 ROS의 최근 LTS 버전은 Melodic으로, 공식적으론 Gazebo 9만 지원합니다. 따라서 Gazebo 11을 사용하기 위해선 ROS와 연동하기 위한 패키지들을 새로 빌드해줘야 합니다.


ROS1 Melodic이 설치된 상태에서, Gazebo 11을 설치하려면 패키지의 의존성 등을 고려하여 다음과 같이 입력합니다.

$ sudo apt install gazebo11 libgazebo11 gazebo11-common libgazebo11-dev 

이러면, Gazebo 9은 자동으로 제거가 되고, Gazebo 11이 설치됩니다.

이제 ROS와 연동하기 위한 gazebo_ros_pkgs를 새로 받아 빌드합니다.

catkin workspace 내에서 작업해야 하므로,

$ cd ~/catkin_ws/src
$ git clone https://github.com/ros-simulation/gazebo_ros_pkgs.git
$ cd gazebo_ros_pkgs
$ git checkout melodic-devel
$ catkin build 

빌드가 완료되면, 예전과 같이 정상적으로 ROS1과 연동하여 사용이 가능합니다. 플러그인도 사용 가능.

$ roslaunch gazebo_ros empty_world.launch

위와 같이 설치를 하게 되면, ros-melodic-desktop-full의 의존성이 깨지므로, apt를 사용할때 autoremove로 ROS 패키지들을 자동으로 지우게끔 유도하게 되는데 이를 해결하려면,

$ sudo apt install ros-melodic-perception ros-melodic-desktop ros-melodic-ros-control ros-melodic-ros-controllers

와 같이 각각의 패키지들을 설치하면 해결되고, 그 외의 패키지는 필요없는 것들이니 지워도 무방합니다.

끝.

Blockly 2019 Q4 Release

Blockly도 그동안 계속 버전 패치를 해왔는데, 이번 2019 Q4 릴리즈에선 약간 더 큰 변화를 가지고 릴리즈 되었습니다.

https://groups.google.com/forum/#!searchin/blockly/npm%7Csort:date/blockly/cOxLdS7vX-c/bq-KIyBXAAAJ

Zelos라는 렌더링 엔진을 사용하는 것인데, 기존 렌더링 엔진인 Geras를 보완하여 개발하였습니다. 블럭들의 모습이 Scratch의 그것들과 상당히 유사해졌고, 실시간으로 블럭을 그려서 사용하다보니 좀더 예쁜? 모습을 보여줍니다.

각 렌더링 엔진에 대한 설명은 다음의 슬라이드에서 참고.

https://docs.google.com/presentation/d/1nNk0DYT_aeMxKS7BVfAZxpQSOmwBu9XuG96oezrp_Ss/edit#slide=id.g40d1aecc4e_0_470

여하튼 기존 개발 환경 구축과 마찬가지로 소스를 가지고와 옵션을 살짝 건드려주면? 다음과 같은 모습을 보여줍니다.

렌더링 엔진을 선택하는 부분은 Blockly를 인젝션하는 부분에 옵션을 다음과 같이 추가해주면 됩니다.

    var workspacePlayground = Blockly.inject('blocklyDiv', {

      toolbox: BLOCKLY_TOOLBOX_XML['standard'],
      media: '/blockly/media/',
      trashcan: true,
      zoom: {
        controls: true,
        wheel: true,
        startScale: 0.7,
        maxScale: 3,
        minScale: 0.3,
        scaleSpeed: 1.2
      },
      renderer : "zelos"
    });

끝.

Realsense D400 시리즈 최적 사용 방법

Reference

RGB-D 카메라 중, 최근 많이 사용되고 있는 인텔 리얼센스 D400 시리즈 중 D435 모델을 사용하고 있다. 대부분 ROS를 이용해서 사용하는만큼 그냥 기본 옵션을 주로 사용하고 있었는데, 최근 카메라 관련 자료를 찾아보다가 위의 링크의 문서를 보게 되었다.

결론부터 알아보면, 카메라의 스펙을 고려하여 그에 해당하는 옵션을 선택해서 사용하는 것이 가장 좋은 품질의 Depth 데이터를 얻을수 있다는 얘기다.


  1. Depth의 해상도는 848×480으로 한다. (D415의 경우 1280×720) 그 이외의 해상도를 선택하면 저 해상도를 기준으로 하여 스케일링한 후 출력을 내보내는 듯 하다. 만약 사용하려는 알고리즘의 성능을 고려하여 해상도를 낮춰야 한다면, 저 native 해상도로 받은 후, 후처리를 통해 해상도를 낮추는 것을 더 추천한다고 한다. (흐음~)
  2. 카메라를 사용하는 환경의 조명을 개선한다. (조명 조건이 좋을수록 Depth 데이터의 품질도 좋아진다.
  3. 사용하려는 물체의 위치를 카메라에 가능한 가깝게 하도록 노력한다. D400 시리즈 모델의 경우 IR 패턴광을 사용하여 Depth 정보를 계산하는만큼, 거리가 멀어지면 멀어질수록 오차는 기하급수적으로 심해진다.
  4. 여러 개의 카메라를 사용한다. (이건 좀 이해가 안가는데…) 암튼 FOV를 커버하기 위해서 여러 개의 카메라를 사용하는 것을 추천한다. D400 시리즈의 경우 여러개의 카메라를 사용해도 상관이 없고, 만약 하드웨어 동기화 기능을 사용할 경우, 카메라에 위치한 별도의 커넥터를 사용하여 연결하면, master-slave 관계를 설정하여 사용 가능하다.
  5. post-processing를 적극 활용한다.

대략 이 정도인듯.

realsense_ros (https://github.com/IntelRealSense/realsense-ros) 패키지를 사용하는 경우, 위 사항을 고려하여 사용한다면, 다음과 같이 사용하면 됨.

$ roslaunch realsense2_camera rs_camera.launch enable_pointcloud:=true color_width:=848 color_height:=480 color_fps:=60 depth_width:=848 depth_height:=480 depth_fps:=60 infra_width:=848 infra_height:=480 infra_fps:=60 align_depth:=true filters:="spatial

물론 roslaunch의 arguments를 사용하면 좀더 이쁘게 만들수도 있을듯.

덧:

현재 librealsense2 패키지의 최신 버전은 커널 5.0대에서 사용할 때, Pointcloud를 제대로 읽어오지 못함. 따라서 문제가 해결될때까지 2.30 버전을 사용하는 것을 권장.

Build and Install OpenCV 4.2.0 on Ubuntu with GPU support

Current OpenCV version only support CUDA 10.1.

Download sources

$ wget https://github.com/opencv/opencv/archive/4.2.0.tar.gz
$ wget https://github.com/opencv/opencv_contrib/archive/4.2.0.tar.gz

Extract the compressed files

$ tar zxf opencv-4.2.0.tar.gz
$ tar zxf opencv_contrib-4.2.0.tar.gz

CMake configuration

$ cd opencv-4.2.0
$ mkdir build
$ cd build
$ cmake -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.2.0/modules -DWITH_CUDA=ON -DBUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Release ..

Build

$ make -j8

It takes 15 ~ 20 minutes. And install,

$ sudo make install

Check installations

$ opencv_version 
4.2.0

$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2
<module 'cv2' from '/usr/local/lib/python3.6/dist-packages/cv2/python-3.6/cv2.cpython-36m-x86_64-linux-gnu.so'>
>>> 

Done.

Install Tensorflow with GPU support

Reference: https://www.tensorflow.org/install/gpu

  • Install CUDA 10.1 with GPU Driver
$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.2.89-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1804_10.1.243-1_amd64.deb
$ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
$ sudo apt-get update
$ wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
$ sudo apt install ./nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
$ sudo apt-get update
$ sudo apt install cuda
$ sudo apt install cuda-10-1
  • Install cuDNN
$ sudo apt install libcudnn7-dev
  • Install TensorRT
$ sudo apt install libnvinfer-dev=6.0.1-1+cuda10.1 libnvinfer6=6.0.1-1+cuda10.1 libnvinfer-plugin6=6.0.1-1+cuda10.1
  • Install python3-pip and update
$ sudo apt install python3-pip
$ sudo pip3 install -U pip
  • Install Tensorflow
$ sudo pip3 install -U tensorflow-gpu
$ sudo pip3 install -U launchpadlib

Check the installation

$ python3
Python 3.6.9 (default, Nov  7 2019, 10:44:02)                                                                                                                                                                                         
[GCC 8.3.0] on linux                                                                                                                                                                                                                  
Type "help", "copyright", "credits" or "license" for more information.                                                                                                                                                                
>>> import tensorflow as tf                                                                                                                                                                                                           
2020-01-17 00:54:07.956548: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libnvinfer.so.6                                                                                       
2020-01-17 00:54:07.957808: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libnvinfer_plugin.so.6
>>>
>>> tf.config.list_physical_devices('GPU')
2020-01-17 01:06:54.209819: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1
2020-01-17 01:06:54.244560: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-01-17 01:06:54.244971: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1555] Found device 0 with properties: 
pciBusID: 0000:68:00.0 name: GeForce RTX 2080 Ti computeCapability: 7.5
coreClock: 1.545GHz coreCount: 68 deviceMemorySize: 10.76GiB deviceMemoryBandwidth: 573.69GiB/s
2020-01-17 01:06:54.245001: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.1
2020-01-17 01:06:54.245027: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10
2020-01-17 01:06:54.246771: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10
2020-01-17 01:06:54.247101: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10
2020-01-17 01:06:54.248591: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10
2020-01-17 01:06:54.249448: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10
2020-01-17 01:06:54.249496: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2020-01-17 01:06:54.249579: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-01-17 01:06:54.249978: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:981] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-01-17 01:06:54.250314: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1697] Adding visible gpu devices: 0
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

Done.

PyQt and HiDPi Scaling

ROS1 관련 도구들 (rqt 시리즈, rviz 등)은 Qt 특히 PyQt를 사용하여 만들어졌는데, 일반 상황에선 괜찮으나, 4k 모니터 등 고해상도 모니터에서 HiDPi 설정을 켠 경우에 레이아웃이 깨지는 경우가 발생한다.

폰트 크기는 정상적이지만, 버튼 등 나머지 레이아웃은 그렇지 않음으로 인해, 이상하게 보인다.

간단히 환경변수를 셋팅함으로서 이 문제는 해결이 된다.

$ export QT_AUTO_SCREEN_SCALE_FACTOR=1

위와 같이 실행한 후, 다시 rviz 등을 실행해 보면,

UI의 비율 및 크기가 잘 맞춰져 있음을 볼 수 있다. 터미널을 실행할 때마다 설정되어야 하므로, .bashrc 등 사용자의 환경변수 설정 파일에 추가하면 편리하게 사용이 가능하다.

$ echo "export QT_AUTO_SCREEN_SCALE_FACTOR=1" >> ~/.bashrc

끝!

추가.

Qt 관련 환경 변수 중, 위와 비슷한 QT_SCALE_FACTOR도 있는데, 이는 폰트 크기를 포함한 모든 UI 컴포넌트를 조정한다. 또한 QT_SCREEN_SCALE_FACTORS 도 있는데, 이는 QT_AUTO_SCREEN_SCALE_FACTOR와 유사하게 동작하고, 사용자가 Scale Factor를 임의로 조정할 수 있다.

WordPress 옮기기 & AWS Lightsail 관련 설정

이전엔 DigitalOcean의 VPS에 LEMP (Nginx, MySQL, PHP)를 설치하고, 여기에 WordPress를 설치해서 사용하고 있었는데, 막상 사용하다보니 해외서버라서 그런지 좀 느렸다.Amazon Lightsail 역시 VPS로 아마존 서버를 사용하고, 서울에 있는 서버를 사용하니 꽤 빠르더라. 그리고 같은 가격에 DigitalOcean의 같은 가격대의 상품보단 쪼~~~~끔 더 나았고.

쓸데없는 삽질임에도 뭐… 연습 겸 ㅋㅋㅋ WordPress 서버 이전!!

Lightsail에 인스턴스를 하나 생성하고, 기본적인 LEMP 환경을 구축한다. 참고 링크 (https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04)

그런 다음엔

  1. 기존 wordpress의 wp-content 폴더를 백업한다. (압축파일, 꽤 용량이 크다)
  2. 데이터베이스를 백업한다.
$ mysqldump -u {your_database_id} -p {your_database_name} > backup_mysql_db.sql
  1. 백업한 파일을 일단 로컬PC로 옮기고… (scp를 사용하면 편함)
  2. 이것을 Lightsail VPS로 옮겨준다.

새로운 서버에 WordPress를 설치하고 (새로 설치하는 것처럼), 데이터베이스와 사용자 환경은 기존 사용자와 같게 끔 설치한다.

다음으로,

  1. 백업한 wp-content 파일을 같은 디렉토리에서 압축을 풀어서 복구
  2. 백업한 데이터베이스를 다시 복구
$ mysql -u {your_database_id} -p {your_database_name} < backup_mysql_db.sql

이렇게 하면 일단은 완료.


Lightsail은 Static IP도 일단은 공짜로 제공.

DNS 관련해선 DNS zone를 생성해서 설정

그 다음 Firewall은 서버 내부에서도 설정가능하지만, 외부도 해줘야 함. (서버 인스턴스에서 설정) 요걸로 좀 많이 삽질함.

이렇게 하면 끝!

iCloud Drive 강제 동기화 하기

맥북, 아이패드, 아이폰 등에서 파일 및 데이터를 공유하여 사용하기 위해서 iCloud Drive를 사용 중입니다. 요 근래 맥북에 업데이트되는 과정에서 iCloud Drive의 데이터가 동기화되지 않거나, 특정 프로그램에서 링크가 깨지는 등의 증상이 나타나서 해결책을 찾아보았습니다.

에러증상 #1

“documents” can’t be opened because the original item can’t be found

에러증상 #2

맥북의 iCloud Drive의 파일과 다른 기기와의 파일 리스트가 서로 다름

이를 해결하기 위해선,

먼저 ~/Library/Application Support/CloudDocs 폴더를 지운다. 그리고, bird, cloudd 프로세스를 중지한다.

$ rm -rf ~/Library/Application Support/CloudDocs
$ killall bird
$ killall cloudd

이렇게 되면, iCloud Drive의 리스트가 업데이트 되고, 다시 재동기화하는 과정을 진행한다.

끝.

Fix Screen Flickering of Dell Laptop on Ubuntu 18.04.3

현재 작업용으로 델 XPS15 9570 모델을 사용하고 있습니다. 처음 Ubuntu를 설치하여 사용할 때는 큰 문제는 없었는데, 최근들어 Ubuntu 설치때부터 사용중에도 화면이 계속 깜빡이거나 픽셀이 깨지는 등의 에러가 발생하였습니다.


덧: 검색해 본 결과, i915 (인텔 그래픽 드라이버)의 문제로, 4K 모니터를 가진 인텔 랩탑에서 대부분 발생하는 문제로 보임.


마치 하드웨어 (디스플레이 부분)의 불량인듯 보였지만, 윈도우로 부팅하게되면 이런 증상은 나타나지 않았기에 서비스를 맡기기에도 애매하였는데요. 검색을 해보니 (키워드: kernel 5.0, screen flickering) 리눅스 커널이 5.0 버전으로 올라가면서 eDP (아마도 디스플레이 패널 관련) 드라이버의 코드가 변경됨에 따라 일부 LCD 패널에서 발생하는 문제라고 합니다.

It was introduced by an optimization for eDP 1.4+ (“link config fast and narrow”) in the 5.x kernel; the patch doesn’t work for some panels, including that of the XPS 15, and had to be rolled back.

이 문제를 해결하는 방법으론 커널 버전을 이를 해결한 버전으로 올려야 한다고 하는데, 찾아보고 설치해 본 결과 커널 버전을 올려도 이와 같은 문제가 해결되진 않았습니다.

다음 방법으론 커널 소스를 직접 받아, 문제되는 부분을 수정하고, 커널을 빌드하여 사용하는 방법이 있습니다. 다음의 과정은 이와 같은 과정을 정리하였습니다.

먼제 커널 빌드에 필요한 패키지를 설치합니다. (Install necessary packages)

$ sudo apt install git build-essential kernel-package fakeroot libncurses5-dev libssl-dev ccache flex bison

작업을 위한 디렉토리를 생성하고, 커널 소스를 받아옵니다. 현재 사용하고 있는 커널 버전과 가장 유사한 버전을 가져오면 됩니다. 사용하고 있는 커널 버전은 “uname -r” 명령을 통해 확인 가능합니다.

make directory for kernel build and clone kernel source

업데이트: 현재 (2010/01) LTS 최신 커널 버전은 5.3.0-26이다.

$ cd ~
$ mkdir kernel_build 
$ cd kernel_build 
$ git clone -b linux-5.3.y git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
Cloning into 'linux-stable'...

remote: Enumerating objects: 8131608, done.
remote: Counting objects: 100% (8131608/8131608), done.
remote: Compressing objects: 100% (1204105/1204105), done.
Receiving objects: 100% (8131608/8131608), 1.30 GiB | 2.21 MiB/s, done.
remote: Total 8131608 (delta 6878708), reused 8130931 (delta 6878032)
Resolving deltas: 100% (6878708/6878708), done.
Checking out files: 100% (63144/63144), done.

소스를 받아오는데 좀 시간이 걸립니다. 해외망 속도가 빠르면 빨리 받아오기도…

다음으로 현재 사용중인 커널 버전의 빌드 configuration 파일을 복사해옵니다. move linux-stable directory, and copy current configuration of your current kernel.

$ cd linux-stable
$ cp /boot/config-`uname -r` .config

이제, 복사해온 configuration 파일을 현재 받은 커널 소스의 빌드 환경에 적용해 주도록 합니다. Now you have to adapt the old configuration to the new kernel.

$ yes '' | make oldconfig

이제 문제가 된 eDP 관련 소스 파일을 수정해 줍니다.

$ vi drivers/gpu/drm/i915/display/intel_dp.c

대략 2120번 라인으로 가보면, intel_dp_compute_link_config 함수 내에 다음의 라인을 수정해줍니다.

업데이트: 커널 버전이 바뀌면서 소스 파일의 위치 및 라인 변경

        if (intel_dp_is_edp(intel_dp)) {
		/*
		 * Use the maximum clock and number of lanes the eDP panel
		 * advertizes being capable of. The panels are generally
		 * designed to support only a single clock and lane
		 * configuration, and typically these values correspond to the
		 * native resolution of the panel.
		 */
		limits.min_lane_count = limits.max_lane_count;
		limits.min_clock = limits.max_clock;
	}

이 부분을,

        if (false && intel_dp_is_edp(intel_dp)) {
		/*
		 * Use the maximum clock and number of lanes the eDP panel
		 * advertizes being capable of. The panels are generally
		 * designed to support only a single clock and lane
		 * configuration, and typically these values correspond to the
		 * native resolution of the panel.
		 */
		limits.min_lane_count = limits.max_lane_count;
		limits.min_clock = limits.max_clock;
	}

와 같이 수정합니다.


이제 커널을 빌드합니다.

$ make -j8 deb-pkg

시간이 꽤 걸린 후에 빌드가 완료되면 상위 디렉토리에 몇개의 deb 파일이 생성됩니다. 이를 설치해주면 됩니다.

$ ll *.deb
-rw-r--r-- 1 byeongkyu byeongkyu  11204740 Jan 18 07:20 linux-headers-5.3.18+_5.3.18+-1_amd64.deb
-rw-r--r-- 1 byeongkyu byeongkyu  59519512 Jan 18 07:21 linux-image-5.3.18+_5.3.18+-1_amd64.deb
-rw-r--r-- 1 byeongkyu byeongkyu 841223388 Jan 18 07:26 linux-image-5.3.18+-dbg_5.3.18+-1_amd64.deb
-rw-r--r-- 1 byeongkyu byeongkyu   1057176 Jan 18 07:20 linux-libc-dev_5.3.18+-1_amd64.deb

linux-libc-dev, linux-headers, linux-image 요 세개의 패키지를 설치하고 완료된 이후 재부팅하면 적용됨을 볼 수 있습니다.

물론 화면의 깜빡임 역시 해결되었습니다.

Change the category title color and thickness of scrollbar on Blockly

Blockly의 처음 인상은 굉장히 단순해 보인다. 몇가지만 수정하면 좀더 이쁘게 보일수 있을 것 같은데, 일단은 코드 상에서 해결해야 하는 제목에서 보이는 두 내용을 적용하도록 수정해본다.

소스 파일의 내용 기준은 현재 가장 최신버전인 blockly-3.20191014.4를 기준으로 한다. 추후 업데이트 되더라도 큰 차이는 없을테니 잘 찾아서 적용하면 될듯.

Scrollbar Thickness

blockly-3.20191014.4/core/scrollbar.js [301:305]

Blockly.Scrollbar.scrollbarThickness = 15;
if (Blockly.Touch.TOUCH_ENABLED) {
  Blockly.Scrollbar.scrollbarThickness = 25;
}

위 두 값 (15, 25)를 원하는 값으로 수정. 대략 4, 8 정도면 적당한 듯.

Category Title Color

blockly-3.20191014.4/core/toolbox.js [259:275]

Blockly.Toolbox.prototype.handleBeforeTreeSelected_ = function(node) {
  if (node == this.tree_) {
    return false;
  }
  if (this.lastCategory_) {
    this.lastCategory_.getRowElement().style.backgroundColor = '';
    this.addColour_(); // add
  }
  if (node) {
    var hexColour = node.hexColour || '#57e';
    node.getRowElement().style.backgroundColor = hexColour;
    node.getRowElement().getElementsByTagName('span')[1].style.color = '#fff'; // add
    // Add colours to child nodes which may have been collapsed and thus
    // not rendered.
    this.addColour_(node);
  }
  return true;
};

blockly-3.20191014.4/core/toolbox.js [605:627]

Blockly.Toolbox.prototype.addColour_ = function(opt_tree) {
  var tree = opt_tree || this.tree_;
  var children = tree.getChildren(false);
  for (var i = 0, child; child = children[i]; i++) {
    var element = child.getRowElement();
    if (element) {
      if (this.hasColours_) {
        var border = '8px solid ' + (child.hexColour || '#ddd');
        var colour = child.hexColour || '#000'; // add
      } else {
        var border = 'none';
        var colour = '#000'; // add
      }
      element.getElementsByTagName('span')[1].style.color = colour; // add
      if (this.workspace_.RTL) {
        element.style.borderRight = border;
      } else {
        element.style.borderLeft = border;
      }
    }
    this.addColour_(child);
  }
};

위와 같이 // add 라고 되어 있는 줄을 함수 내에 추가하고, blockly 디렉토리 내에서 빌드를 해보면,

$ ./build.py

압축된 javascript 파일이 생성된다. 이를 이용하여 데모페이지를 보게 되면,

정해진 카테고리의 테마 색상에 따라, 카테고리의 타이틀 색상과 스크롤바의 크기 역시 세련되게 변경 됨을 볼 수 있다.

sha256sum을 이용해 파일의 유효성을 검증해보기

Ubuntu나 Raspbian 등 운영체제를 설치하기 위해선 해당 배포 웹페이지에서 iso 파일을 받게 된다. 보통 배포되는 웹페이지를 보면 파일과 함께 md5, sha256 파일이 같이 보여지게 되는데, 이를 이용해 내가 다운로드 받은 파일이 제대로 받아진 것인지를 확인할 수 있다.

macOS나 Ubuntu에선 기본으로 sha 관련 툴이 제공된다. 먼저 다운로드 받은 파일의 sha256 hash 코드를 확인해보려면,

$ shasum -a 256 ubuntu-18.04.3-desktop-amd64.iso
add4614b6fe3bb8e7dddcaab0ea97c476fbd4ffe288f2a4912cb06f1a47dcfa0  ubuntu-18.04.3-desktop-amd64.iso

결과로 다음과 같이 hash 코드가 생성되고 파일명을 같이 보여준다. 이를 위에서 보여졌던 SHA256SUMS 파일을 받아 비교해 보면,

$ cat SHA256SUMS.txt 
add4614b6fe3bb8e7dddcaab0ea97c476fbd4ffe288f2a4912cb06f1a47dcfa0 *ubuntu-18.04.3-desktop-amd64.iso
b9beac143e36226aa8a0b03fc1cbb5921cff80123866e718aaeba4edb81cfa63 *ubuntu-18.04.3-live-server-amd64.iso

눈으로 하나 하나 비교해봐도 되겠지만, 검사 기능도 역시 지원한다.

$ shasum -a 256 -c SHA256SUMS.txt               
ubuntu-18.04.3-desktop-amd64.iso: OK
shasum: ubuntu-18.04.3-live-server-amd64.iso: 
ubuntu-18.04.3-live-server-amd64.iso: FAILED open or read
shasum: WARNING: 1 listed file could not be read

받을 파일 중 ubuntu-18.04.3-desktop-amd64.iso는 무결함을 검증하였다. 이제 안심하고 이 이미지 파일을 사용할 수 있게 된다.

Install Confluence Server on VPS

Confluence (https://www.atlassian.com/ko/software/confluence)는 협업 문서 작성 도구로 잘 알려져 있는 툴이다. 많은 개발 회사들에 사용하는 것으로 알려져 있다. 무료는 아니고, 클라우드 서비스, 설치형 둘다 제공한다.

한번 써보고자 시도해 봤는데 (그냥 새로운 걸 좋아하는 취지에서…) 클라우드 서비스를 사용하려면, 한달에 $10을 내야 한다. 현재 블로그를 운용하는 VPS가 약간은 잉여인지라, 거기에 추가해보면 어떨까 해서 다시 삽질을 시작해봤다.

설치는 대단히 간단하다. 서버에 설치 스크립트 파일을 저장하고, 이를 루트 권한으로 실행하면 끝.

설치 방법은 https://confluence.atlassian.com/doc/installing-confluence-on-linux-143556824.html에 나와 있는대로, 몇번 엔터키만 툭탁하면 설치는 완료.

한글 관련 문제가 있어, 데이터베이스를 생성할 때, utf-8을 기본값으로 설정해야 한다. 이 작업을 하지 않으면, 한글 문서를 적어도 깨져보이는 에러가 발생한다.

mysql 설정, 파일에 다음의 두 라인 추가

/etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
...
character_set_server=utf8
collation-server=utf8_bin 

데이터베이스 생성

$ mysql -u root -p

mysql> CREATE DATABASE <database-name> CHARACTER SET utf8 COLLATE utf8_bin;
mysql> GRANT ALL PRIVILEGES ON <database-name>.* TO '<confluenceuser>'@'localhost' IDENTIFIED BY '<password>';

기본 접근 경로는 http://<your_domain>:8090 이다. 처음 접속 시 라이센스 및 데이터베이스 관련 설정을 해야 하고, 약간의 시간이 걸리고 나면 완료.

설치형은 $10를 한번만 지불하면 된다. 물론 기술지원 (메이저 업데이트)을 위해선 1년마다 $10을 지불해야 하지만, 납득할 만한 가격인듯.

다음으로 현재 블로그에 사용하는 SSL을 이용해, 보안접속이 가능하도록 설정한다.

내부 프록시를 사용하는 방법을 사용하고, 또 현재 도메인을 이용해 <your-domain>/confluence 와 같이 시용하기 위해 몇가지 설정을 한다. 이를 위해서 nginx 설정 파일, confluence의 server.xml 파일을 수정한다.

/etc/nginx/sites-enabled/default

다음 라인 추가.

         location /confluence {
                client_max_body_size 100m;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://localhost:8090;
        }

        location /synchrony {
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://localhost:8091/synchrony;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
        }

다음 라인 코멘트 처리

    #location = /favicon.ico { log_not_found off; access_log off; }
    #location = /robots.txt { log_not_found off; access_log off; allow all; }
    #location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
    #    expires max;
    #    log_not_found off;
    #}
/opt/atlassian/confluence/conf/server.xml

아래와 같이 수정

    <Connector port="8090" connectionTimeout="20000" redirectPort="8443"
        maxThreads="48" minSpareThreads="10"
        enableLookups="false" acceptCount="10" debug="0" URIEncoding="UTF-8"
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        proxyName="ahnbk.com" proxyPort="443" scheme="https" secure="true" compression="on"/>

...

    <Context path="/confluence" docBase="../confluence" debug="0" reloadable="false" useHttpOnly="true">
                    
...

한글 문제 해결을 위해서, mysql 연결관련 설정에서 링크를 다음과 같이 설정.

jdbc:mysql://localhost:3306/confluence?useUnicode=true&characterEncoding=utf8

위와 같이 수정하고, nginx, confluence 재시작

$ sudo service nginx restart
$ sudo service confluence restart

이제 https://ahnbk.com/confluence 로 접속해보면 (도메인 이름은 각자 상황에 맞게)

와 같이 정상적으로 보인다. ^^ 성공!

Python으로 macOS Text to Speech 엔진 (NSSpeechSynthesizer) 사용해보기

로봇이든 다른 분야에서든 TTS (Text To Speech) 엔진이 필요할 때가 있는데, 보통은 오픈 소스로 공개된 저품질의 엔진이나, 클라우드 엔진을 사용하곤 한다. 물론 요즘 딥러닝 기술이 많이 발전하여 학습 후 실제 목소리와 비슷한 소리를 내는 것도 가능하다곤 하지만, 그건 논외로 하고….

macOS에는 NSSpeechSynthesizer라는 훌륭한 품질의 TTS 엔진 – 그것도 거의 모든 나라의 목소리가 포함되어 있는 – 이 내장되어 있다. 이를 사용하기 위해선 Swift나 ObjectC를 사용할 수도 있지만, 사용하기 쉬운 Python을 이용해서 사용도 가능하다.

먼저 필요한 패키지를 설치한다. 이를 위해 Python3, pip3가 설치되어 있어야 한다. Homebrew (https://docs.brew.sh/Installation)를 이용하면 쉽게 설치 가능하다.

$ pip3 install -U pyobjc

pyobjc 모듈은 Python과 ObjC 간 연결을 위한 것이다. pyobjc는 메타모듈로 이를 설치하면 사용에 필요한 거의 모든 모듈을 다 설치한다.

이제 Python3를 이용하여 다음의 스크립트를 실행해본다.

from AppKit import NSSpeechSynthesizer

speech = NSSpeechSynthesizer.alloc().initWithVoice_("com.apple.speech.synthesis.voice.yuna.premium")
speech.startSpeakingString_(u'안녕? 만나서 반가워요.')

한국어 유나의 목소리로 맑은 음성을 들려줄 것이다. 이외에도 제어를 위한 다양한 함수들이 존재한다.

speech.isSpeaking()
speech.stopSpeaking()

사용 가능한 목소리 리스트를 확인하려면 다음과 같이 입력한다.

>> NSSpeechSynthesizer.availableVoices()

고품질의 목소리를 미리 다운로드 받아야 한다. System Preferences > Accessibility > Speech 에서 System Voice를 선택, Customize 항목으로 들어가면 각 나라별 목소리 리스트와 다운로드 받을 수 있는 체크박스가 존재한다.

끝.

닭볶음탕

닭은 양에 맞는 적당한 크기를 닭볶음탕용으로 준비. (토막토막)

찬물에서 한번 씻고, 넓은 냄비에 넣고, 물은 잠방잠방하게 넣어서 끓인다.

물이 끓기 전, 설탕 or 당분을 취향에 맞게 넣는다.

물이 끓으면, 감자, 당근, 양파를 넣고 끓인다.

야채가 어느 정도 익었다 싶으면, 간마늘 크게 한 수푼 넣고 또 끓인다.

푸욱 끓인 후,

간장 한컵을 넣고

버섯 등 곁들일 야채 추가.

또 끓인 다음, 고추가루를 취향껏 넣는다. 이때 간를 봐서 맞춰야 함. 짜면 물을 보충, 싱거우면 간장 추가.

이제 먹으면 끝!

Install WSL 2 on Windows 10

많이 알려져 있는 사실이긴 한데, (마이크로소프트가 어떤 변덕인지는 몰라도) Windows 10에서 Linux를 손쉽게 사용할 수 있게 되었다. 복잡한 설치가 필요한 것도 아니고, 그냥 Microsoft Store에서 클릭 한번이면 설치가 완료된다. 아직 Native Linux에는 좀 미치진 못하지만, 그래도 쉽게 설치하고 써볼 수 있다는게 어딘가… ㅎㅎ

마이크로소프트의 웹페이지에서 쉽게 설명하곤 있지만, 그래도 설치는 해봤으니 여기에다가 정리

설치

몇번 재부팅 과정이 필요하다. 처음 WSL 기능 활성화 할때 한번, WSL 2로 변환하기 위해 Virtual Machine Platform 기능을 활성화 하기 위해서 한번.

Power Shell을 Administrator 모드로 연다. (왼쪽 하단 돋보기 버튼을 누르고 power shell을 입력한 후, 앱 아이콘이 보이면 오른쪽 버튼을 눌러 Run as Administrator를 선택하면 된다.)

> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

y를 누르면, 재부팅하면서 WSL 기능을 활성화 한다.

각종 배포판을 지원하긴 하지만, 무난한 Ubuntu 18.04 LTS를 설치해본다. (다른 배포판들은 https://docs.microsoft.com/en-us/windows/wsl/install-win10 에서 확인 가능)

Get 버튼을 눌러 구입(?)하면, 자동으로 설치까지 완료.

시작메뉴를 확인해보면, 위와 같이 앱이 설치되어 있음을 확인할 수 있다. 이제 저걸 실행해보면,

어설픈 터미널창이긴 한데, 실제 Ubuntu의 터미널 환경을 제공한다. 물론 apt-get으로 패키지들도 설치 가능하다.

이제 잠시 종료하고, WSL 2로 업데이트해보도록 한다. 개선된 버전으로 64bit 실행파일을 지원한다곤 하는데, 자세한 설명은 여기(https://docs.microsoft.com/en-us/windows/wsl/wsl2-about)를 참조하면 될듯.

다시 Power Shell를 위와 마찬가지로 Administrator 모드로 열고,

> Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

을 실행하고, 재부팅한다.

다시 Power Shell을 열고, 다음과 같이 입력하여, 기설치된 배포판을 WSL 2로 변환한다.

> wsl --set-version Ubuntu-18.04 2

약간 시간이 흐르고, 변환이 완료되었다고 나오면 끝.


Windows 10에선 기존 허접하게 생긴 터미널 창을 대신하여, Windows Terminal (Preview) 버전도 제공된다. 역시 마이크로소프트 스토어를 통해서 설치가 가능하며, 여기를 눌러 설치한다.

실행해보면, 좀더 깔끔하게 생긴 터미널 창이 제공되고, 탭의 아래 꺽쇠 부분을 눌러보면 설정 파일에 접근할 수 있다.

터미널을 실행하면 바로 wsl로 접근할 수 있도록 프로파일을 추가해본다.

파일 중간 쯤, “profile” 섹션에 다음과 같이 추가한다.

{
      "acrylicOpacity": 0.9,
      "closeOnExit": true,
      "colorScheme": "Campbell",
      "commandline": "wsl -d Ubuntu-18.04",
      "cursorColor": "#FFFFFF",
      "cursorShape": "bar",
      "fontFace": "D2Coding",
      "fontSize": 9,
      "guid": "{c909ebdd-6f47-410b-a911-85f601d2cb80}",
      "historySize": 9001,
      "icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
      "name": "wsl",
      "padding": "0, 0, 0, 0",
      "snapOnInput": true,
      "startingDirectory": "/home/byeongkyu",
      "useAcrylic": true
    }

여기서 guid는 고유한 값이어야 하므로, 터미널을 열고 uuidgen을 실행하면 사용할 수 있는 guid 값이 생성되고 이를 사용하면 된다.

이제 이것저것 설치해보며 사용해보면 될듯하다. 끝!

[ROS] 패키지 소스 빌드 전 의존 패키지 설치하기

ROS를 개발하다보면 여러가지 패키지들을 사용해야 하고, 또한 오픈소스로 공개되어 있는 (주로는 github 등에서 받아오는) 패키지를 받아 빌드하여 사용해야할 경우가 생긴다.

이때 빌드하려는 패키지가 사용하는 의존 패키지들을 설치해줘야 빌드가 제대로 될 수 있는데, 이를 일일이 설치하는 것부터가 일이 되는 경우가 있다. README.md 파일에 이런 패키지들을 설치해야 합니다라고 알려주고 설치방법을 다음과 같이 패키지 리스트와 같이 알려 주는 경우도 있다.

$ sudo apt install package1 package2 ...

ROS에선 이를 쉽게 해결하기 위해서 rosdep 툴을 제공한다. rosdep이 하는 일을 보면, 여러가지 패키지들의 이름을 저장해 놓고, 각 패키지의 이름에 해당하는 시스템 패키지 명을 db로 저장하고 있는 형태이다. 기본적으론 rosdep.yaml 파일을 메인 레포지토리에서 받아와서 사용하고 있으며, 사용자가 이 파일을 수정하여 사용할 수도 있다.

각 패키지의 package.xml 파일엔 이 패키지가 의존하는 패키지들의 리스트가 저장되어 있다 (잘 만들어진 패키지라면…). rosdep은 각 패지키의 package.xml 파일을 읽어와, 의존 패키지 리스트를 만들고, 이를 위에서 설명한 db와 매칭하여 시스템 패키지를 설치하는 과정을 거친다.

따라서 패키지 디렉토리로 이동하여 다음과 같이 입력한다.

$ rosdep install --from-paths . --ignore-src -r -y

이렇게 하면 현재 디렉토리부터 하위 디렉토리를 순환하여 돌아다니며, 의존 패키지들을 자동으로 설치해 준다.

만약 파이썬으로 구성된 패키지이고, 의존 패키지를 requirements.txt 내에 잘 정리해 두었다면, 위와 같은 방법으로 다음과 같이 실행하면 의존 패키지를 pip를 이용하여 설치한다.

$ find -name 'requirements.txt' | xargs -L 1 sudo pip install -U -r

다른 사람이 만들어 놓은 패키지를 잘 이용하는 것도 좋지만, 위와 같은 과정을 잘 익혀두었다가 본인이 만든 패키지를 배포할때도 package.xml, requirements.txt 파일을 잘 정리하여 배포한다면 다른 사람이 별다른 수고없이도 쉽게 사용할 수 있게 된다.

Ubuntu와 Windows 멀티부팅 시 시스템 시간 변경 문제 해결

Ubuntu와 Windows를 듀얼부팅으로 사용하는 경우, Ubuntu를 사용한 다음 윈도우로 부팅하면 현재 시각이 다른 값으로 설정되어 있다. 자동으로 조정 기능을 껐다가 키면 다시 정상 시각으로 설정되지만 매번 그러기엔 아주 귀찮다.

Ubuntu에서 시각을 UTC를 기준으로 삼아 사용해서 생기는 문제인데, 이를 사용하지 않고 로컬타임으로 사용하게끔 하면 문제는 해결된다.

16.04 이후 버전부터는 터미널에서 다음과 같이 입력한다.

$ sudo timedatectl set-local-rtc 1

이렇게 되면, 이제 서로 다른 운영체제로 부팅하더라도 시스템의 시간은 변경되지 않는다.

커스텀 메시지를 이용할 경우 catkin 빌드 에러 해결

ROS 패키지를 개발할 때, Topic, Service, Action을 사용할 경우, 기존 정의되어 있는 여러가지 메시지를 사용할 수도 있지만, 편의상 사용자가의 커스텀 메시지를 이용해야 하는 경우가 있다.

이때 Python으로 개발하면 별다른 문제는 없지만, C++로 만든 노드의 경우 catkin 빌드를 하게 되면 커스텀 메시지의 header 파일을 찾을 수 없다는 에러가 나온다.

Errors     << dynamixel_ros_control:make /home/robot/catkin_ws/logs/dynamixel_ros_control/build.make.000.log                                                                        
In file included from /home/robot/catkin_ws/src/dynamixel_ros_control/include/dynamixel_ros_control/dynamixel_hw_interface.h:9:0,                                                   
                 from /home/robot/catkin_ws/src/dynamixel_ros_control/src/dynamixel_ros_control_node.cpp:3:                                                                         
/home/robot/catkin_ws/src/dynamixel_ros_control/include/dynamixel_ros_control/dynamixel_motor.h:11:48: fatal error: dynamixel_ros_control/HomingAction.h: No such file or directory 
compilation terminated.                                                                                                                                                             
make[2]: *** [CMakeFiles/dynamixel_ros_control_node.dir/src/dynamixel_ros_control_node.cpp.o] Error 1                                                                               
make[1]: *** [CMakeFiles/dynamixel_ros_control_node.dir/all] Error 2                                                                                                                
make: *** [all] Error 2    

몇번 빌드를 반복하다보면 에러 없이 빌드 되는 경우도 있지만 마땅한 해결 방법은 아니다. 몇번 골치를 썩다가 wiki의 투토리얼을 다시금 살펴보니 해결책이 나와있었다. 하지만 패키지를 만들때 생성되는 CMakeList.txt 파일엔 이 내용이 반영되어 있지 않아 혼란이 있는 듯 하다.

Writing a Simple Publisher and Subscriber (C++) 페이지를 보면 중간쯤 다음과 같이 입력하라고 나온다.

노드를 빌드하기 전, 노드에 의존 항목을 추가하여 위와 같은 에러를 방지하려는듯 하다. 따라서 CMakeList.txt 파일에 다음과 같이 입력하면 된다.

add_executable(${PROJECT_NAME}_node
  src/dynamixel_ros_control_node.cpp
  src/dynamixel_hw_interface.cpp
  src/dynamixel_motor.cpp
)

add_dependencies(${PROJECT_NAME}_node ${PROJECT_NAME}_generate_messages_cpp)
target_link_libraries(${PROJECT_NAME}_node
  ${catkin_LIBRARIES}
)

이제 다시 catkin 빌드를 할 때, 관련 에러는 발생하지 않는다.

[여름휴가] 괌

7월 3일 ~ 7월 7일에 괌으로 좀 이른 여름 휴가를 다녀왔습니다. 형 가족까지 포함하여, 총 7명 (어른4, 아이3)이 멤버였고, 이로 인해 호텔도 방 두 개를, 렌트카도 7인승 차를 빌려야 했네요.

아이들 컨디션을 적극적으로 고려하여, 일반적으로 많이 이용하시는 야간 비행보단 주간 비행 (진에어)을 이용하였습니다. 비행기 가격은 큰 차이가 없었고, 오히려 Late Check-in, Late Check-out을 이용하지 않는 아주 깔끔한 시간대였다고 생각합니다.

아침에 출발하는 비행기

7월이면 괌은 우기로 접어드는 때로, 시시때때로 소나기가 오는 기후라고 합니다만, 이번에 저희가 방문했을 때는 물놀이 할 땐 적당히 구름이 있는 날씨, 드라이브 할 땐 아주 파란 하늘을 보여줘서, 날씨 운도 좀 괜찮았습니다. 물론 덥고 습한 날씨였던건 뭐 어쩔수 없었고요.

호텔 베란다에서 보는 풍경
파란 하늘, 푸른 바다

여행 일정은 저희가 처음 괌에 가는 이유로, 많은 분들이 체험하는 코스로, 호텔 (온워드 리조트)도 그냥 무난한 곳으로 정했습니다. 실패는 하지 않는, 오히려 아이들은 충분한 물놀이로 재밌게 즐겼던 것 같습니다.

리조트 바로 옆 워터파크, 리조트 이용객은 공짜

숙소에 커다란 워터파크가 있고, 바로 앞은 맑고 푸른 바다가, 주변도 잠깐만 이동하면 식당이나 마트도 있어 편리했고, 오히려가 시내에서 살짝 거리가 있으니 사람들도 많지 않아 좋았습니다.

특히 셋째날엔 아들녀석을 위해 경비행기 조정 체험도 하였는데, 이륙부터 비행, 착륙까지 아들 녀석이 직접 조종간을 잡아 조종하였습니다. (물론 옆에서 도움주시는 조종사 분이 계시고요.) 처음엔 땀을 흘리면서 긴장하던 녀석도 시간이 지나니 엄청 재미있어하더라고요.

경비행기 타기 전
숙력된 조종사가 옆에서 보조해주면서 비행 조종 체험

총 5일의 일정 중, 하루는 렌트카를 끌고 괌 남쪽을 쭈욱 돌아봤습니다. 물론 인증샷을 위한 사진 포인트들도 이때 쭈욱 들르면 됩니다. 괌이 그리 크지 않은 섬이라서, 약 3~4시간이면 다 돌아볼만 합니다.

아마 무슨 성당?
바다 아래로 내려가는 수족관
드라이브하다가 적당한 곳에서 세운 후 본 풍경
길다가 찾은 식당
사랑의 절벽
리조트 앞 바다, 카누도 무료 대여

전반전으로 느낀 괌은 그냥 제주도 느낌이었습니다. 한국 관광객들도 많고, 한국인 업체들도 많고, 호텔 역시 한국어 서비스가 거의 기본입니다. 영어를 잘 하지 못하더라도 편하게 다녀올수 있는 휴양지라고 생각합니다. 아울렛 등에서 쇼핑도 즐길수 있고, 음식도 괜찮았습니다. 물가도 우려했던 바와는 달리 많이 비싸진 않았고요.

한국에 도착해도 이른 저녁

이번 여름 휴가 중 일단 하나 완료!

[vscode] 원격에 있는 머신의 코드를 수정하기

코드 편집기로 Visual Studio Code를 사용하고 있다. 주로 로봇의 PC에 설치하여 모니터와 키보드를 연결하고 작업을 하지만, 어느 정도 완성이 되고 나면 로봇을 동작시키면서 개발을 해야 하므로 모니터와 키보드는 더이상 연결할 수 없게 된다.

ssh를 이용하여 접속한 후 GUI를 미러링하여 사용할 수 있지만, 그렇게 되면 많이 느려지게 된다. 그래서 간단한 작업들은 터미널에서 vim을 이용하여 코드를 수정하곤 하는데, 이것저것 다수의 코드를 수정하려면 다소 힘들게 된다.

Visual Studio Code의 확장기능 중 Remote – SSH가 이럴 경우 대단히 유용한다. 로봇(또는 그냥 원격머신)의 PC에 ssh로 접속하여 그냥 로컬머신에서 vscode를 사용하는 것과 동일하게 사용할 수 있다.

설치는 Extentions 탭으로 이용하여 Remote – SSH를 찾아 설치하면 끝.

그렇게 되면 왼쪽 탭에 모니터와 같은 모양의 아이콘이 생성된다.

처음 실행하게 되면, 위와 같이 host를 설정하라는 메시지가 나온다. Configure를 클릭하면

와 같이 나오는데, 주로 본인 계정하에서 사용하므로 첫번째와 같이 본인의 홈 폴더에 생성하도로 한다. 그렇게 되면 기존 config 파일이 나오거나, 없으면 다음과 같이 템플릿을 생성하여 보여준다.

# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host alias
    HostName hostname
    User user

자 이제 원격PC에 접속하기 위한 계정 정보를 입력한다.

# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host robot1_pc
    HostName 192.168.9.10
    User robot

위와 같이 계정 정보와 서버의 주소를 입력한다. 만약 hosts 파일에 컴퓨터명을 등록했으면 주소 대신 컴퓨터명을 적으면 된다. 위의 내용을 터미널 상에서 ssh를 접속하게 된다면 다음과 같은 내용이라고 보면 된다.

$ ssh robot@192.168.9.10

저장하고 나면, 왼쪽 Connections 창에 이름이 나타난다. 이제 이름 옆 창 표시를 클릭하면, 새로운 vscode 창이 생기면서 원격 머신에 접속하게 된다.

접속이 완료되면, 왼쪽 하단에 원격 PC이름이 나타나고, 터미널도 역시 원격 PC의 터미널이 보임을 알 수 있다. 또한, Open Folder를 클릭하면 원격 PC의 디렉토리 및 파일들이 나타난다. 이제부턴 로컬 머신과 같이 사용가능하다. (물론 파일 복사나 이동 등도 가능)

팁.

매번 접속시 비밀번호를 묻는게 귀찮다면, 원격PC의 known_hosts에 로컬PC의 ssh 퍼블릭 키를 등록하면 된다. 키 생성은 ssh-keygen으로 하고, 생성된 id_rsa.pub 파일의 내용을 원격 PC의 known_hosts 파일에 추가한다.

Refresh hosts on macOS

많은 이유로 /etc/hosts 파일에 아이피 및 호스트 이름을 추가하여 사용할 경우가 많은데, Ubuntu에선 hosts 파일을 수정하기만해도 반영이 되는 반면, macOS에선 반영이 되지 않는다. (아마 이번 Catalina에서부터 그런듯..)

구글링을 해본 결과 hosts 파일을 수정하고, 터미널을 열어 다음과 같이 실행 해줘야 업데이트 되는듯 하다.

$ sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

끝.

macOS Launchpad 관련 팁

적어놓고 보니 예전 포스트(https://ahnbk.com/?p=340)랑 같은 내용인데… 사라진 어플리케이션 추가하는 방법 추가.

레이아웃 수정

기본 배열은 7×5로 되어 있음. 더 많은 수의 아이콘을 배열할 수도 있는데, 예를 들어 8×6으로 배치하고 싶다면 터미널을 열고,

$ defaults write com.apple.dock springboard-columns -int 8
$ defaults write com.apple.dock springboard-rows -int 6 
$ killall Dock

와 같이 입력하고 런치패드를 보면 변경되어 있는 것을 확인 가능.

기본값으로 돌리고 싶다면, 터미널을 열고

$ defaults delete com.apple.dock springboard-rows
$ defaults delete com.apple.dock springboard-columns
$ killall Dock

와 같이 입력하면 됨.

런치패드 리셋

Launchpad를 초기 설치 상태로 초기화 하고 싶다면,

$ defaults write com.apple.dock ResetLaunchPad -bool true
$ killall Dock

설치된 앱 아이콘이 런치패드에 안보인다면

Finder를 열고, /Application으로 이동. Cmd+A를 이용해서 어플리케이션들을 모두다 선택하고 이를 끌어서 Launchpad 아이콘 위에 놓는다. 그렇게 하면 앱 리스트 업데이트 됨.

Set display scale for Ubuntu 18.04 on Parallels 14

패러럴즈에 Ubuntu 18.04를 설치하고, 패러럴즈 툴을 설치했음에도 불구하고 해상도 설정이 좀 이상한 경우가 있다. 특히 Display scale을 조절할 수 있는 옵션이 나타나지 않는데, 이 경우 흐리멍텅한 화면이나, 네이티브 해상도 (아주 작은 글씨)로만 사용해야 한다.

검색해보니 설정에는 안보이지만, 터미널 상에서 UI 스케일을 조정할 수 있는 방법이 있었다.

$ gsettings set org.gnome.desktop.interface scaling-factor 2

원하는 스케일 팩터를 뒷부분에 적으면 된다. 2는 200%를 의미한다.

다시 초기값으로 돌리려면,

$ gsettings reset org.gnome.desktop.interface scaling-factor

와 같이 입력한다.

터미널에서 명령어를 입력 후, 재부팅하거나 Logout 후 재로그인하면 설정이 반영된다.

Kinematic Model for 3 Wheel Omni Drive Robot

3 Wheel Omni Drive 로봇은 메카넘휠(90º) 3개를 120º 간격으로 배치하여 구성하며, 바퀴 속도의 조합에 따라 전방향으로 이동 가능하다.

식은 다음과 같이 나타낼 수 있다.

 \dot{\phi} = J_{2}^{-1} J_{1f} R(\theta) \dot{\xi_{I}}  \dot{\xi_{I}} = R(\theta)^{-1}J_{1f}^{-1}J_{2}\dot{\phi}

여기서,

 \dot{\phi} = \begin{bmatrix} \dot{\phi}_{1} \\ \dot{\phi}_{2} \\ \dot{\phi}_{3} \end{bmatrix}  \dot{\xi_{I}} = \begin{bmatrix} \dot{x} \\ \dot{y} \\ \dot{\theta} \end{bmatrix}   J_{2} = \begin{bmatrix} 2r & 0 & 0 \\ 0 & 2r & 0 \\ 0 & 0 & 2r \end{bmatrix}  J_{1f} = \begin{bmatrix} sin(\frac{\pi}{3}) & -cos(\frac{\pi}{3}) & -l \\ 0 & -cos(\pi) & -l \\ sin(-\frac{\pi}{3}) & -cos(-\frac{\pi}{3}) & -l \end{bmatrix}  R(\theta) = \begin{bmatrix} cos \theta & sin\theta & 0 \\ -sin \theta & cos \theta & 0 \\ 0 & 0 & 1\end{bmatrix}

r는 바퀴의 반지름, l은 로봇 중심으로부터 바퀴까지의 거리이다.

이제 바퀴의 속도 변경에 따른, 로봇의 움직임을 구하려면,

 \begin{bmatrix} \dot{x} \\ \dot{y} \\ \dot{\theta} \end{bmatrix} = \begin{bmatrix} cos \theta & sin\theta & 0 \\ -sin \theta & cos \theta & 0 \\ 0 & 0 & 1\end{bmatrix}^{-1} \begin{bmatrix} sin(\frac{\pi}{3}) & -cos(\frac{\pi}{3}) & -l \\ 0 & -cos(\pi) & -l \\ sin(-\frac{\pi}{3}) & -cos(-\frac{\pi}{3}) & -l \end{bmatrix}^{-1} \begin{bmatrix} 2r & 0 & 0 \\ 0 & 2r & 0 \\ 0 & 0 & 2r \end{bmatrix} \begin{bmatrix} \dot{\phi}_{1} \\ \dot{\phi}_{2} \\ \dot{\phi}_{3} \end{bmatrix}

여기서 나온 결과, 즉 로봇의 속도 (\dot{x}, \dot{y}, \dot{\theta})를 누적하면 Odometry 정보를 얻을 수 있다.

이제 로봇을 제어하기 위해서, 원하는 로봇의 선속도 (\dot{x}, \dot{y} ), 각속도 (\dot{\theta})가 입력이 되면,

 \begin{bmatrix} \dot{\phi}_{1} \\ \dot{\phi}_{2} \\ \dot{\phi}_{3} \end{bmatrix} =  \begin{bmatrix} 2r & 0 & 0 \\ 0 & 2r & 0 \\ 0 & 0 & 2r \end{bmatrix}^{-1} \begin{bmatrix} sin(\frac{\pi}{3}) & -cos(\frac{\pi}{3}) & -l \\ 0 & -cos(\pi) & -l \\ sin(-\frac{\pi}{3}) & -cos(-\frac{\pi}{3}) & -l \end{bmatrix}  \begin{bmatrix} cos(0) & sin(0) & 0 \\ -sin(0) & cos(0) & 0 \\ 0 & 0 & 1\end{bmatrix}  \begin{bmatrix} \dot{x} \\ \dot{y} \\ \dot{\theta} \end{bmatrix}

와 같이 각 바퀴의 속도를 구할 수 있다. 이제 이 속도를 바퀴 모터에 적용하면 로봇이 원하는 방향으로 움직인다.

참고:

Ubuntu에서 USB 시리얼포트 low_latency 설정하기

로봇에 외장 기기를 부착할 경우 USB 시리얼포트 디바이스를 많이 사용한다. 요즘 나온 메인보드엔 시리얼포트가 없으니 당여한 이야기인데, USB 통신의 특성상 latency timer 값이 16ms로 기본으로 설정되어 있어, 빠른 응답 특성을 요구할때엔 속도 저하의 이유가 된다.

먼저 현재 설정된 latency_timer 값을 확인해본다.

$ cat /cat/sys/bus/usb-serial/devices/ttyUSB0/latency_timer
16

ttyUSB0는 사용자의 포트 번호에 따라 변경하여 사용하면 된다. 위와 같이 16ms로 설정되어 있는 값을 1ms로 변경해본다.

$ echo 1 | sudo tee /sys/bus/usb-serial/devices/ttyUSB0/latency_timer

문제는 이를 부팅때마다 반복해야 되는데, setserial 명령어를 udev 룰에 추가해서 이를 간단히 해결할 수 있다. 먼저 setserial를 설치한다.

$ sudo apt install setserial

다음으로 /etc/udev/rules.d로 이동해서, 99-ttyUSB.rules 파일을 만들거나, 이미 사용하고 있는 룰에 다음과 같이 추가한다.

KERNEL=="ttyUSB[0-9]*", MODE="666", ATTRS{idVendor}="0403", RUN+="/bin/setserial /dev/%k low_latency"

저장하고, 재부팅한다.

이제 명령어로 확인해보면

$ cat /cat/sys/bus/usb-serial/devices/ttyUSB0/latency_timer
1
$ cat /cat/sys/bus/usb-serial/devices/ttyUSB1/latency_timer
1
$ cat /cat/sys/bus/usb-serial/devices/ttyUSB2/latency_timer
1

모든 USB 시리얼포트의 latency_timer 값이 1ms로 설정되어 있음을 볼 수 있다.

[독서후기] 기계 요소 설계 (3판)

태생이 전자 공돌이라, 소프트웨어까진 어떻게 해본다 쳐도, 기구설계는 완전 다른 이야기인듯 하다. 물론 요즘에야 개발 도구들이 잘 되어 있어 뚝딱뚝딱 만들어 볼수 있다곤 하지만, 그래도 기본적인 사항은 알아야 이해를 하고 만들어볼 수 있는 듯.

3D 프린터를 좀더 알차게 사용해보고 싶은 마음에, 서점 간 김에 그래도 좀 예제가 많아보이는 책을 구입했다. 기초적인 요소들에 대한 설명과 각종 수치들이 있고, 한국에선 요런거 사용함 등등의 내용이 보인다. 아주 자세히 보진 못하겠지만, 간단한 기구 설계할 때 유용하게 사용할 수 있을 듯 하다.

교보문고: http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788942907403&orderClick=LAH&Kc=#N

[독서후기] 칼만 필터는 어렵지 않아

아마 예전 책 제목은 “칼만 필터의 이해”였던 것 같다. 칼만필터를 로봇에 적용해서 사용하긴 하는데 (OpenCV에서 함수 형태로 있으니…) 이게 어떻게 동작하는 것인지, 왜 적용해야 하는지에 대한 궁금함이 항상 있던 차에 예전 판본을 보고 이해했던 기억이 난다.

이번에 새로 개정되어 제목도 바뀌어 출시되었길래, 또 다시 구입했다. 내용도 약간 증가한듯 하고, 비록 매틀랩 코드이지만 예제도 잘 구현되어 있다. (이걸 파이썬이나 다른 언어로 바꾸는거야 머, 쉬운 작업이니)

교보문고: http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9791156644415&orderClick=LEB&Kc=

소라게 집 바꿔주기

한달 전 쯤? 아들 녀석이 방과후 학교에서 소라게 한마리를 가지고 왔습니다. 조그만 플라스틱 케이스와 젤리처럼 생긴 먹이 하나와 같이요. 사실 며칠 지나지 않아 죽거나 할 줄 알았는데 의외로? 한달이 지났는데도 잘 지내고, 밤이 되면 부스럭부스럭 조그만 케이스를 탈출하고자 노력하더군요. ㅠㅠ

인터넷을 검색해보니 소라게의 수명이 엄청 길더라고요. 자연에서 사는 녀석들은 몇십년을 산다고도 하고… 암튼 이대로는 안되겠다 싶어 인터넷을 검색해보고, 다이소에서 적당한 집 (큰 플라스틱 케이스)을 사오고, 코코칩이라 불리는 바닥재, 먹이용 젤리를 주문했습니다.

두텁게 코코칩을 깔아주고, 소라게를 새로운 집에 옮겨주고 젤리를 하나 까서 같이 놓아주었습니다.

원래 소라게의 습성이 어두운 밤에 주로 움직이고, 저런 코코칩 같은 바닥재를 깔아주면 파고 들어가서 산다고 하네요. 지금도 계속 바닥으로 파고들고, 가끔씩 먹이 있는 쪽으로 움직여 젤리를 파먹고 하고 있습니다. 새로운 집을 맘에 들어 하는 것 같습니다.

바닥재는 3주 정도에 한번씩 갈아주고, 그 동안은 너무 건조해지지 않도록 스프레이로 물을 조금씩 뿌려주면 되고, 젤리도 갈아주면 된다고 합니다. 혼자는 외로워할테니 한마리 더 넣어줘도 좋을듯 한데… 저 녀석이 암컷인지 숫컷인지 알 방법이 없네요.?? ^^

스팸무스비

이름은 뭔가 있어보이는데, 그냥 스팸 들어간 김밥인데 모양은 좀 다른 것. 아주~ 간단히 만들수 있고 재료를 좀더 추가하면 고급스럽게도 만들수 있다.

재료: 밥 2공기, 참기름 두 큰술, 깨소금 약간, 소금 약간, 스팸 작은 통 하나, 김밥용 김 2장.

  1. 밥 2공기를 큰 그릇에 덜어놓고 한소큼 식힌다. 참기름 혹은 들기름 2 큰술, 소금 약간, 깨소금 약간 넣고 잘 섞어준다. 밥의 간은 약간 싱겁게 해야 나중에 스팸의 간과 섞여 괜찮다.
  2. 스팸 작은 통을 열고 다 꺼낸 다음 스팸통 길이 방향으로 4개로 나눈다.
  3. 자른 스팸은 후라이팬에 잘 구워준다.
  4. 투명랩을 좀 크게 잘라, 빈 스팸통에 잘 깔아준다.
  5. 밥을 통의 1/3민큼 채우고, 그 다음 구운 스팸을 넣어준다.
  6. 나머지 공간을 또 밥으로 채운다.
  7. 랩을 잘 싸서 꺼낸다.
  8. 4 ~ 7 과정을 반복.
  9. 김밥용 김을 반으로 나눈다.
  10. 8번까지 만든 것을 김밥 중앙에 넣고, 김을 잘 싸준다.
  11. 먹기 좋게 썰어서 먹는다.

스팸 이외에 계란스크램플, 볶음양파, 볶음 김치 등을 추가해서 싸 먹어도 별미임.

끝!

Blockly 블럭 만들기 #1

Blockly는 사용자가 원하는 기능의 블럭을 자유롭게 만들수 있다. 기본 제공된 블럭 외에 사용자의 로봇이나 기타 기능과 연동하기 위한 블럭을 생성하고, 이를 툴박스에 넣어 사용하면 된다.

Blockly는 이러한 블럭들을 쉽게 만들수 있도록 도구를 제공한다. 재밌게도 블럭을 생성하는 것도 블럭으로 만든다. 물론 나중에 좀 익숙해지만 텍스트로 편집하는게 좀더 쉬울 수 있지만, 제공되는 도구를 이용하면 생성되는 블럭 모양을 확인하면서 수정이 가능하다.

먼저 Blockly Developer Tools를 사용해보자. 사파리나 크롬 등 브라우저를 열고 (https://blockly-demo.appspot.com/static/demos/blockfactory/index.html)으로 이동한다.

위와 같은 화면이 나오는데, 왼쪽은 블럭을 생성하기 위한 블럭 코딩 영역, 오른쪽 상단엔 생성된 블럭 모양을 실시간으로 확인이 가능하고, 오른쪽 중앙엔 추후 코드에 삽입하기 위한 JSON 코드, 오른쪽 하단엔 블럭을 실제 코드로 생성하기 위한 generator 코드가 생성된다.

일단 흐름을 보기 위해 아주 간단한 블럭을 생성해보도록 한다. 사용자의 문자열 입력을 받아 console에 출력하는 블럭을 만들어 보록 한다. 먼저 블럭의 name을 수정한다. 여기선 console_print로 한다.

다음으로 이 블럭은 다른 블럭들과 상하 연결하여 사용 가능해야 하므로, connection을 top+bottom connections로 변경한다. 이렇게 되면 미리보기 창의 블럭 모양이 변경됨을 볼 수 있다.

다음으로 사용자의 문자열을 입력 받아야 한다. 왼쪽 툴박스 창에서 Input 카테고리내에 value input 블럭을 inputs에 추가한다. 이 입력을 받아서 내부에서 처리하기 위한 변수명은 value_input 옆에 있는 NAME이다. 이를 필요에 따라 적절하게 수정한다.

이제 블럭에 기능을 설명하기 위한 텍스트 블럭을 추가한다. (Field 카테고리 Text 블럭)

입력은 Text만 받아야 하므로, 입력 type에 String 블럭을 추가한다.

마지막으로 블럭의 색상과, 이 블럭을 사용하는데 필요한 tooltip, help url 등을 입력한다.

이제 생성된 블럭의 모양을 확인해보면,

와 같이 간단한 모양의 블럭이 생성되었다. 생성된 JSON 코드를 확인해보면,

{
  "type": "console_print",
  "message0": "console print %1",
  "args0": [
    {
      "type": "input_value",
      "name": "NAME",
      "check": "String"
    }
  ],
  "previousStatement": null,
  "nextStatement": null,
  "colour": 330,
  "tooltip": "Block for printing the user message",
  "helpUrl": "https://ahnbk.com"
}

그리고, 이와 같이 생성된 Generator stub을 보면,

Blockly.JavaScript['console_print'] = function(block) {
  var value_name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);
  // TODO: Assemble JavaScript into code variable.
  var code = '...;\n';
  return code;
};

와 같이 되어 있다. 자 이제 블럭이 생성되었으므로, 이를 지난번에 만들었던 예제에 생성된 블럭을 추가해보도록 한다. 예제 디렉토리로 이동하여, my_blocks.js 파일을 생성하고, 다음과 같이 입력한다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    // insert blocks here

]);

이제 “insert blocks here 부분에 위에서 생성된 코드를 붙여 넣는다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    {
        "type": "console_print",
        "message0": "console print %1",
        "args0": [
          {
            "type": "input_value",
            "name": "NAME",
            "check": "String"
          }
        ],
        "previousStatement": null,
        "nextStatement": null,
        "colour": 330,
        "tooltip": "Block for printing the user message",
        "helpUrl": "https://ahnbk.com"
      },
]);

저장한 다음, index.html 파일을 열고 위 파일을 로딩하도록 추가한다.

<!DOCTYPE html>
<!-- HTML file to host Blockly in a mobile WebView. -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <style type="text/css">
    html, body, #blocklyDiv {
      border: 0;
      height: 100%;
      margin: 0;
      padding: 0;
      width: 100%;
    }
  </style>
  <script src="blockly_compressed.js"></script>
  <script src="blocks_compressed.js"></script>
  <!-- TODO: Select msg file based on locale. -->
  <script src="msg/js/en.js"></script>
  <script src="toolbox_standard.js"></script>
  <script src="my_blocks.js"></script>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script type="text/javascript">
    var workspacePlayground = Blockly.inject('blocklyDiv', {
          media: 'media/',
          toolbox: BLOCKLY_TOOLBOX_XML['standard'],
          zoom: {controls: true}
        });
  </script>
</body>
</html>

바로 이전 포스팅에서 만든 Custom 카테고리에 생성된 블럭을 추가한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+   '</block>'
+ '</category>'

자, 이제 예제를 다시 열어보면,

Custom 카테고리에 console print 블럭이 추가되어 있음을 볼 수있다. 만약 문자열 입력에 default 값을 넣어 주고 싶다면, 툴박스에 코드를 다음과 같이 수정한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+     '<value name="NAME">'
+       '<shadow type="text">'
+         '<field name="TEXT">Hello Blockly</field>'
+       '</shadow>'
+     '</value>'
+   '</block>'
+ '</category>'

이제 다시 예제를 확인해보면,

와 같이 되어 있음을 확인할 수 있다. 다음엔 좀더 다양한 모양의 블럭을 만들어보록 한다.

Scratch 3.0 Scratch Link를 수정해보기

Scratch 3.0은 외부기기와 연동하기 위해서 Scratch Link 앱을 이용해야 한다. (Scratch 내부에 구현할 수도 있을 것 같은데, 이렇게 되면 다른 운영체제에서 호환이 안될테니 아마도 이렇게 만들어 놓은듯 하다) 내부를 들여다보면 Scratch Link에서 WebSocket 서버를 제공하고, BLE, BT로 구분하여 접속할 수 있다. 통신은 JSONRPC를 이용한다. BLE, BT 모두 프로토콜은 동일하다.

따라서 Bluetooth 시리얼, BLE 등을 이용한 기기들은 기존 프로토콜을 이용하여 접속 및 연동이 가능하다. OROCA-Edubot도 이와 같은 과정을 통해 연동에 성공하였다. 이제 좀더 나아가 시리얼포트를 이용할 경우 (예를 들어 Arduino 보드나 사용자가 개발한 보드들)엔 어떻게 해야 할까?

고민해본 결과 가장 클리어한 방법은 Scratch Link를 확장하여 시리얼 통신을 지원하게끔 하면 될 것 같다. 마침 기존까진 실행파일로만 제공되었던 Scratch Link가 이젠 소스까지 제공되고 있다. 개발 지원 운영체제는 macOS와 윈도우이다.

소스는 https://github.com/llk/scratch-link에서 받을 수 있다. 개발PC에 일단 클론해 보고,

$ git clone https://github.com/LLK/scratch-link.git

README.md 파일에 나온 것과 같은 과정을 통해 빌드해보자. 보안 WebSocket를 사용해야 하므로 인증서가 필요하다. 나중에 판매하거나 양상 목적이라면 돈을 주고 구입해야 하지만, 개인이 사용할 경우엔 PC에서 쉽게 생성 가능하다.

$ cd scratch-link
$ cd Certificates
$ openssl req -x509 -out scratch-device-manager.cer -keyout scratch-device-manager.key \
  -newkey rsa:2048 -nodes -sha256 \
  -subj '/CN=scratch-device-manager' -extensions EXT -config <( \
   printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

$ ./convert-certificates.sh

위 과정이 완료되면 out 디렉토리에  scratch-device-manager.pem, scratch-device-manager.pfx 파일이 생성된다.

현재 내가 사용하고 있는 개발환경은 macOS 이므로, macOS 폴더로 이동하여 빌드를 시작한다. Xcode, pngcrush를 미리 설치되어 있어야 한다. Xcode는 앱스토어에서, pngcrush는 homebrew를 이용하면 쉽게 설치할 수 있다.

$ cd ..
$ cd macOS
$ make

빌드에 필요한 패키지들이 자동으로 설치되고, 에러가 없이 완료되면 dist 디렉토리가 생성되고 Scratch Link 앱이 생성되어 있음을 볼 수 있다.

이제 이 앱을 실행해보면, 기존과 같이 메뉴바에 Scratch Link가 위치한다.

Scratch 3.0를 실행하고, BLE와 BT 등 기존과 동일하게 동작하는지 확인한다. 지난번 개발환경 구축 포스팅에서 device-manager.scratch.mit.edu를 hosts 파일을 이용해 127.0.0.1로 강제 변경하였는데, 이제 Scratch Link를 직접 사용할 수 있으므로 localhost로 변경하여 사용해도 무방하다.

따라서, hosts 파일을 변경하는 작업을 하지 않고, scratch-vm 내에서 다음의 파일에 있는 웹소켓 주소를 localhost로 변경한다.

> scratch-vm/src/io/ble.js

const ScratchLinkWebSocket = 'wss://localhost:20110/scratch/ble';
> scratch-vm/src/io/bt.js

const ScratchLinkWebSocket = 'wss://localhost:20110/scratch/bt';

(추가) 최근 변경된 버전에선 ./util/scratch-link-websocket.js 에서 저 링크들을 관리하는 듯 하다. 따라서 해당 파일을 수정해주면 됨.

> scratch-vm/src/util/scratch-link-websocket.js
28: this._ws = new WebSocket('wss://device-manager.scratch.mit.edu:20110/scratch/ble');
31: this._ws = new WebSocket('wss://device-manager.scratch.mit.edu:20110/scratch/bt'); 

이제 Scratch 3.0을 실행하고 확장카드를 실행해보면,

사파리의 경우, 위와 같이 Scratch Link와의 연결이 되지 않는다. 새로운 사파리 창을 띄우고 https://localhost:20110 로 접속한다. 다음과 같이 나오는데,

Show Details 버튼을 누르고, 아래쪽에 visit this website 링크를 누르고 다시 한번 접속하겠다고 하면, 어드민 암호를 입력하고 현재의 인증서가 사파리에 저장된다.

이제 Scratch 3.0에서 정상적으로 실행됨을 볼 수 있다.

다음엔 Scratch Link의 소스를 분석하고 시리얼 통신이 가능하도록 모듈을 작성해보도록 한다.


참고링크

잔치국수

아들 녀석이 국수를 좋아하여 종종 끓여주고 있음.

육수

  1. 물 650ml 정도에 국간장, 양조간장을 같은 비율로 섞어주고 끓인다. 이때 간을 봤을 따, 살짝 간장맛이 들 정도로.
  2. 당근, 호박, 양파를 채 썰고 끓는 물이 넣어준다.
  3. 야채가 익으면, 계란 한개를 풀어 불을 끈 상태에서 넣어준다.
  4. 다시 끓이면서, 소금으로 간을 맞춘다. 후추도 첨가.

국수

  1. 면의 종류가 많긴한데, 먹기 부드러운 세면으로 선택
  2. 1인분 양을 잡아 끓이고, 다 익었다 싶으면 찬물에 행궈서 그릇에 이쁘게 담아준다.

완성

  1. 국수를 담은 그릇에 국물만 적당량을 살살 부어준다.
  2. 야채 및 계란 등 삶은 건더기를 국수 위에 살포시 얹어준다.

간장을 많이 넣으면 국물이 짙은 색으로 탁해지니 약간만 넣고, 최종 간은 소금으로. 양념장을 만들어 넣어 먹어도 되지만 이대로 먹어도 큰 무리수는 없음.

취향에 따라 김치 등을 얇게 썰어 같이 먹어도 별미.

Anki Vector 언박싱

얼마전 Anki社가 문을 닫았다. Cozi, Vector 등 귀엽고 기능 많은 로봇들을 개발하였고, 언뜻 보기엔 많이 팔리기도 하였는데… 왜 그랬을까… 뭐 암튼! 항상 구매해보고 싶단 생각은 갖고 있었는데, 문을 닫았다니 더 구매해보고 싶어서 뉴스가 뜬 날 바로 주문했다. 가격은 대충 30만원 정도? 해외주문이라 시간이 좀 걸리는 듯 하더니 드디어 도착.

이 로봇은 저 귀여운 눈이 포인트! 로봇 전체 색상은 아주 짙은 회색 (검은색이라고 해야 되나?)이 기본이고, 군데군데 금색 포인트, LED 등이 배치되어 있다.

상자를 열어보면 알차게 로봇과 큐브가 넣어져 있다. 역시 제품은 꺼내자마자 켜고 동작되어야 제맛. 이것저것 많이 설정하거나 하면 사용자는 힘들어진다. 전원키고 스맛폰 연결해서 계정 연동하면 끝.

한글이 지원되지는 않고 영어로 명령을 내리거나 의사소통해야 된다. Wakeup 단어는 “Hey! Vector.”이다. 로봇이 뭔가 처리를 하는 동안엔 알아듣지 못하며, 중간중간 idle 상태에선 칼 같이 알아듣는다.

큐브를 이용해 이것저것 명령을 내릴수도 있고, 그냥 냅두면 알아서 갖고 논다. 몇시간 정도 갖고 놀아본 결과, 이 제품의 컨셉은 애완동물인듯 하다. 가만히 냅둬도 이곳저곳 돌아다니고, 충전을 위한 도킹 스테이션을 자기의 집으로 생각하는…

거리센서와 카메라를 이용해 어느 정도 SLAM 기능을 수행하는 듯하다. “Go to charging station!”이라고 명령을 내리면 스스로 주변에 있는 도킹 스테이션을 찾아 충전을 시작한다.

로봇을 제어하기 위한 API도 상당히 충실하게 지원한다. python3로 프로그래밍 가능하니 쉬울듯. 잠깐 생각해본 바로는 ROS 연동도 쉽게 될것 같다. 실제로 여러가지 프로젝트들이 Vector를 이용해서 진행되고 있는 듯.

참고링크

Jetson Nano에서 Intel RealSense D435 카메라 사용해보기

Jetson Nano에는 USB 3.1 gen2 포트가 4개 존재한다. 문득 집에서 놀고 있는 인텔 리얼센스 카메라가 있어 연결해보기 위해 작업 시작.

Intel에서 Intel® RealSense™ SDK의 arm64용 빌드된 패키지를 제공해주지 않으므로, Jetson Nano에서 사용하기 위해선 소스를 직접 빌드하여야 한다. 먼저 소스 빌드를 위해 필요한 패키지들을 설치한다.

$ sudo apt install libgtk-3-dev libxcursor-dev libxinerama-dev

다음으로 레포지토리 (https://github.com/IntelRealSense/librealsense)에서 최신 릴리즈된 소스를 받아온다. (https://github.com/IntelRealSense/librealsense/releases)

받아온 소스의 압축을 풀고,

$ tar zxf librealsense-2.21.0.tar.gz
$ cd librealsense.2.21.0
$ mkdir build
$ cd build 
$ cmake ..

정상적으로 종료되면, 빌드를 시작한다. 이때 메모리 부족이 일어나므로 이번 포스트와 마찬가지로 swap 파티션을 활성화 한다.

$ sudo swapon /swapfile

빌드 시작

$ make -j1

정상적으로 빌드가 완료되면, 이제 설치.

$ sudo make install

이제 리얼센스 카메라를 연결하고, 테스트용 프로그램인 realsense-viewer를 실행해본다.

$ realsense-viewer

카메라가 정상적으로 인식되고 USB 3.1 gen2로 연결되어 있음을 볼수 있다. 몇가지 테스트 해본 결과 Jetson Nano에서는 1280×720의 해상도를 처리하기엔 너무 느리다. 따라서 해상도를 640×480으로 변경하여 사용한다.

이제 카메라를 켜보면~,

Depth 이미지와 RGB 이미지가 정상적으로 잘 보인다. 코어의 성능때문인지 30프레임이 다 나오는 것 같진 않다.


주의사항! 현재 Jetson Nano에는 5V, 2.1A 어뎁터를 사용하여 연결하였는데, 위와 같이 코어의 성능을 뽑아쓰는 어플리케이션을 돌리다보면 갑자기 전원이 나가는 경우가 가끔씩 발생하였다. 좀더 높은 전류를 제공하는 어댑터를 사용하기를 권장함.

Blockly 툴박스에 카테고리 추가

Blockly를 실행하면 좌측 (혹은 설정에 의해서 하단)에 툴박스가 존재한다. 사용자의 필요에 따라 이 툴박스에 카테고리를 추가하는 등에 대한 작업을 할 수 있다.

툴박스에 설정은 toolbox_standard.js 파일에서 진행한다. 지난 포스팅에서 실행했던 디렉토리에서 toolbox_standard.js 파일을 열어보면…

var BLOCKLY_TOOLBOX_XML = BLOCKLY_TOOLBOX_XML || Object.create(null);

/* BEGINNING BLOCKLY_TOOLBOX_XML ASSIGNMENT. DO NOT EDIT. USE BLOCKLY DEVTOOLS. */
BLOCKLY_TOOLBOX_XML['standard'] =
// From XML string/file, replace ^\s?(\s*)?(<.*>)$ with \+$1'$2'
// Tweak first and last line.
'<xml>'
+ '<category name="Logic" colour="%{BKY_LOGIC_HUE}">'
+   '<block type="controls_if"></block>'
+   '<block type="logic_compare"></block>'
+   '<block type="logic_operation"></block>'
+   '<block type="logic_negate"></block>'
+   '<block type="logic_boolean"></block>'
+   '<block type="logic_null" disabled="true"></block>'
+   '<block type="logic_ternary"></block>'
+ '</category>'
+ '<category name="Loops" colour="%{BKY_LOOPS_HUE}">'
+   '<block type="controls_repeat_ext">'
+     '<value name="TIMES">'
+       '<shadow type="math_number">'
+         '<field name="NUM">10</field>'

...

와 같이 되어 있다. Javascript로 되어 있으며, 자세히 살펴보면 XML을 String 형태로 변환해서 사용함을 볼 수 있다. 주석에 나와 있는 것처럼, Blockly Dev Tools를 이용해서 만들어 줄 수 있지만, 간단한 작업은 본 파일을 수정하면 된다.

Custom이라는 카테고리를 추가해보도록 한다. 블럭은 아직 추가하지 않기로 한다. 카테코리를 추가하고 싶은 지점에 다음과 같이 추가한다.

+ '<category name="Custom" colour="100">'
+ '</category>'

이제 다시 웹페이지를 reload해서 보게 되면,

Custom 카테고리가 추가되어 있는 것을 볼 수 있다. 아직 블럭들이 추가되어 있지 않으므로, 클릭해도 블럭들은 보이지 않는다. 카테고리 사이에 구분자(Seperator)를 추가하려면 원하는 지점에 다음과 같이 추가한다.

여기까지.

Jetson Nano에서 OpenCV 4.1 with CUDA 빌드

영상처리에 많이 사용되는 OpenCV를 Jetson Nano에서도 사용 가능하다. 빌드 과정은 PC에서와 동일하나 플랫폼의 특성 상 몇가지 다른 부분이 있다. 기본으로 설치되어 있는 패키지를 사용해도 되지만, CUDA를 활용하기 위해선 빌드 과정을 통해 설치하여야 한다.

L4T에는 cuda10.0이 이미 설치되어 있다.

OpenCV github 레포지토리에서 소스를 다운로드한다.

현재 릴리즈된 최신 버전은 4.1.0이다.

먼저 cmake를 설치한다.

$ sudo apt install cmake

다운로드한 압축파일 (opencv-4.1.0.tar.gz, opencv_contrib-4.1.0.tar.gz) 을 풀고, 다음과 같이 cmake를 이용해 빌드 파일을 생성한다.

$ cd opencv-4.1.0
$ mkdir build
$ cd build
$ cmake -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.1.0/modules -DWITH_CUDA=ON -DCUDA_FAST_MATH=1 -DBUILD_EXAMPLES=ON  -DBUILD_opencv_python3=ON -DPYTHON3_INCLUDE_DIR2=/usr/include/python3.6m -DPYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include -DCUDA_ARCH_BIN="5.3" -DCUDA_ARCH_PTX=""  -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF ..

각종 의존 패키지들을 체크하고 정상적으로 종료되면 다음과 같은 결과를 보여준다.

-- General configuration for OpenCV 4.1.0 =====================================
--   Version control:               unknown
-- 
--   Extra modules:
--     Location (extra):            /home/byeongkyu/Downloads/opencv_contrib-4.1.0/modules
--     Version control (extra):     unknown
-- 
--   Platform:
--     Timestamp:                   2019-05-02T04:43:14Z
--     Host:                        Linux 4.9.140-tegra aarch64
--     CMake:                       3.10.2
--     CMake generator:             Unix Makefiles
--     CMake build tool:            /usr/bin/make
--     Configuration:               Release
-- 
--   CPU/HW features:
--     Baseline:                    NEON FP16
--       required:                  NEON
--       disabled:                  VFPV3
-- 
--   C/C++:
--     Built as dynamic libs?:      YES
--     C++ Compiler:                /usr/bin/c++  (ver 7.4.0)
--     C++ flags (Release):         -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -Wimplicit-fallthrough=3 -Wno-strict-overflow -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG  -DNDEBUG
--     C++ flags (Debug):           -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-delete-non-virtual-dtor -Wno-comment -Wimplicit-fallthrough=3 -Wno-strict-overflow -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -fvisibility-inlines-hidden -g  -O0 -DDEBUG -D_DEBUG
--     C Compiler:                  /usr/bin/cc
--     C flags (Release):           -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -Wimplicit-fallthrough=3 -Wno-strict-overflow -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -O3 -DNDEBUG  -DNDEBUG
--     C flags (Debug):             -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wundef -Winit-self -Wpointer-arith -Wshadow -Wuninitialized -Winit-self -Wno-comment -Wimplicit-fallthrough=3 -Wno-strict-overflow -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -fdata-sections    -fvisibility=hidden -g  -O0 -DDEBUG -D_DEBUG
--     Linker flags (Release):      -Wl,--gc-sections  
--     Linker flags (Debug):        -Wl,--gc-sections  
--     ccache:                      NO
--     Precompiled headers:         YES
--     Extra dependencies:          m pthread cudart_static dl rt nppc nppial nppicc nppicom nppidei nppif nppig nppim nppist nppisu nppitc npps cublas cufft -L/usr/local/cuda/lib64 -L/usr/lib/aarch64-linux-gnu
--     3rdparty dependencies:
-- 
--   OpenCV modules:
--     To be built:                 aruco bgsegm bioinspired calib3d ccalib core cudaarithm cudabgsegm cudacodec cudafeatures2d cudafilters cudaimgproc cudalegacy cudaobjdetect cudaoptflow cudastereo cudawarping cudev datasets dnn dnn_objdetect dpm face features2d flann fuzzy gapi hfs highgui img_hash imgcodecs imgproc line_descriptor ml objdetect optflow phase_unwrapping photo plot python2 quality reg rgbd saliency shape stereo stitching structured_light superres surface_matching text tracking ts video videoio videostab xfeatures2d ximgproc xobjdetect xphoto
--     Disabled:                    world
--     Disabled by dependency:      -
--     Unavailable:                 cnn_3dobj cvv freetype hdf java js matlab ovis python3 sfm viz
--     Applications:                tests perf_tests examples apps
--     Documentation:               NO
--     Non-free algorithms:         NO
-- 
--   GUI: 
--     GTK+:                        NO
--     VTK support:                 NO
-- 
--   Media I/O: 
--     ZLib:                        /usr/lib/aarch64-linux-gnu/libz.so (ver 1.2.11)
--     JPEG:                        libjpeg-turbo (ver 2.0.2-62)
--     WEBP:                        build (ver encoder: 0x020e)
--     PNG:                         build (ver 1.6.36)
--     TIFF:                        build (ver 42 - 4.0.10)
--     JPEG 2000:                   build (ver 1.900.1)
--     OpenEXR:                     build (ver 1.7.1)
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES
-- 
--   Video I/O:
--     DC1394:                      NO
--     FFMPEG:                      NO
--       avcodec:                   NO
--       avformat:                  NO
--       avutil:                    NO
--       swscale:                   NO
--       avresample:                NO
--     GStreamer:                   YES (1.14.1)
--     v4l/v4l2:                    YES (linux/videodev2.h)
-- 
--   Parallel framework:            pthreads
-- 
--   Trace:                         YES (built-in)
-- 
--   Other third-party libraries:
--     Lapack:                      NO
--     Eigen:                       YES (ver 3.3.4)
--     Custom HAL:                  YES (carotene (ver 0.0.1))
--     Protobuf:                    build (3.5.1)
-- 
--   NVIDIA CUDA:                   YES (ver 10.0, CUFFT CUBLAS FAST_MATH)
--     NVIDIA GPU arch:             53
--     NVIDIA PTX archs:
-- 
--   OpenCL:                        YES (no extra features)
--     Include path:                /home/byeongkyu/Downloads/opencv-4.1.0/3rdparty/include/opencl/1.2
--     Link libraries:              Dynamic load
-- 
--   Python 2:
--     Interpreter:                 /usr/bin/python2.7 (ver 2.7.15)
--     Libraries:                   /usr/lib/aarch64-linux-gnu/libpython2.7.so (ver 2.7.15rc1)
--     numpy:                       /usr/lib/python2.7/dist-packages/numpy/core/include (ver 1.13.3)
--     install path:                lib/python2.7/dist-packages/cv2/python-2.7
-- 
--   Python 3:
--     Interpreter:                 /usr/bin/python3 (ver 3.6.7)
--     Libraries:                   /usr/lib/aarch64-linux-gnu/libpython3.6m.so (ver 3.6.7)
--     numpy:                       /usr/lib/python3/dist-packages/numpy/core/include (ver )
--     install path:                lib/python3.6/dist-packages/cv2/python-3.6
-- 
--   Python (for build):            /usr/bin/python3
--
--   Java:                          
--     ant:                         NO
--     JNI:                         NO
--     Java wrappers:               NO
--     Java tests:                  NO
-- 
--   Install to:                    /usr/local
-- -----------------------------------------------------------------
-- 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/byeongkyu/Downloads/opencv-4.1.0/build

빌드 중 램 부족으로 인한 에러가 발생하므로, swap 파티션을 생성하여 이를 보완하도록 한다.

$ sudo fallocate -l 4.0G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile

부팅시마다 마운트 하도록 다음의 파일 수정
$ sudo vi /etc/fstab

라인 추가
/swapfile none swap 0 0

자, 이제 빌드를 시작해보면…

$ make -j1

코어 4개를 적극 활용하면 좋겠으나, 램이 4기가 밖에 없는 관계로 램 부족과 같은 에러가 발생하거나 아예 멈춰버리는 불상사가 발생한다. 따라서 쓰레드 1개로 빌드 시작! 컴파일 시간이 어마어마하게 걸리고 방열판이 엄청나게 뜨거워지므로 조심.

빌드가 완료되면, 완료된 파일들을 설치한다. 설치 경로는 /usr/local 이다.

$ sudo make install

이제 제대로 설치되었는지 확인해본다.

$ opencv_version 
4.1.0

$ python3
>>> import cv2
>>> cv2
<module 'cv2' from '/usr/local/lib/python3.6/dist-packages/cv2/python-3.6/cv2.cpython-36m-aarch64-linux-gnu.so'>
>>> cv2.__version__
'4.1.0'

일단 설치는 여기까지!

Jetson Nano 무선랜(wifi + bluetooth) 카드 장착

Jetson Nano엔 무선랜 기능이 포함되어 있지 않다. 라즈베리파이도 3b+ 모델에서는 무선랜 기능이 포함되어 있는데, 뭐 그냥 붙여줬으면 좋으련만… USB 동글 등을 이용해서 사용할수 있겠지만, Jetson Nano에는 pci-e 확장포트가 존재한다. 따라서 이 포트에 m.2 규격의 무선랜 모듈을 장착하여 사용할 수 있다.

주변에 쉽게 구할 수 있고, 나름 리눅스 친화적인 무선랜 모듈은 인텔 제품이며 여러가지 버전이 존재한다. 현재 Jetson Nano의 커널 버전은 4.9 (L4T) 버전이므로, 현재 구할 수 있는 무선랜 모듈 중 가장 나은 선택은 Intel ac8265이다. Intel ac9560이 좀더 최신 칩셋에 나은 기능을 갖고 있지만, 드라이버가 커널 4.14 이상에서만 동작하므로 현재로선 사용이 불가능하다.

약 3만원 정도에 구입이 가능하다. 단 구입할때 안테나도 같이 구매하여야 한다. Intel ac8265의 주요 사양은 https://ark.intel.com/content/www/us/en/ark/products/94150/intel-dual-band-wireless-ac-8265.html 에서 확인 가능하다.

Jetson Nano의 옆 나사 두개를 풀어주고 모듈을 제거하면, 확장기판 내에 m.2 슬롯이 존재함을 볼수 있다. 나사를 풀러주고 구입한 무선랜 모듈을 장착한다.

안테나도 연결해준다. 저런 패치형 안테나 말고도, 일반적인 스틱형 안테나도 장착할 수 있다. (NGFF antenna 또는 M.2 wireless antenna로 검색하면 됨)

다시 코어 모듈을 조립하고 전원을 인가한다. 부팅이 완료된 후 터미널을 열어 다음과 같이 입력하여 무선랜 카드가 잘 인식되었는지 확인한다.

$ lspci
00:01.0 PCI bridge: NVIDIA Corporation Device 0fae (rev a1)
00:02.0 PCI bridge: NVIDIA Corporation Device 0faf (rev a1)
01:00.0 Network controller: Intel Corporation Wireless 8265 / 8275 (rev 50)
02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (rev 15)

이제 무선랜 및 블루투스 기능을 정상적으로 사용 가능하다.

끝!

안드로이드 앱 여러 개 권한 요청

앱을 설치하고 처음 실행시 여러 개의 권한 요청 방법. requestPermissions 함수를 사용하는데, 두번째 인자인 퍼미션 목록은 String Array 타입이다. 즉 여러 개의 권한을 한꺼번에 요청 가능.

필요한 권한 목록을 ArrayList<String>에 저장하고, 목록이 완성되면 requestPermissions을 이용해 권한 요청

ArrayList<String> permissions = new ArrayList<String>();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            permissions.add(Manifest.permission.RECORD_AUDIO);

...

if(permissions.size() > 0) {
            String[] reqPermissionArray = new String[permissions.size()];
            reqPermissionArray = permissions.toArray(reqPermissionArray);
            ActivityCompat.requestPermissions(this, reqPermissionArray, MY_PERMISSIONS_REQUEST_MULTI);
        }

사용자가 권한을 승인하거나 거절한 경우에 대한 대응은 onRequestPermissionsResult 함수에서 처리.

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

if(grantResults.length > 0) {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
    ...
    }

    ...
}

끗.

3D 프린트 이것저것 팁 & 의견

필라멘트

  • 일반 가정 집에서 ABS은 절대 사용 불가이다. 프린팅하는 동안 냄새도 고약하고, 건강엔 엄청 안좋다. PLA가 가장 무난하고, 강도를 필요로하는 것이라면 PETG를 사용하는 것을 추천함. (요즘은 PLA+ 등도 나오는데 뭐 별다른건 없는듯)
  • 난 PLA를 사용하다가 현재는 PETG를 사용중인데, 차이점은 PETG가 좀더 질기다라고 느껴진다. 출력이 완료되고 굳은 후 PLA는 힘을 주면 (엄청 쎄게) 똑 부러지는 반면 PETG는 살짝 구부려지다가 부러진다. 그러니깐 좀더 질기다라는게 맞는듯.
  • 싸구려 필라멘트는 저렴해서 좋긴한데, 필라멘트의 굵기가 일정하지 않거나 감겨있는 게 잘못되어 꼬여버리면 출력 중 필라멘트가 끊기는 참사가 발생함.

출력 팁

  • 출력시 바닥면이 뜨는 변형이 일어난다면, 바닥 온도를 살짝 올려보자.
  • 첫번째 레이어의 출력속도를 현저히 낮춰보는 것도 해결 방법.
  • 출력 후 옆면이 고르지 않게 나온다면, X, Y축의 벨트 텐션을 좀더 강하게 조정한다.
  • 출력 중 출력물이 안착이 안된고 날라다닌다면, 베드의 레벨링이 잘못된 경우이다. 레벨을 세심하게 조정해본다.
  • 첫번째 레이어가 좀더 크기 출력되는 경우 (일명 코끼리발 Elephant’s Foot 현상), 이를 보정하기 위한 옵션이 슬라이싱 툴에 존재한다. 정확히는 첫번째 레이어의 크기를 일정 비율만큼 줄이는 것임.
  • 간격이 떨어져있는 출력물에 거미줄 같이 지저분한 것이 많이 생긴다면, 출력 온도를 좀 낮춘다.

재미난 출력물들 찾기

텐서플로와 머신러닝으로 시작하는 자연어 처리

머신러닝, 자연어처리, 텐서플로…

TensorFlow는 그냥 툴이고 배우는데야 큰 문제는 없지만, 머신러닝을 이해하지 않고 TensorFlow를 배우는건 그냥 겉핥기만 될뿐인듯 하다. 문제는 그것을 배우고 활용하기 위한 전처리 작업들이 만만치 않다는 점. 초보자들이 양질의 데이터를 구하기란 매우 힘든 일인듯.

이 책은 그냥 자연어 처리에 이러한 방법도 있다라는 것을 보기 위해 구입한 책이다. 완벽하게 분석해서 내 것으로 만들면 좋겠지만, 내가 아직은 많이 필요한 내용이 아니므로 그냥 트렌드 파악용?

http://kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9791158391379&orderClick=JAj

파이썬으로 만드는 OpenCV 프로젝트

OpenCV는 영상처리 부분에선 많이 (그냥 99% 정도?) 사용되는 소프트웨어 프레임워크이다. 사실 기술서적은 버전이 변화함에 따라 쓸모없어지는 경우가 많아 구입이 좀 꺼려지긴하는데, 기본 기능들 (버전이 변하더라도 사용법은 거의 변하지 않는)에 대해 잘 정리되어 있는 책인듯 하여 구입하였다.

기본 기능부터, 약간의 고급 기능까지 함수들의 사용법에 대해 잘 정리되어 있고, Python으로 예제 코드가 되어 있고 결과물에 대해 스샷까지 잘 되어 있어 책상이 꼽아두었다가 필요할때 한번씩 꺼내볼만하다.

http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9788966262410&orderClick=LEB&Kc=

제육볶음

재료: 돼지고기(앞다리살, 제육볶음용 600g), 양파 1개, 당근 반개 or 한개, 고추장, 고추가루, 간장, 마늘 간것, 올리고당, 설탕, 굴소스

  • 양파, 당근은 먹기 좋게 손질해 놓는다.
  • 고추장 2큰술, 고추가루 1큰술, 간장 2큰술, 마늘 간것 1큰술, 올리고당 한큰술, 굴소스 한큰술을 넣고 섞어 양념장을 만든다.
  • 돼지고기는 제육볶음용이면 얇게 썰어져 있고, 이를 다시 먹기 좋은 크기로 자른다.
  • 프라이팬에 돼지고기를 볶다가 색깔이 거의다 변했을 쯤, 설탕 2큰술을 넣고 잘 섞어준다.
  • 야채 손질해 놓은 것과 양념장을 넣고 잘 볶아준다.
  • 재료가 완전히 익었을 때 쯤, 깨소금을 뿌려서 마무리.
  • 상추 등 쌈채소에 싸서 맛있게 냠냠.

생각보다 엄청 간단하게 만들수 있고, 양도 대단히 푸짐하다. 돼지고기 한근 정도면, 2명이 충분히 먹을수 있을 만한 양인듯. 소스도 맛있어서 밥을 비벼 먹어도 괜찮음.

upip를 이용하여 MicroPython 라이브러리 설치하기

Python에서 pip를 이용해 라이브러리를 설치했던 것처럼, MicroPython에서도 upip를 이용하면 MicroPython으로 포팅된 Python 코어 라이브러리를 사용할 수 있다. (심지어 보드 레벨에서!!)

먼저, WiFi가 연결되어 있어야 한다.

>>> import network
>>> sta_if = network.WLAN(network.STA_IF)
>>> sta_if.active(True)
>>> sta_if.connect("<your-ap-name>", "<your-ap-password>")

연결이 되고 IP가 할당되었는지 확인 후,

>>> import upip
>>> upip.install("<설치할 패키지명>")

위와 같이 실행하면 보드에 직접 Python 라이브러리를 받아온다. 예를 들면,

>>> upip.install("micropython-uasyncio")
Installing to: /lib/
Warning: pypi.org SSL certificate is not validated
Installing micropython-uasyncio 2.3 from https://files.pythonhosted.org/packages/2f/d0/da285cf389f3736b204514f936b9621976735096ba02f6cd701ef0426a05/micropython-uasyncio-2.3.tar.gz
Installing micropython-uasyncio.core 2.3 from https://files.pythonhosted.org/packages/2b/3a/5737ff41dfe85d3ddf4c783e9289f6c869526fd31b495bf012612e6c82cf/micropython-uasyncio.core-2.3.tar.gz

>>> import uasyncio

MicroPython으로 포팅된 Python 코어 라이브러리는 https://github.com/micropython/micropython-lib/에서 확인 가능하다.

닭곰탕

가끔씩 해먹는 닭곰탕. 맛도 좋고, 만드는 방법도 간단해서 맛의 편차가 거의 없는 것이 장점이다.

  1. 준비물: 생닭 (9호 or 11호), 대파, 양파 1개, 통마늘
  2. 생닭은 마트에서 구입할 때 곰탕용이라고 말하면, 그에 맞게끔 잘 손질해 줌. 주로 통으로 가져오거나 반으로 쪼개서 가져오면 됨.
  3. 큰 냄비에 생닭, 대파, 양파, 통마늘을 넣고 끓인다.
  4. 40~50분 정도면 되지만, 좀더 부드럽게 먹으려면 1시간 이상 푸욱 끓인다.
  5. 닭이 익었는지 확인하고, 닭을 꺼낸다. 나머지 야채들은 건져서 버린다.
  6. 꺼낸 닭에서 살을 발라내고, 잘게 찢는다.
  7. 발라낸 닭뼈를 다시 냄비에 집어넣고 또 끓인다.
  8. 국물이 진하게 우려날 때쯤, 발라낸 살을 넣고 끓인다.
  9. 따뜻한 밥 한공기에 국물을 충분히 넣고, 채썬 파를 넣고 먹는다.

끝!

MicroPython REPL 이것저것 on ESP32

  • REPL 접속(?)

micropython은 자체적으로 REPL을 제공한다. UART0를 통해 PC와 연결되며, 이를 사용하기 위해선 시리얼통신 프로그램(picom, putty 등)을 사용하거나, 터미널에서 screen 명령 등을 이용해서 이용 가능하다.

$ screen /dev/tty.SLAB_USBtoUART 115200

접속 종료는 ctrl+a, ctrl+\를 순서대로 누른다.

  • REPL에서 자동완성 기능 사용

import 후, 혹은 내장 펑션을 사용하는 경우에 tab 키를 이용한 자동완성 기능을 제공한다. 예를 들어 machine을 import 하고 machine.까지 입력 후 tab 키를 눌러보면,

>>> import machine
>>> 
>>> machine.
__class__       __name__        ADC             DAC
DEEPSLEEP       DEEPSLEEP_RESET                 EXT0_WAKE
EXT1_WAKE       HARD_RESET      I2C             PIN_WAKE
PWM             PWRON_RESET     Pin             RTC
SLEEP           SOFT_RESET      SPI             Signal
TIMER_WAKE      TOUCHPAD_WAKE   Timer           TouchPad
UART            ULP_WAKE        WDT             WDT_RESET
deepsleep       disable_irq     enable_irq      freq
idle            lightsleep      mem16           mem32
mem8            reset           reset_cause     sleep
time_pulse_us   unique_id       wake_reason
>>> machine.

와 같이 사용가능한 변수 명 함수명을 보여주고, 자동완성도 가능함.

  • 긴 코드 붙여넣기

REPL에서 예제 코드나 좀 긴 코드를 테스트해보고자 할 경우, 복사 붙이기 모드가 지원된다. 한줄씩 입력해야 하는 번거로움을 좀 덜 수 있다. >>> 에서 Ctrl+E키를 누르면,

>>> 
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== 

와 같이 보이고, 여기에 긴 코드를 입력하던지, 아니면 다른 곳에서 코드를 복사해서 붙인다. 입력이 완료되면, Ctrl+D를 눌러 모드를 종료하면 입력된 코드를 순차적으로 실행한다. 취소하려면 Ctrl+C.

  • PC에서 직접 실행하기

REPL모드가 아니고, PC에서 코드를 완성하고 보드에 옮기지 않고 바로 실행이 가능하다. ampy를 이용하면 되는데,

$ ampy -p /dev/tty.SLAB_USBtoUART run <your_code>.py

와 같이 실행하면 바로 실행 가능.

WordPress에서 사용빈도를 반영한 태그 위젯 만들어 보기

참고링크: https://www.wpbeginner.com/plugins/how-to-display-most-popular-tags-in-wordpress

기본으로 제공되는 Tags 위젯은 사용된 Tag를 순서대로 모두다 보여줘서 좀 번거로운 면이 있다. 사용 빈도를 반영하여 많이 사용된 Tag를 크게 보여주고, 또 원하는 순위까지만 보여주는 것이 좀 괜찮을 것 같은데, 이를 위해선 해당 기능을 포함하는 플러그인을 설치해서 사용하거나, 간단히 functions.php를 수정하면 된다.

사용하는 테마의 디렉토리로 가서, functions.php 파일을 열고, 다음의 구문을 추가한다.

function wpb_tag_cloud() {
$tags = get_tags();
$args = array(
    'smallest'                  => 12,
    'largest'                   => 24,
    'unit'                      => 'px',
    'number'                    => 15,
    'format'                    => 'flat',
    'separator'                 => " ",
    'orderby'                   => 'count',
    'order'                     => 'DESC',
    'show_count'                => 1,
    'echo'                      => false
);

$tag_string = wp_generate_tag_cloud( $tags, $args );

return $tag_string;

}
// Add a shortcode so that we can use it in widgets, posts, and pages
add_shortcode('wpb_popular_tags', 'wpb_tag_cloud');

// Enable shortcode execution in text widget
add_filter ('widget_text', 'do_shortcode');

여기에서 설정할 옵션은

  • smallest: 가장 낮은 순위의 Tag 폰트 크기
  • largest: 가장 높은 순위의 Tag 폰트 크기
  • number: 15 순위까지 보여주기
  • seperator: Tag들 보여줄때 구분자

이와 같이 설정하고, Widget 설정에서 Text 위젯을 추가하고, 내용에 wpb_popular_tags을 입력한다. 결과를 보면,

와 같이 깔끔하게 보인다.

바나나 거치대 (Banana Stand)

바나나를 나무에 걸려 있는 모양으로 두면 보관을 오래할 수 있다고 한다. 옷걸이를 이용해서 만들거나, 파는 제품을 살 수도 있지만, 3D 프린터로 한번 만들어 본다.

CAD를 이용해서 모양을 만들고 (구조는 대단히 단순함)…

3D 프린터로 뽑아주면 (채움 20%, PETG 필라멘트 사용)…

이제 바나나를 걸어주면~~~~!!!

안정적으로 잘 매달려 있게 된다. 좀더 큰 것을 걸어두면 어떨런지는 모르겠는데, 일단은 만족!

Setup MicroPython on ESP32 (for macOS)

CP210x 드라이버 설치 (https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers)

esptool 설치 (pip3가 없는 경우, homebrew를 이용해 설치한다)

$ pip3 install esptool

보드를 연결하고, 기존 flash 모두 초기화

$ esptool.py --chip esp32 -p /dev/tty.SLAB_USBtoUART erase_flash

esptool.py v2.6
Serial port /dev/tty.SLAB_USBtoUART
Connecting........__
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: 84:0d:8e:0c:c9:f0
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 2.9s
Hard resetting via RTS pin...

펌웨어 다운로드 (https://micropython.org/download/#esp32)

펌웨어를 보드에 Flash 한다.

$ esptool.py --chip esp32 -p /dev/tty.SLAB_USBtoUART write_flash -z 0x1000 esp32-bluetooth.bin

esptool.py v2.6
Serial port /dev/tty.SLAB_USBtoUART
Connecting.....
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
MAC: 84:0d:8e:0c:c9:f0
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1549888 bytes to 970594...
Wrote 1549888 bytes (970594 compressed) at 0x00001000 in 85.5 seconds (effective 145.0 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

보드의 전원을 껐다가 다시 인가하고, 터미널에서 다음과 같이 입력하여 REPL 환경으로 진입.

$ screen /dev/tty.SLAB_USBtoUART 115200

처음엔 아무런 내용이 나타나지 않지만, 명령어를 입력하면 됨. 평소 사용한 REPL 환경와 동일.

>>> print("hello")
hello
>>> 

REPL 환경에서 나오려면, ctrl + a, ctrl + \ 키를 순서대로 누르고, 화면 밑 프롬프트가 나오면 y를 눌러 종료 가능.

Adafruit에서 제공하는 Micro Python Tool을 이용해 보드 내부의 파일 시스템에 파일을 넣거나 스크립트를 실행할 수 있음.

$ pip3 install adafruit-ampy

보드 내부 파일 시스템에 있는 파일들 표시

$ ampy -p /dev/tty.SLAB_USBtoUART ls

/boot.py

다음의 명령어를 이용해 파일들 조작 가능.

ampy
Usage: ampy [OPTIONS] COMMAND [ARGS]...

  ampy - Adafruit MicroPython Tool

  Ampy is a tool to control MicroPython boards over a serial connection.
  Using ampy you can manipulate files on the board's internal filesystem and
  even run scripts.

Options:
  -p, --port PORT    Name of serial port for connected board.  Can optionally
                     specify with AMPY_PORT environment variable.  [required]
  -b, --baud BAUD    Baud rate for the serial connection (default 115200).
                     Can optionally specify with AMPY_BAUD environment
                     variable.
  -d, --delay DELAY  Delay in seconds before entering RAW MODE (default 0).
                     Can optionally specify with AMPY_DELAY environment
                     variable.
  --version          Show the version and exit.
  --help             Show this message and exit.

Commands:
  get    Retrieve a file from the board.
  ls     List contents of a directory on the board.
  mkdir  Create a directory on the board.
  put    Put a file or folder and its contents on the board.
  reset  Perform soft reset/reboot of the board.
  rm     Remove a file from the board.
  rmdir  Forcefully remove a folder and all its children from the board.
  run    Run a script and print its output.

보드에 있는 LED로 GPIO 테스트. 13번 핀에 연결되어 있음.

>>> import machine
>>> led = machine.Pin(13, machine.Pin.OUT)
>>> led.value(1)
>>> led.value(0)

일단은 여기까지.

참고링크: https://github.com/pvanallen/esp32-getstarted

짜장밥

준비물: 돼지고기, 양파 2개, 감자 반개, 호박 반개, 당근 반개, 짜장가루 (2인분)

  1. 양파를 채 썰고, 후라이펜에 기름을 두르고 볶는다.
  2. 양파가 갈색이 되면 돼지고기를 넣고 또 볶는다.
  3. 돼지고기에 핏기가 사라지면, 감자, 양파, 호박, 당근을 넣고 또 볶는다.
  4. 3번에 넣은 야채가 좀 익었다 싶으면, 물 700ml를 넣고 끓인다.
  5. 야채가 거의다 익었으면, 짜장가루를 넣고 불을 줄인 후 살살 섞어준다.

끝!.

전반적인 과정은 카레와 완전 비슷. 다만 야채의 모양을 좀 다르게 해주는게 나은 듯. 짜장밥에 들어가는 야채는 깍뚝썰기로.

카레

준비물: 돼지고기 or 소고기, 양파 2개, 당근 반개, 감자 반개, 카레 고형분 2인분, 버터 쪼금

  1. 양파, 당근, 감자는 얇게 채 썰기해서 준비. 당근, 감자는 채칼을 이용하면 쉽게 가능.
  2. 후라이팬에 버터 (없으면 올리브유)를 넣고, 양파를 다 넣고 살짝 갈색이 될때까지 볶는다.
  3. 고기를 넣고 볶는다.
  4. 고기가 핏기가 사라지면, 당근, 감자를 넣고 볶는다.
  5. 야채가 숨이 죽고 좀 익었다 싶으면 (이때쯤 양파는 좀더 진한 갈색으로 변함), 물을 700ml 정도 넣고 끓인다.
  6. 물이 끓기 시작하면, 카레를 넣고 저어준다.
  7. 야채가 완전히 익을때까지 저어준다.

끝!.

양파가 볶으면 볶을수록 단맛이 올라와, 아이들도 좋아하는 달달한 맛을 느낄수 있음. 거의 익어버려 양파의 아삭한 감이 사라지므로 먹기도 편함.

Jetson Nano GPIO 사용 해보기

Jetson Nano에는 라즈베리파이와 마찬가지로 40핀의 GPIO 헤더가 존재한다. 이를 이용해 다양한 외부기기와 연결 가능한데, python의 라이브러리를 이용하면 사용자가 제어 가능하다.

먼저 필요한 패키지를 설치한다. python3는 기본 설치되어 있고, pip3는 설치해야 한다.

$ sudo apt install python3-pip

다음으로 Jetson.GPIO 패키지를 설치한다.

$ sudo pip3 install Jetson.GPIO

이제 python3를 실행하고 설치한 패키지를 import 해보면 다음과 같이 퍼미션 에러가 발생한다. 몇가지 설정이 필요하다.

>>> import Jetson.GPIO
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/Jetson/GPIO/__init__.py", line 1, in <module>
    from .gpio import *
  File "/usr/local/lib/python3.6/dist-packages/Jetson/GPIO/gpio.py", line 33, in <module>
    raise RuntimeError("The current user does not have permissions set to "
RuntimeError: The current user does not have permissions set to access the library functionalites. Please configure permissions or use the root user to run this

gpio 그룹을 생성하고 사용자를 gpio 그룹에 추가한다.

$ sudo groupadd -f -r gpio
$ sudo usermod -a -G gpio <user_id>

다음으로 udev 룰을 rules.d 디렉토리로 복사한다.

$ sudo cp /opt/nvidia/jetson-gpio/etc/99-gpio.rules /etc/udev/rules.d/

재부팅하거나, 다음과 같이 입력하여 udev 룰을 다시 불러온다. (재부팅해야 함.)

$ sudo udevadm control --reload-rules && sudo udevadm trigger

이제 python3를 실행한 다음, 패키지를 import 해보면, 정상적으로 사용 가능하다. 몇가지 예제 코드들이 /opt/nvidia/jetson-gpio/samples에 있으므로 참고하면 될듯하다. 사용법은 RPi.GPIO와 동일하다.

>>> import Jetson.GPIO as GPIO
>>> GPIO.setmode(GPIO.BOARD)
>>> GPIO.setup(33, GPIO.OUT)
>>> GPIO.output(33, GPIO.HIGH)
>>> GPIO.output(33, GPIO.LOW)

위와 같이 간단히 사용 가능하고, 인터럽트, 콜백함수 등 다양한 기능을 지원한다. 몇가지 주의 사항으로는,

  • GPIO의 전압은 5V가 아닌 3.3V
  • PWM 기능은 지원하지 않음
  • I2C, SPI는 각각 두개씩 지원
  • 몇가지 특수펑션들 사용하고, 전원관련 핀을 빼면 여분의 GPIO는 별로 없는듯.

상세한 핀아웃은 https://www.jetsonhacks.com/nvidia-jetson-nano-j41-header-pinout/을 참고하면 됨.

Jetson Nano Developer Kit 사양 & 크기 정보

Technical Specifications

GPU128-core Maxwell
CPUQuad-core ARM A57 @ 1.43 GHz
Memory4 GB 64-bit LPDDR4 25.6 GB/s
StoragemicroSD (not included)
Video Encode4K @ 30 | 4x 1080p @ 30 | 9x 720p @ 30 (H.264/H.265)
Video Decode4K @ 60 | 2x 4K @ 30 | 8x 1080p @ 30 | 18x 720p @ 30 (H.264/H.265)
Camera1x MIPI CSI-2 DPHY lanes
ConnectivityGigabit Ethernet, M.2 Key E
DisplayHDMI 2.0 and eDP 1.4
USB4x USB 3.0, USB 2.0 Micro-B
OthersGPIO, I2C, I2S, SPI, UART
Mechanical100 mm x 80 mm x 29 mm

보드 자체의 크기는 실제 측정해보면 100mm x 79mm 이다. 나사 홀의 위치는 다음과 같다.

Blockly 실행 해보기

지난번 포스팅 (https://ahnbk.com/?p=469)대로 Blockly 소스를 받아 빌드하고, 그 결과물로 실행보도록 한다. 나중에 iOS나 Android에서도 어짜피 webview를 이용해서 실행해야 하므로, 과정은 동일하다.

먼저 작업할 디렉토리를 생성한다. 여기에선 편의상 blockly_demo란 디렉토리를 사용한다.

$ mkdir blockly_demo
$ cd blockly_demo

이제 이 디렉토리에 빌드된 파일을 복사한다.

javascript_compressed.js
blocks_compressed.js
blockly_compressed.js

다음으로 demos/mobile/html/toolbox_standard.js 툴박스 파일도 작업 디렉토리로 복사한다. 또 localization 관련한 디렉토리도 복사한다. 복사 위치는 msg 디렉토리 밑으로 한다. 본래 Blockly 디렉토리의 msg/js 디렉토리를 현재 개발 디렉토리로 복사한다. 완료되었으면 다음과 같이 보여야 한다.

이제 에디터를 이용해서 파일을 하나 생성한다. 파일 이름은 index.html 이다. 파일 내용은 다음과 같다.

<!DOCTYPE html>
<!-- HTML file to host Blockly in a mobile WebView. -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <link rel="stylesheet" href="styles.css">
  <script src="blockly_compressed.js"></script>
  <script src="blocks_compressed.js"></script>
  <!-- TODO: Select msg file based on locale. -->
  <script src="msg/js/en.js"></script>
  <script src="toolbox_standard.js"></script>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script type="text/javascript">
    var workspacePlayground = Blockly.inject('blocklyDiv', {
          media: 'media/',
          toolbox: BLOCKLY_TOOLBOX_XML['standard'],
          zoom: {controls: true}
        });
  </script>
</body>
</html>

또 style.css 파일로 생성하여 다음과 같이 입력한다.

html, body, #blocklyDiv {
    border: 0;
    height: 100%;
    margin: 0;
    padding: 0;
    width: 100%;
}

이제 크롬, 사파리, 인터넷 익스플로러 등 웹브라우저를 이용해 index.html을 열어본다.

Blockly가 잘 로딩되어 실행됨을 볼수 있다.

벚꽃

아… 올해도 제대로 된 벚꽃 구경은 못하나보다. 매년 가자가자 했는데, 결국엔 벚꽃 구경 소식은 뉴스로만 듣게 되는듯.

독산역 앞 벚꽃거리

아쉬우나마 길거리 걸어가다 찍은 벚꽃 거리 사진을 올린다. 업무 때문에 정처없이 걸어다가 문득 보인 철길 옆으로 주욱 늘어선 벚꽃 나무 사진.

Jetson Nano Developer Kit 개봉기

지난 달에 열린 GTC에서 NVIDIA가 JETSON NANO를 발표하였습니다. 라즈베리파이만한 크기에 가격도 저렴? ($99)하고, 게다가 CUDA 코어까지 내장되어 있어, 요즘 유행하는 머신러닝 알고리즘들을 테스트 해보거나, 가지고 놀기 좋게끔 만든 보드인듯 합니다.

한국은 공식판매처가 한컴MDS로 되어 있어, 맘편하게 하나 주문해봤는데, 오늘에서야 도착했습니다. 아마 다른 분들도 다 오늘쯤 받으시겠네요.^^

패키징은 벌것 없습니다. 딸랑 종이박스 하나이고, 내용물도 메뉴얼을 제외하면 보드 하나만 들어있습니다. 흔한 USB 케이블도 없네요. 녹색 커버를 열면 아래와 같이 보드가 담겨있는 봉투가 보이고,

그 밑에 간단한 보드 메뉴얼과, 보드 받침대? 역할을 하는 종이접기류가 보이네요.

보드는 정전기 방지 봉투가 잘 포장되어 있고, 뜯어보면 많이 보셨던 JETSON NANO가 들어있습니다. 뭐 크게 감흥이 있는 건 아니고, 나노 모듈에 확장보드 형태로 되어 있습니다.

뒷면은 부품들을 배치하지 않아, 거의 평면이고, 다만 단자들이 전부다 노출되어 있는 관계로 사용하실땐 받침이나 커버가 꼭 있어야겠네요. (위에서 본 종이접기처럼….)

한쪽면에 확장 커넥터들을 몰아놨습니다. 왼쪽부터 DC어댑터 단자(5V/4A max), HDMI, DP, USB3.0 x4, Ethernet, Micro USB B (전원공급용, 5V/2A) 이고요.

보드 오른쪽은 라즈베리파이와 유사하게 확장 포트들이 배치되어 있습니다. GPIO와 POE 등. 반대쪽엔 UART, POWER SW, RESET, 카메라를 연결할 수 있는 단자가 보이고요. 여기에 라즈베리파이에 사용했던 카메라를 꼽을수 있다네요.

나노모듈을 제거하면 밑에 PCIe 확장포트가 보입니다.

여기에 아래 제품과 같은 무선랜/블루트스 카드를 꼽으면 무선 연결이 가능해집니다. 다른 용도로도 사용이 가능할 것 같고요. 다만 안테나가 비포함되어 있으니 안테나도 같이 주문해야 합니다.

나노모듈 밑에는 MicroSD 카드를 꼽을수 있습니다. JETSON 웹페이지에서 이미지를 다운로드 받아 SD카드에 굽고, 끼워서 부팅하면 됩니다.

라즈베리파이와 크기를 비교하면 가로 길이는 비슷하지만, 세로 길이가 좀 깁니다. 하지만 라즈베리파이의 경우 확장커넥터들이 오른쪽에 밀려있는 관계로 실제 조립하거나 사용할때는 나노가 좀더 편리할 수도 있겠단 생각이 듭니다.

몇가지 아쉬운 점들이 보이는데, USB C 단자를 설계해 놓고 Micro USB B 단자를 꼽아놓은 점, USB포트에 커넥터들 임시땜빵, 오디오출력이 가능함에도 오디오출력 포트가 없다는 점 등이 있네요. RTC를 위한 배터리 단자도 자리만 남겨놓고 조립은 안했네요.

뭐.. 처음 제작된 제품이니 조만간 리비전되어 나올 듯 합니다. 전원을 넣고 부팅하면 Ubuntu 설치할 때와 마찬가지로 언어선택, 키보드, 계정 생성 등의 진행절차가 나오고 완료하면 Ubuntu로 부팅이 가능합니다. Ubuntu 버전은 18.04가 설치되어 있으며, 일단 처음 설치한 후의 느낌은 라즈베리파이보단 좀더 쾌적하게 움직인다 정도 입니다.

Prusament PETG Urban Grey 필라멘트 구입

Prusa i3 MK3를 구입하고 번들로 같이 왔던 Prusament PLA Galaxy Silver 필라멘트를 사용해왔더랬습니다. 짙은 회색에 PLA 재질이라 프린트 품질도 훌륭하고 표면도 아주 괜찮았습니다만… 그럭저럭 사용하다보니 한 롤을 다 써버렸습니다.

처음에 같이 주문했던 Prusament PLA Azure Blue, Prusament PLA Lipstick Red도 있긴 했지만 색상이 상당히 튀는 색이라 나중에 특별한 넘 뽑을때나 사용하기로 하고, 일반적으로 사용할 필라멘트를 추가 주문하였습니다.

이번에 주문한 제품은 Prusament PETG Urban Grey로 밝은 회색 빛의 PETG 재질의 필라멘트입니다. PETG는 PLA 보다 강도도 뛰어나고, ABS에 비해 환경 유해물질도 나오지 않습니다. 또 프린트 중 냄새도 현저히 없는 수준이고요.

오늘 받아서 간단히 큐브를 프린트 해보았는데, 출력 중 변홍도 없고 꽤 괜찮게 나옵니다. 다만 노즐 온도가 290도, 베드 온도가 90도까지 올라가야하니 PLA 출력때보다 열기가 좀 느껴지네요. 냄새도 PLA보다 덜 납니다. 가격은 한롤 당 $28.49 정도로 저렴한 PETG보단 비싼 편이나 Prusa에서 셋팅한 값을 그대로 사용할 수 있고, 출력 품질도 보장되니 그다지 비싸게 느껴지진 않습니다.

Scratch 3.0 블로킹 블럭 만들기

확장블럭 중 블로킹 기능을 필요로 하는 블럭을 만들어야 할 필요가 있다. 예를 들면 로봇의 경우 setDistance, setRotation 등 특정 거리나 특정 각도까지 움직이고, 이때 동작이 완료될 때까지 기다려야 하는 블럭이 존재한다.

Scratch 3.0의 Extension의 경우 개발 언어는 Javascript이다. 따라서 일반 언어에서 사용하듯 while 문을 이용한 제어가 불가능하다. 만약 while 문을 사용하면 스크립트 실행 자체가 멈춰버리므로, 프로그램 자체가 멈춰버리고, 이게 길게되면 각종 브라우저에서 에러와 같이 인식한다.

이를 구현하기 위해서 Promise와 setInterval의 조합으로 해결이 가능하다. 로봇이 움직이고 있다는 신호는 로봇의 상태 정보를 통해 파악할 수 있고, 움직이는 명령을 보낸 후, 로봇의 상태 정보를 지속적으로 모니터링하여 로봇의 동작이 완료되었음을 의미하는 플래그가 셋되면 블럭의 동작을 완료하면 된다.

기존 setDistance() 함수는 다음과 같이 되어 있다.

setDistance (args) {
    const l_dist = parseInt(args.L_DIST);
    const r_dist = parseInt(args.R_DIST);

    this._peripheral.setDistance(l_dist, r_dist);
}

이제 여기에 플래그를 모니터링하고, 블럭의 동작을 완료하는 부분을 추가한다.

setDistance (args) {
    const l_dist = parseInt(args.L_DIST);
    const r_dist = parseInt(args.R_DIST);

    this._peripheral.setDistance(l_dist, r_dist);

    return new Promise(resolve => {
        var first_check = true;
        var ttt = setInterval(() => {
            if(this._peripheral.isRobotMoving == false && first_check == true) {
                first_check = false;
            }
            else if(this._peripheral.isRobotMoving == false && first_check == false) {
                resolve();
                clearInterval(ttt);
            }
        }, 100);
    });
}

이제 로봇의 연결하고 위 블럭을 실행해보면, 다음과 같이 동작이 완료될 때까지 대기함을 볼 수 있다.

워드프레스에서 카테고리 리스트 페이지 만들기

워드프레스에서 카테고리를 선택하였을 때, 해당 카테고리에 해당되는 포스트들의 리스트만 보여주는 페이지가 필요할 수 있다. 아마 찾아보면 플러그인도 있긴 할텐데, 간단히 파일 하나 정도만 생성해주면 사용자가 원하는 카테고리 페이지를 만들 수 있다.

현재 사용하고 있는 테마의 디렉토리로 이동한다.

$ cd /var/www/html/wp-content/theme/twentyseventeen

category.php 파일을 생성한다. 만약 카테고리 이름별로 페이지를 생성하고 싶다면, 원하는 카테고리 이름을 추가하여 만들어주면 된다. 예를 들어 scratch 카테고리 페이지를 만든다면, category-scratch.php를 만들어준다. 워드프레스는 해당 페이지를 읽을 때 우선순위를 두게 되는데,

category-{name}.php — category.php — archive.php — index.php

와 같은 우선순위를 두고 읽게 된다.

category.php 파일을 생성하고, 다음과 같이 입력한다. archive.php 파일에서 필요한 부분만 수정하였다.

<?php
    get_header(); ?>

<div class="wrap">
    <?php if ( have_posts() ) : ?>
        <header class="page-header">
        <?php
            the_archive_title( '<h1 class="page-title">', '</h1>' );
            the_archive_description( '<div class="taxonomy-description">', '</div>' );
        ?>
        </header><!-- .page-header -->
    <?php endif; ?>

    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">

        <?php
        if ( have_posts() ) :
        ?>
        <?php
            /* Start the Loop */
            while ( have_posts() ) :
                    the_post(); ?>

            <div id="category-post">+ <a href="<?php the_permalink(); ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a>
            <small><?php the_time('F jS, Y') ?> by <?php the_author_posts_link() ?></small> </div>

            <?php
            endwhile;
        else :
            get_template_part( 'template-parts/post/content', 'none' );
        endif;
        ?>
        </main>
    </div>
<?php get_sidebar(); ?>
</div>

<?php
get_footer();

저장한 다음, 다시 블로그에서 카테고리를 선택하면 다음과 같이 보인다.

이때 보여지는 포스트의 수는 워드프레스의 Reading 설정에 있는 갯수를 따른다. 이를 늘려도 되지만, 그렇게 되면 첫 페이지에 보여지는 포스트의 수가 너무 늘어나게 되므로, archive 페이지에서만 보여지는 포스트의 수를 늘려주도록 한다.

마찬가지로 테마의 디렉토리에서 functions.php 파일을 열어 다음의 내용을 추가한다.

add_action( 'pre_get_posts', 'prefix_category_query' );
function prefix_category_query( $query ) {
    if( is_archive() && $query->is_main_query() ) {
        $query->set( 'posts_per_page', '100' );
    }
}

archive 페이지의 경우에 보여지는 포스트의 수를 100개로 늘렸다. 이제 다시 카테고리를 선택해보면,

전체 포스트가 잘 보여짐을 볼 수 있다.

Scratch 3.0 업데이트

스크레치 3.0은 현재도 활발하게 업데이트가 진행 중이다. 이에 따라, 기존에 작성하였던 Extension들도 이에 맞추어 몇가지 수정해야 하는 상황이 발생한다.

scratch-gui

  • 확장카드의 이미지들의 위치가 한 디렉토리로 통일됨. 기존은 외부 연결에 사용되는 아이콘 그림의 경우 peripheral-connection에 넣었으나, 이젠 확장카드 이름의 디렉토리에 모두 넣어 사용하고 peripheral-connection 디렉토리는 사라짐.
  • 기존 외부링크로 되어 있던 리소스들이 로컬로 전환 중. 사운드, 이미지 등이 로컬 파일로 존재. 즉, 이제 인터넷 연결 없이도 원할히 사용 가능.
  • 확장카드 추가 – gdx, LEGO Boost 등

scratch-vm

  • 추가된 확장카드에 대한 소스들 추가.

scratch-l10n

  • 추가된 확장카드에 대한 언어 추가.