2016년 10월 31일 월요일

RPi에서 음성 인식하기

라즈베리 파이에서 USB 마이크와 PocketSphinx라는 소프트웨어를 이용하여 음성인식을 시험해보았다. 이 글에서는 마이크를 사용하여 소리를 녹음해보고, 음성인식에 필요한 소프트웨어를 설치한 다음, 음성을 인식하는지 테스트해보고, 그것을 이용해서 다른 명령을 실행시키는 과정을 다룬다.

USB 마이크


구입과 설치


USB 마이크의 구입과 설치에 대해서는 Raspberry Pi에서 소리 입력 및 녹음하기를 참조. 아래와 같은 명령으로 소리를 녹음하고 재생할 수 있으면, 다음으로 진행할 수 있다(이 글에서는 소리의 녹음을 위해 ALSA 프레임워크를 사용한다).

$ arecord -D plughw:1,0 -d 5 test.wav
$ aplay test.wav


USB 마이크를 기본 녹음 장치로 설정


USB 마이크를 기본 녹음 장치로 만들어두는 것이 편리할 것이다.

$ more ~/.asoundrc
pcm.!default {
    type asym
    playback.pcm {
        type plug
        slave.pcm "hw:0,0"
    }
    capture.pcm {
        type plug
        slave.pcm "hw:1,0"
    } 
}


홈 디렉터리에 위와 같이 .asoundrc를 작성한다. 이제 arecord를 실행할 때 -D 옵션을 생략해도 된다.

$ arecord -d 5 test.wav


음성인식


의존 패키지 설치


PocketSphinx를 설치하기에 앞서, 필요한 패키지들을 먼저 설치한다.

sudo apt install libasound2-dev autoconf libtool bison swig python-dev python-pyaudio
sudo pip install gevent grequests


SphinxBase와 PocketSphinx 설치


SphinxBase를 설치한다.

git clone git://github.com/cmusphinx/sphinxbase.git
cd sphinxbase
./autogen.sh
make
sudo make install
cd ..

PocketSphinx를 설치한다.

git clone git://github.com/cmusphinx/pocketsphinx.git
cd pocketsphinx
./autogen.sh
make
sudo make install
cd ..

ldconfig를 실행.

sudo ldconfig


사전 만들기


말뭉치(corpus)를 텍스트 파일로 작성한다. 아래의 말뭉치는 구글 캘린더를 조작하는 명령과 관련된 것이다.

$ more corpus.txt 
day
week
month
4
agenda
refresh
next
previous
today

언어 모델과 사전을 만들 차례이다. 위에서 만든 말뭉치 파일을 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 페이지의 Upload a sentence corpus file:에서 선택한 다음, "COMPILE KNOWLEDGE BASE" 버튼을 누르면 잠시 후에 다운로드 페이지로 이동한다. .lm과 .dic 파일을 다운로드하거나, .tgz로 압축된 파일을 다운로드하여 압축을 푼다. 파일들을 적당한 위치로 옮기고, 필요하다면 이름도 바꾼다.

음성인식 테스트


다음과 같이 pocketsphinx_continuous 명령을 실행하여 음성을 인식하는지 테스트해본다. -lm과 -dict 옵션에는 .lm 파일과 .dic 파일을 각각 지정한다. USB 마이크가 디폴트 녹음 장치로 설정되지 않았다면, -adcdev plughw:1,0 옵션을 추가하여 실행한다.

$ pocketsphinx_continuous -hmm /usr/local/share/pocketsphinx/model/en-us/en-us -lm cal.lm -dict cal.dic -samprate 16000/8000/48000 -inmic yes
INFO: pocketsphinx.c(152): Parsed model-specific feature parameters from /usr/local/share/pocketsphinx/model/en-us/en-us/feat.params
Current configuration:
[NAME]   [DEFLT]  [VALUE]
-agc   none  none
-agcthresh  2.0  2.000000e+00
-allphone    
-allphone_ci  no  no
-alpha   0.97  9.700000e-01

...

INFO: ps_lattice.c(1441): Joint P(O,S) = -144424 P(S|O) = -24085
INFO: ngram_search.c(872): bestpath 0.00 CPU 0.000 xRT
INFO: ngram_search.c(875): bestpath 0.00 wall 0.001 xRT
NEXT
INFO: continuous.c(275): Ready....
^C

위에서는 음성을 NEXT라고 인식한 것을 볼 수 있다.

음성인식 결과를 이용하여 다른 명령 실행하기

pocketsphinx_continuous를 사용하여 구글 캘린더(이전 글 참조)를 조작하는 셸 스크립트를 다음과 같이 작성해보았다.

#!/bin/sh

cd /home/pi/calendar

pocketsphinx_continuous -hmm /usr/local/share/pocketsphinx/model/en-us/en-us \
    -lm cal.lm -dict cal.dic -samprate 16000/8000/48000 -inmic yes | \
    xargs -L1 sh -c 'echo "$*" | head -c1 | tr A-Z a-z | \
    xargs xdotool search -onlyvisible -class "chromium" windowactivate type' -

위에서 pocketsphinx_continuous는 마이크로 입력된 음성을 분석해서 그에 해당하는 단어를 출력하는 것을 반복한다. 그러면, 출력이 있을 때마다 파이프와 xargs를 통해 head, tr, xdotool이 실행된다. 'NEXT'를 인식하였다면, 첫 글자 'N'을 취해서, 소문자인 'n'으로 변환하고, Chromium 창에서 'n'을 타이핑하는 것이다. 구글 캘린더는 키보드 입력으로 'n'이 들어오면 다음(next)으로 이동한다.
자동 실행을 위해 다음과 같은 systemd unit 파일을 ~/.config/systemd/user/pocketsphinx.service에 작성했다.

