3月26日のOSアップグレードで、少しインタフェースが変わったため、確実な同居方法を
メモした。また、04-13版のレベルダウンへも対応追記。
1.AIYプロジェクトのVoice Kit SD imageをダウンロード
aiyprojects-2018-04-13.img.xz
https://dl.google.com/dl/aiyprojects/aiyprojects-latest.img.xz
2.MicroSDに書き込み、インストール
aiyprojects-2018-04-13.img.xzを解凍し、書き込みする
参照:https://aiyprojects.withgoogle.com/voice/#assembly-guide-5-2–boot-the-device
2.1 Audio音量関数の修正
2018-04-13.imgにはレベルダウンがあり、AIY-project*/src/aiy/audio.py を修正する。
113行 db_range = -60.0 – (-60.0 * (volume / 100.0)) を
db_range = -6.6 – (-6.6 * (volume / 100.0)) に書き換え。
3.AIYでの日本語動作確認
PulseAudioの初期値が未設定なので、/etc/pulse/default.pa を編集します。
バックエンドドライバーをロードする行を探してアンコメント(#削除)し、
以下のように device パラメータを追加してください。
さらに autodetect モジュールをロードする行をコメントアウトしてください。
load-module module-alsa-sink device=dmix
load-module module-alsa-source device=dsnoop
# load-module module-udev-detect
# load-module module-detect
再起動
amixer sset Master 50% (初期音量を変更したい)
参照:https://kureuetan.com/web/raspberrypi/4998/#OS
~/bin/AIY-projects-shell.sh
src/examples/voice/assistant_grpc_demo.py
この後、GoogleAssistantアプリで、デバイスVoiceKitの言語選択を日本語に設定してください。
4.RasPi用Snowboyのインストール
参照:https://github.com/wanleg/snowboyPi
4.1 事前準備
sudo apt update && sudo apt -y upgrade && sudo apt-get -y auto-remove && sudo reboot
OSのバージョンは、3月末で、次のようになります。
Linux raspberrypi 4.14.30-v7+ #1102 SMP Mon Mar 26 16:45:49 BST 2018 armv7l GNU/Linux
sudo apt -y install python-pyaudio python3-pyaudio sox python3-pip python-pip libatlas-base-dev
sudo pip3 install pyaudio
sudo cp ~/.asoundrc /root/
4.2 Snowboyの準備
wget https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/rpi-arm-raspbian-8.0-1.1.1.tar.bz2
tar xvf rpi-arm-raspbian-8.0-1.1.1.tar.bz2
mv rpi-arm-raspbian-8.0-1.1.1 snowboy
4.3 サウンドテスト
speaker-test -c 2
arecord -d 3 test.wav
aplay test.wav
4.4 Hotwordの作成
pip install requests
cd snowboy
wget https://github.com/wanleg/snowboyPi/raw/master/training_service.py
https://snowboy.kitt.ai にログインし、Profile settings をクリック、
作成された API token をコピーしてメモする。
training_service.pyの次のパラメタを設定する
############# MODIFY THE FOLLOWING #############
token = “コピーしてメモしたAPI token”
hotword_name = “ホットワードの名前”
language = “jp”
age_group = “30_39”
gender = “M”
microphone = “usb microphone”
############### END OF MODIFY ##################
rec -r 16000 -c 1 -b 16 -e signed-integer 1.wav
rec -r 16000 -c 1 -b 16 -e signed-integer 2.wav
rec -r 16000 -c 1 -b 16 -e signed-integer 3.wav
python training_service.py 1.wav 2.wav 3.wav NeGoogle.pmdl
cp NeGoogle.pmdl resources/NeGoogle.pmdl
4.5 Hotwordのテスト
python3 demo.py ~/snowboy/resources/NeGoogle.pmdl
startAlexa.sh,assistant_grpc_snow_demo.pyのダウンロードと走行確認
wget -O startAlexa.sh https://mori1-hakua.tokyo/test/startAlexa.txt
wget -O _snowboydetect.so https://mori1-hakua.tokyo/test/_snowboydetect.so
wget -O assistant_grpc_snow_demo.py https://mori1-hakua.tokyo/test/snow_demo.txt
python3 assistant_grpc_snow_demo.py
5. Alexaのインストール
参照:https://github.com/alexa/avs-device-sdk/wiki/Raspberry-Pi-Quick-Start-Guide-with-Script
cd
wget https://raw.githubusercontent.com/alexa/avs-device-sdk/master/tools/Install/setup.sh
wget https://raw.githubusercontent.com/alexa/avs-device-sdk/master/tools/Install/config.txt
wget https://raw.githubusercontent.com/alexa/avs-device-sdk/master/tools/Install/pi.sh
vi setup.sh で次の変更を実施
en-US を ja-JP
vi config.txtで次の設定を実施
Client ID, Client Secret, and Product IDの設定
vi avs-device-sdk/build/BuildDefaults.cmake で include(KeywordDetector) をコメント化
sudo bash setup.sh config.txt
sudo bash startauth.sh の起動後、http://localhost:3000をアクセス
sudo cp ~/.asoundrc /root/
sudo bash startsample.sh を実行し、c、1、6と入力して日本語モードにする
6. AlexaとGoogleAssistantの同時実行
cd ~/snowboy
python3 assistant_grpc_snow_demo.py NeGoogle.pmdl
この他、
python3 assistant_grpc_snow_demo.py snowboy.umdl も試せます。
4月2日
「アレクサ」のヒット率を高めた、alexa_02092017.umdl を使うように変更した。
ついでに、次をサポート。Googleの方が偉そうになりました。
・ね~グーグル、Alexaを英語モードで呼び出して
What time is it?
・ね~グーグル、Alexaを日本語モードで呼び出して
今日の選抜野球の結果は?
やっぱりエスカレートの機能追加です。フフフ・・・
・ね~グーグル、Alexa も一緒に聞いて
「日本の首都はどこですか」
Alexaは?
日本の首都は東京です。
そだね~、
日本の首都は、トウキョウです。
・ね~グーグル、Alexa も一緒に聞いて
「選抜高校野球で優勝したのはどこですか」
Alexaは?
5対2で、大阪の大阪桐蔭高校が勝ちました。
そだね~、
すみません、お役に立てそうにありません。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2017 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A demo of the Google Assistant GRPC recognizer.""" # from google.assistant.library.event import Event, EventType, IterableEventQueue import snowboydecoder # Hotword 検出器 import sys, os, signal, time, threading, select,fcntl from subprocess import Popen, PIPE, call, check_output import logging import aiy.assistant.grpc import aiy.audio import aiy.voicehat logging.basicConfig( level=logging.INFO, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s" ) buttonFlag = False # 押しボタン関連 buttonNone = False # Ctrl+Cの伝達に使用 speakFlag = False # 発話中 status_ui = None # LED状態表示アクセス detector = None # Hotword検出器インスタンス player = None # Alexaプロセス player2= None # Alexaプロセス(sudo) think = None # Thinking... speak = None # Speaking... arch_v6 = check_output(["uname -a"],shell=True).decode('utf-8') if arch_v6.find("armv6")<0:arch_v6 = False else:arch_v6 = True print ("ArchitectureV6=",arch_v6) # Pi3 or PiZero # events = IterableEventQueue() interrupted = False # Hotword検出器脱出要求 def signal_handler(signal, frame): global buttonNone, interrupted aiy.voicehat.get_led().stop() buttonNone = True interrupted = True # capture SIGINT signal, e.g., Ctrl+C signal.signal(signal.SIGINT, signal_handler) def interrupt_callback(): # Hotword検出中の脱出チェックループ global interrupted return interrupted def detect_callback1(word="t"): # Hotword検出出口(Alexa) global player, detector, interrupted, speakFlag, buttonFlag if player:player.poll() if player.returncode==None: player.stdin.write((word+"\n").encode('utf-8')) player.stdin.flush() # Alexaへの単発要求(tはTalk) if word!="t": return detector.stream_in.stop_stream() # 検出中断 speakFlag = True butttonFlag = False interrupted = True status_ui.status('listening') print('Listening...') snowboydecoder.play_audio_file(snowboydecoder.DETECT_DING) def detect_callback2(): # Hotword検出出口(Google) global buttonFlag, interrupted, detector if buttonFlag==False: buttonFlag = True # ボタン押下と同等 # events.offer(Event(EventType.ON_NO_RESPONSE,{'button':'ON'})) detector.stream_in.stop_stream() # 検出中断 interrupted = True def on_button_press(): # ボタン押下検出出口(Google) global buttonFlag, interrupted, think, speak, speakFlag print('Button pressed') think = False speak = False if speakFlag==True: speakFlag = False # Alexaニュースの発話終了を手動で return if buttonFlag==False: buttonFlag = True # events.offer(Event(EventType.ON_NO_RESPONSE,{'button':'ON'})) interrupted = True # Hotword検出中の脱出 def myTh_out(player): # Alexa Print出力の監視チェック global detector, buttonNone, interrupted, speakFlag, think, speak think = False speak = False # set non-blocking flag while preserving old flags fl = fcntl.fcntl(player.stdout, fcntl.F_GETFL) fcntl.fcntl(player.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK) while not player.stdout.closed: (ready,_,_) = select.select([player.stdout],[],[],0.1) # 100msごと if ready and not player.stdout.closed: buf = player.stdout.read() if buf: buf = buf.decode().rstrip('\n') print(buf) if ("not connected!" in buf) or ("idle!" in buf): if think: # Thinkingのまま終了 think = False speak = True speakFlag = True elif speak==True: # Speakingの順調な終了 if arch_v6: time.sleep(3) speak = False speakFlag = False detector.stream_in.start_stream() else: # 途中の状態遷移 if "Thinking.." in buf: think = True if "Speaking.." in buf: think = False speak = True continue def myThread(player): # Alexa へのコマンド入力チェック global detector, buttonNone, interrupted, speakFlag, speak while not player.stdin.closed: (ready,_,_) = select.select([sys.stdin],[],[],3) # 3秒ごとにチェック if ready: word = input() # 入力バッファに文字がある else: continue if len(word)==0: # Ctrl+Cの押下を想定 buttonNone = True interrupted = True return if player:player.poll() if player.returncode==None: if word[0]=="p" or len(word)==1 and word in "tshi1234": # 単発+p speak = False speakFlag = False detect_callback1(word.rstrip('/').replace('/','\n')) #"p" continue if word=="q": # "q" stop_alexa() break sudo_exec(word.rstrip('/').replace('/','\n')) # それ以外 def stop_alexa(): # Alexaの停止 global player if player:player.poll() if player.returncode==None: player.stdin.write('q\n'.encode('utf-8')) player.stdin.flush() try: outs, errs = player.communicate() except BlockingIOError: Popen(["killall","python3"]) def sudo_exec(word): # 一旦Alexaを停止し、sudo Alexaでコマンド実行 global player2 print(word) stop_alexa() forAlexa(True) time.sleep(5) if arch_v6: time.sleep(5) player2.stdin.write((word+"\n").encode('utf-8')) player2.stdin.flush() time.sleep(1) player2.stdin.write(("q\n").encode('utf-8')) player2.stdin.flush() outs, errs = player2.communicate() print (str(errs)) time.sleep(1) forAlexa() time.sleep(2) thread = None th_out = None def forAlexa(sudo=False): # Alexaの起動と入出力2スレッド化 global thread, player, player2 if sudo: player2 = Popen(["sudo","/home/pi/build/SampleApp/src/SampleApp", \ "/home/pi/build/Integration/AlexaClientSDKConfig.json"],stdin=PIPE) else: player = Popen(["bash","-lc","'/home/pi/startAlexa.sh'"],stdin=PIPE,stdout=PIPE) thread = threading.Thread(target=myThread, args=(player,)) thread.start() th_out = threading.Thread(target=myTh_out, args=(player,)) th_out.start() time.sleep(2) detect_callback1('p/1/2/2/2/2/q'.replace('/','\n')) def main(): global thread, player, status_ui, detector, buttonFlag, buttonNone, \ interrupted, speakFlag, think, speak model = "NeGoogle.pmdl" if len(sys.argv)>1: model = sys.argv[1] # GoogleのHotwordは引数から status_ui = aiy.voicehat.get_status_ui() status_ui.status('starting') assistant = aiy.assistant.grpc.get_assistant() button = aiy.voicehat.get_button() button.on_press(on_button_press) # ボタン押下イベント出口登録 hotwords = ["resources/alexa_02092017.umdl" , "resources/"+model] senses = [0.2, 0.3] # Hotwordの揺れ許容度 callbacks = [detect_callback1, detect_callback2] # それぞれの出口登録 detector = snowboydecoder.HotwordDetector(hotwords, sensitivity=senses) forAlexa() # Alexaの起動 print('\033[96mListening\033[0m... Press Ctrl+C to exit') with aiy.audio.get_recorder(): while True: status_ui.status('ready') print('Press the button and speak') buttonFlag = False interrupted = False detector.start(detected_callback=callbacks, interrupt_check=interrupt_callback, sleep_time=0.05) if buttonFlag==False: # Hotword検出以外の脱出 if buttonNone: break # Ctrl+C while speakFlag==True: # Alexaの呼び出しの場合 time.sleep(1) continue # Alexa butttonFlag = False # Google呼び出し status_ui.status('listening') print('\033[96mListening\033[0m...') text, audio = assistant.recognize() # Google音声認識 if text: print('You said "', text, '"') if 'Alexa も' in text and '聞いて' in text: # Alexaとの共聴 # detect_callback1()の代わり detector.stream_in.stop_stream() player.stdin.write('t\n'.encode('utf-8')) # call(["n2tts -o - Alexaからね| aplay"],shell=True) # detect_callback1() player.stdin.flush() text, audio = assistant.recognize() # Google音声認識 text2, audio2 = assistant.recognize() # Google,Alexa音声認識 print('You said "', text, '"') if audio: print('Alexa said "', text2, '"') if text2 and "ニュース" in text2: player.stdin.write('2\n2\n'.encode('utf-8')) player.stdin.flush() time.sleep(0.5) player.stdin.write('2\n2\n'.encode('utf-8')) player.stdin.flush() time.sleep(0.5) call(["n2tts -o - Alexa、あとでね?| aplay"],shell=True) # 話の腰を折る speakFlag = True if audio: if speakFlag==False: call(["n2tts -o - そぅだねえ~?| aplay"],shell=True) # 正常発話に相打ちする aiy.audio.play_audio(audio) # 自分の主張 if speakFlag==True: detector.stream_in.stop_stream() call(["n2tts -o - それでAlexaは?ボタンを押すまでね。| aplay"],shell=True) player.stdin.write('1\n1\n'.encode('utf-8')) player.stdin.flush() # Alexaに話を続行させる while speakFlag: text, audio2 = assistant.recognize() print('Alexa said "', text, '"') # Alexa発話の筆記 if text=="": break # ボタン押下又は発話終了 detector.stream_in.start_stream() continue if 'Alexa を' in text and '呼び出し' in text: if '英語' in text: # 英語モードでのAlexa呼出 sudo_exec("c\n1\n1\n") elif '日本語' in text: # 日本語モードでのAlexa呼出 sudo_exec("c\n1\n6\n") detect_callback1() # Hotword検出済み while speakFlag==True: time.sleep(1) # 発話終了を待つ continue if text == 'さようなら': print('Bye!') break if audio: aiy.audio.play_audio(audio) detector.stream_in.start_stream() aiy.voicehat.get_led().stop() stop_alexa() if th_out:th_out.join() if thread:thread.join() print("Alexaは打ち切られました ") detector.terminate() Popen(["killall","python3"]) if __name__ == '__main__': main()Alexaのリンクが更新されたようです
https://developer.amazon.com/ja-JP/docs/alexa/alexa-voice-service/register-a-product.html