[Unit]
Description=PocketSphinx
After=graphical.target

[Service]
ExecStart=/home/pi/calendar/start-pocketsphinx.sh
Environment=DISPLAY=:0
Restart=no

[Install]
WantedBy=default.target

참고





2016년 10월 20일 목요일

RPi 벽걸이 캘린더

라즈베리 파이와 구글 캘린더를 이용하여 벽걸이 캘린더를 만드는 프로젝트가 있다. 요즘 수요일마다 참석하는 스터디 모임에서 일차적인 목표로 삼은 과제이다.

얼마 전에 구입한 RPi 3 Model B를 이용하여 이를 구현해보았다. Raspbian의 새로운 데스크톱 환경인 PIXEL에 기본으로 포함되는 Chromium 웹 브라우저를 사용했다.

Chromium에서 구글 캘린더를 열면 모바일 페이지가 뜨는 버그가 있는데, URL을 https://calendar.google.com/calendar/render로 변경함으로써 데스크톱용 캘린더 페이지를 열 수 있다(스터디에서는 버그를 피하기 위해 Chromium 대신 iceweasel(Firefox ESR) 브라우저를 설치하는 방법을 택했다. 따라서 아래의 내용을 참고하되 iceweasel에 맞도록 수정이 필요하다). Chromium에서 키보드의 F11 키를 누르면 전체 화면으로 바뀐다. 캘린더 보기는 "월"을 선택한다.

명령행에서 다음의 명령을 실행하면, Chromium을 전체화면으로 실행시키면서 구글 캘린더를 "월" 보기로 보여준다.
chromium-browser --kiosk "https://calendar.google.com/calendar/render#main_7|month"

이제, RPi를 켤 때마다 캘린더가 뜨도록 할 차례이다. 스터디에서는 ~/.config/lxsession/LXDE/autostart 파일을 이용하려고 했지만, 원격 접속의 문제인지 아니면 PIXEL로 바뀐 것 때문인지는 몰라도 예상대로 잘 되지 않는 것 같았다. 집에 와서 검색을 통해 다음과 같은 방법을 찾았다.
  1. 위의 명령을 실행하는 start-kiosk.sh 파일을 pi 사용자의 홈(~) 디렉터리에 만든다.
  2. 다음과 같은 설정으로 ~/.config/systemd/user/kiosk.service 파일을 만든다.
    [Unit]
    Description=Kiosk
    After=graphical.target
    
    [Service]
    ExecStart=/bin/bash /home/pi/start-kiosk.sh
    Environment=DISPLAY=:0
    Restart=always
    
    [Install]
    WantedBy=default.target
  3. 다음의 명령을 실행한다.
    systemctl --user enable kiosk
    systemctl --user start kiosk
  4. sudo reboot 명령으로 테스트해본다. Chromium이 제대로 종료되지 않은 것에 대한 팝업이 뜨는데, 이것을 숨기려면 start-kiosk.sh 파일의 chromium-brower 명령에 옵션을 추가한다.
    chromium-browser --kiosk --disable-session-crashed-bubble --disable-infobars "https://calendar.google.com/calendar/render#main_7|month"
  5. 화면이 꺼지거나 화면보호기가 뜨지 않도록 하려면 start-kiosk.sh에서 Chromium 실행 전에 다음과 같은 명령을 실행하도록 해준다.
    xset -dpms; xset s off

참고





2016년 10월 18일 화요일

Raspberry Pi 잡설

Raspberry Pi 관련 몇 가지 소소한 발견.

RPi에 외장 HDD 연결

  • RPi 1 Model B에서는 USB 포트의 출력이 약해서 외장형 HDD 사용에 문제가 있고, 가능하다 하더라도 포트가 2개 밖에 없기 때문에 다른 USB 장치를 꽂을 여유가 없다. 외장 HDD를 사용하기 위해서는 별도의 전원을 가진 USB 허브가 필요하다.
  • RPi 3 Model B에 외장형 HDD를 직접 물려보았더니 잘 동작한다. 핫 플러깅을 했는데도 문제가 없었다. 전원은 2.5 A 어댑터로부터 RPi의 마이크로 USB에 공급.
  • RPi 1 Model B+에서도 가능할지 모른다.

OpenELEC


  • RPi에서 쓰던, OpenELEC을 설치한 마이크로 SD 카드를 그대로 RPi 3에 넣고 전원을 넣어보니 부팅이 되는 듯 메시지가 올라가다가 멈춘다. 업데이트를 해줘야 쓸 수 있다.
  • OpenELEC에서 TV를 볼 수 있다! 댓글의 채널 목록을 받아서 테스트해보니, 현재 안 나오는 채널도 많지만, 아직 나오는 채널도 많다.
    www.todayhumor.co.kr/board/view.php?table=jisik&no=201727
  • KODI 재시작 명령
    systemctl restart kodi

디스플레이

영상을 감상할 때 종횡비 4:3의 브라운관 TV에 연결하면 위아래가 남고, WFHD(21:9) 모니터에서는 좌우가 남는다. 영상을 감상하는 데에는 역시 16:9 모니터가 최적일 것 같다. 그런 용도가 아니라 하더라도, RPi를 사용할 때 이런저런 신경을 쓰지 않고 편하게 사용할 수 있는 디스플레이 장치는 HDMI 단자가 있고 스피커가 내장된, 종횡비 16:9의 모니터라는 결론.