一口氣將 Voice Kit 連上 Google Cloud Platform — 實作篇

TIH
29 min readMay 31, 2019

--

上一篇文中,我們已經組裝好了屬於自己的 Voice Kit,不過,如果要讓 Voice Kit 成為一個稱職的隨身助手,我們還有一些設定要做。如果你剛好對這部分有些疑問,那這篇文章應該可以解決你的問題喔~

在這篇文章中,我們要進行的有:

1. 讓你的 Voice Kit 2.0 ,變成可以進行簡單(只回一句話)對話的聊天機器人 (Chatbot)。

2. 讓你的 Voice Kit 2.0 可以辨識中文。

在按照這篇文章步驟完成設定後,我們應該可以學到:

  1. 基本的vim操作
  2. 基本的python缺套件安裝
  3. 基本的python語法修改
  4. 使用gTTS套件得到文字轉音訊檔
  5. 讓Voice Kit 辨識中文
  6. 如何開設GCP帳號

如果對整個智慧音箱的理論架構有興趣的話,可以參考另一篇的『理論篇』,裡面會有更詳細的說明~

首先我們需要準備:

1. 已經組裝好,可以正常啟動的 Voice Kit 2.0

2. 能進行網路刷卡的信用卡或 Visa 金融卡

3. 電腦 (Mac、Windows、Linux皆可)

確定都有的話,就讓我們開始吧!

1. 開啟 Google Cloud Platform 帳號

1.1 首先先點入此連結,進入 Google Cloud Platform的頁面。

1.2 選擇同意服務條款,居住地區確認依所在地選擇,這裡就先選台灣吧

*是否接收電子郵件訊息的部分可自由選擇。

1.3 點選同意並繼續,就會進入主畫面了

1.4 進入主畫面後,點選左上目錄,接著點選帳單

1.5 選擇新增帳單帳戶

1.6 點選同意後繼續

1.7 填入相關資訊、依照需求選擇付款方式

*稅務資料可以填寫個人

1.8 資料確認無誤後,點選同意免費試用

1.9 點選我知道了

到這裡,我們就完成 Google Cloud Platform 帳戶的申請了,同時,Google 也會自動開啟一個被命名為『 My First Project 』的專案。

2. 申請服務憑證(Service Credentials)

2.1 點選左上目錄,選擇 API 和服務中的『憑證』項目

2.2 點選建立憑證,選擇『服務帳戶金鑰』項目

2.3 服務帳戶的部分,請選擇『新增服務帳戶』

2.4 帳戶名稱可以輸入自己喜歡、好識別的名稱,這篇文章中,我們將帳戶命名為:『 Voice Kit 專用』

2.5 角色身分是 Project (專案)的『檢視者』

2.6 金鑰類型請選擇 JSON

2.7 點選建立

2.8 下載、儲存金鑰檔案

*金鑰就像是你在 Google Cloud 的記名會員卡,請務必妥善存放金鑰檔案。如果金鑰被盜用,相關消費都會記在這個帳戶上面喔

*如果完成後要把程式碼分享給朋友,也請記得再三檢查有沒有不小心把這個檔案公開到網路上

3. 請參考上一篇文章的第三點,以 ssh pi@{Voice Kit 的 IP} 登入 Voice Kit

3.1 執行 ~/AIY-projects-python/checkpoints/check_cloud.py ,這個 Script可以幫忙檢查憑證設定有沒有錯誤,如果正確,會顯示如下:

pi@raspberrypi:~ $ ~/AIY-projects-python/checkpoints/check_cloud.py
Please follow the Custom Voice User Interface instructions on the AIY website
to download credentials:
https://aiyprojects.withgoogle.com/voice-v1/#makers-guide-3-custom-voice-user-interface
and save them to /home/pi/cloud_speech.json
Press Enter to close...

3.2 請按下鍵盤上的 Enter關掉程式

3.3 以任意文字編輯器把上一步的 My First Project-xxxxxxxx.json (金鑰檔案)打開

3.4 打開後應該能看見類似以下開頭的文字

{
"type": "service_account",

3.5 把文字全部複製下來

3.6 回到 Voice Kit ,輸入 vim /home/pi/cloud_speech.json 打開檔案

3.7 按下 i進入編輯模式,此時畫面左下角應該會看到 -- INSERT --

3.8 把複製的內容貼上,接著按下 ESC 離開編輯模式

3.9 輸入 :wq 存檔

3.10 再次執行 ~/AIY-projects-python/checkpoints/check_cloud.py ,之後可能會看到有出現錯誤訊息的以下字串:

pi@raspberrypi:~ $ ~/AIY-projects-python/checkpoints/check_cloud.py
Testing the Google Cloud Speech API...
Traceback (most recent call last):
File "/opt/aiy/projects-python/src/aiy/_apis/_speech.py", line 274, in do_request
return self._handle_response_stream(response_stream)
File "/opt/aiy/projects-python/src/aiy/_apis/_speech.py", line 223, in _handle_response_stream
for resp in response_stream:
File "/usr/local/lib/python3.5/dist-packages/grpc/_channel.py", line 347, in __next__
return self._next()
File "/usr/local/lib/python3.5/dist-packages/grpc/_channel.py", line 341, in _next
raise self
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with (StatusCode.PERMISSION_DENIED, Cloud Speech-to-Text API has not been used in project 418950185212 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/speech.googleapis.com/overview?project=418950185212 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.)>
The above exception was the direct cause of the following exception:Traceback (most recent call last):
File "/home/pi/AIY-projects-python/checkpoints/check_cloud.py", line 104, in <module>
main()
File "/home/pi/AIY-projects-python/checkpoints/check_cloud.py", line 95, in main
if not check_speech_reco():
File "/home/pi/AIY-projects-python/checkpoints/check_cloud.py", line 68, in check_speech_reco
output = req.do_request()
File "/opt/aiy/projects-python/src/aiy/_apis/_speech.py", line 279, in do_request
raise Error('Exception in speech request') from exc
aiy._apis._speech.Error: Exception in speech request
Press Enter to close...

*看起來會像圖片中的樣子:

不用太緊張,請仔細閱讀一下,中間有出現來自 Google的溫馨提示:

Cloud Speech-to-Text API has not been used in project 418950185212 before or it is disabled.

這代表這個帳戶還沒開啟 Speech-to-Text API 的權限,等下個步驟開啟後就會消失了

4. 啟用 Speech-to-Text API

4.1 回到 GCP(Google Cloud Platform) Console,點左上目錄,點選『API 和服務』選擇 『資訊主頁』項目。

4.2 點選啟用 「+啟用API和服務」

4.3 在『搜尋 API 和服務』中輸入 Speech

4.4 點擊 Cloud Speech-to-Text API

4.5 點擊啟用

4.6 回到 Voice Kit,再次輸入 ~/AIY-projects-python/checkpoints/check_cloud.py 後,就會看到以下的成功訊息了

pi@raspberrypi:~ $ ~/AIY-projects-python/checkpoints/check_cloud.py
Testing the Google Cloud Speech API...
Everything's set up to use the Google Cloud.
Press Enter to close...

*假如輸入內容和先前相同的話,可以按鍵盤的向上鍵,就能重覆輸入同樣內容喔~

5. 進行第一次語音辨識

5.1 接下來執行 ~/AIY-projects-python/src/examples/voice/cloudspeech_demo.py ,執行後會看到以下訊息:

pi@raspberrypi:~ $ ~/AIY-projects-python/src/examples/voice/cloudspeech_demo.py
Press the button and speak

5.2 按下 Voice Kit 上的按鈕,看到畫面出現 Listening...的時候和他說 turn on the light

5.3 運作順利可以像下圖一樣,看到 Voice Kit 發光

*語音部分可以多試幾次看看反應,如果擔心英文發音不夠標準,可以用 Google 翻譯讓 Google 小姐幫忙說~

如果都請Google小姐說了 turn off light 都還沒看到 Voice Kit 發光,是因為原先的 LED 程式碼是比較舊的版本,需要更新,這時就會進入步驟5.4。

5. 4更新程式

5.4.1 用 cd ~/AIY-projects-python 的命令,把工作目錄 (work directory)切換到程式碼在的資料夾

5.4.2 接著用 git log 做確認,從 Date (加粗的文字)可以看到,程式是 2018年 8月的版本

commit 93a6306438492464c25916b6b2f53d8e4750579b
Author: Dmitry Kovalev <dkovalev@google.com>
Date: Fri Aug 3 16:20:52 2018 -0700
Update debian changelog and bump package version.Change-Id: I80bb5a73e2f9b7c12c6b51152fa7d4c1bc0b3145

5.4.3 輸入 git pull ,從 GitHub 上面拉最新的版本

*這篇文章發布時,最新的版本是應該是 2018年 12月,如加粗文字所示:

pi@raspberrypi:~/AIY-projects-python $ git log
commit 56d40c7c1c5125fcfe2e69ec2b6f27a15fe35ad9
Author: Dmitry Kovalev <dkovalev@google.com>
Date: Thu Dec 20 13:58:35 2018 -0800
Add doc generation shortcut to Makefile.Change-Id: Id51fbe9ceeeab6f055911b2f99645d54510cf03c

5.4.4 輸入 git checkout 56d40c7c1c5125fcfe2e69ec2b6f27a15fe35ad9 這個指令來切換到目前的版本 (有種時間跳躍的感覺)

5.4.5 再執行一次 ~/AIY-projects-python/src/examples/voice/cloudspeech_demo.py此時會看到以下錯誤訊息:

*此時出現錯誤是正常的,因為 AIY 的程式碼還沒更新,所以我們需要手動更新一下程式碼

INFO:root:Initializing for language en_GB...
INFO:root:Say something, e.g. turn on the light, turn off the light, blink the light, goodbye.
DEBUG:google.auth.transport.requests:Making request: POST https://oauth2.googleapis.com/token
DEBUG:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): oauth2.googleapis.com
DEBUG:requests.packages.urllib3.connectionpool:https://oauth2.googleapis.com:443 "POST /token HTTP/1.1" 400 None
ERROR:root:AuthMetadataPluginCallback "<google.auth.transport.grpc.AuthMetadataPlugin object at 0xb54815d0>" raised exception!
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/grpc/_plugin_wrapping.py", line 77, in __call__
callback_state, callback))
File "/usr/local/lib/python3.5/dist-packages/google/auth/transport/grpc.py", line 73, in __call__
callback(self._get_authorization_headers(context), None)
File "/usr/local/lib/python3.5/dist-packages/google/auth/transport/grpc.py", line 61, in _get_authorization_headers
headers)
File "/usr/local/lib/python3.5/dist-packages/google/auth/credentials.py", line 121, in before_request
self.refresh(request)
File "/usr/local/lib/python3.5/dist-packages/google/oauth2/service_account.py", line 310, in refresh
request, self._token_uri, assertion)
File "/usr/local/lib/python3.5/dist-packages/google/oauth2/_client.py", line 143, in jwt_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "/usr/local/lib/python3.5/dist-packages/google/oauth2/_client.py", line 109, in _token_endpoint_request
_handle_error_response(response_body)
File "/usr/local/lib/python3.5/dist-packages/google/oauth2/_client.py", line 59, in _handle_error_response
error_details, response_body)
google.auth.exceptions.RefreshError: ('invalid_scope: Bad Request', '{\n "error": "invalid_scope",\n "error_description": "Bad Request"\n}')

5.4.6 輸入 vim src/aiy/cloudspeech.py ,開啟有問題的程式碼,此時會看到以下訊息:

@@ -38,6 +38,9 @@ AUDIO_SAMPLE_RATE_HZ = 16000
AUDIO_FORMAT=AudioFormat(sample_rate_hz=AUDIO_SAMPLE_RATE_HZ,
num_channels=1,
bytes_per_sample=2)
+_CLOUD_SPEECH_OAUTH_SCOPE = (
+ 'https://www.googleapis.com/auth/cloud-platform'
+)
logger = logging.getLogger(__name__)@@ -47,7 +50,7 @@ class CloudSpeechClient:
if service_accout_file is None:
service_accout_file = os.path.expanduser('~/cloud_speech.json')
- credentials = service_account.Credentials.from_service_account_file(service_accout_file)
+ credentials = service_account.Credentials.from_service_account_file(service_accout_file, scopes=[_CLOUD_SPEECH_OAUTH_SCOPE])
self._client = speech.SpeechClient(credentials=credentials)
def _make_config(self, language_code, hint_phrases):

5.4.7 打開此連結,找到第 38 行,可看見以下訊息:

AUDIO_FORMAT=AudioFormat(sample_rate_hz=AUDIO_SAMPLE_RATE_HZ,
num_channels=1,
bytes_per_sample=2)

5.4.8 用 i 進入插入模式,在後面加上以下訊息 ,完成後按 ESC 恢復

_CLOUD_SPEECH_OAUTH_SCOPE = (
'https://www.googleapis.com/auth/cloud-platform'
)

5.4.9 然後找到第 47 行,用 i 進入插入模式,把

credentials = service_account.Credentials.from_service_account_file(service_accout_file)

修改成

credentials = service_account.Credentials.from_service_account_file(service_accout_file, scopes=[_CLOUD_SPEECH_OAUTH_SCOPE])

完成後按 ESC 恢復

*修改完成後,應該會如圖呈現:

5.4.10 用 :wq 存檔離開

5.4.11 再輸入一次 ~/AIY-projects-python/src/examples/voice/cloudspeech_demo.py,這時應該就能看到以下訊息:

pi@raspberrypi:~/AIY-projects-python $ ~/AIY-projects-python/src/examples/voice/cloudspeech_demo.py
INFO:root:Initializing for language en_GB...
INFO:root:Say something, e.g. turn on the light, turn off the light, blink the light, goodbye.
DEBUG:google.auth.transport.requests:Making request: POST https://oauth2.googleapis.com/token
DEBUG:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): oauth2.googleapis.com
DEBUG:requests.packages.urllib3.connectionpool:https://oauth2.googleapis.com:443 "POST /token HTTP/1.1" 200 None
INFO:aiy.cloudspeech:Start listening.
INFO:aiy.cloudspeech:Stop listening.
INFO:root:You said nothing.
INFO:root:Say something, e.g. turn on the light, turn off the light, blink the light, goodbye.
INFO:aiy.cloudspeech:Start listening.

*這個版本的程式碼不需要按按鈕就會自動偵測麥克風,所以直接用 turn on the lightturn off the light 做測試吧

6. 讓 Voice Kit 發出聲音

6.1 先把工作目錄切換到 example下

pi@raspberrypi:~ $ cd ~/AIY-projects-python/src/examples/voice/
pi@raspberrypi:~/AIY-projects-python/src/examples/voice $

:$ 中間的東西就被我們稱做是工作目錄(work directory) 這時候我們執行程式就可以少掉一串路徑

6.2 在這個狀況下,輸入 ./cloudspeech_demo.py 應該可以順利執行上一步驟的程式

接下來,我們要在開燈的時候讓他和我們說 “Light is turned on.” 、關燈的時候說 “Light is turned off.”

我們要使用gtts這個 python套件來將文字轉成語音(gtts是什麼意思請參考理論篇)

6.3 先用 vim 打開程式 vim ./cloudspeech_demo.py

6.4 移動到form aiy.cloudspeech import CloudSpeechClient

這行下面,點 i 進入插入模式,接著新增新的一行,輸入form gtts import gTTS ,如下圖:

6.5 按下 ESC離開插入模式,輸入 :wq 存檔並關掉檔案

6.6 執行 ./cloudspeech_demo.py ,但可能會看到下圖的錯誤訊息

6.7 這時用 pip把套件裝下來就行了,輸入 pip3 install gtts

6.8 當看到以下訊息時代表安裝成功

Installing collected packages: ... gtts
Successfully installed ...

6.9 這時再重新執行 ./cloudspeech_demo.py 應該就可以順利執行了

* 因為 gtts 可以幫我們把文字存成 mp3的格式,接下來要安裝可以播放 mp3的套件。

6.10 用 vim ./cloudspeech_demo.py 打開程式,在

from gtts import gTTS

下繼續新增

from pydub import AudioSegment

然後執行,如果缺套件的話同樣輸入 pip3 install pydub 安裝

6.11 接著執行 ./cloudspeech_demo.py

6.12 確認兩個 python套件安裝完成後,接著我們把文字轉成語音撥出的函式放在 from ...def get_hints( ... 的中間

def say(text):
tts = gTTS(text) # 把文字變成gtts內建的物件
tts.save('output.mp3') # 把得到的語音存成 output.mp3
sound = AudioSegment.from_mp3('output.mp3') # 把 output.mp3 讀出
sound.export('output.wav', format='wav') # 轉存成 output.wav
audio.play_wav('output.wav') # 把 wav 用 VoiceKit 播出來

*打在#後的是註解,不用打

6.13 因為用到了 audio 這個套件,所以在上面的 from再多輸入一行:

from aiy.voice import audio

完成後如下圖:

6.14 接著存檔執行,確定沒有打錯

6.15 在 def main(): 後新增 say('test') ,如下圖

6.16 執行 ./cloudspeech_demo.py ,應該會看到以下錯誤訊息:

Traceback (most recent call last):
File "./cloudspeech_demo.py", line 81, in <module>
main()
File "./cloudspeech_demo.py", line 48, in main
say('Test')
File "./cloudspeech_demo.py", line 30, in say
sound = AudioSegment.from_mp3("output.mp3")
File "/home/pi/.local/lib/python3.5/site-packages/pydub/audio_segment.py", line 716, in from_mp3
return cls.from_file(file, 'mp3', parameters=parameters)
File "/home/pi/.local/lib/python3.5/site-packages/pydub/audio_segment.py", line 665, in from_file
info = mediainfo_json(orig_file)
File "/home/pi/.local/lib/python3.5/site-packages/pydub/utils.py", line 263, in mediainfo_json
res = Popen(command, stdin=stdin_parameter, stdout=PIPE, stderr=PIPE)
File "/usr/lib/python3.5/subprocess.py", line 676, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.5/subprocess.py", line 1282, in _execute_child
raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'ffprobe'

6.17 錯誤關鍵在 No such file or directory: ‘ffprobe’ 上面,請輸入以下文字進行安裝

sudo apt install ffmpeg

6.18 出現 Do you want to continue? [Y/n] 時請選 Y,接著執行 ./cloudspeech_demo.py

*沒問題的話應該能聽到 Test 的音效

接著,需要在原先開燈關燈判斷的地方加上開關完成的回應。

6.19 找到 if 'turn on the light' in text: 後面加上 say('the light has been turned on')

6.20 找到 if 'turn off the light' in text: 後面加上 say('the light has been turned off')

6.21 找到 if 'goodbye' in text: 後面加上 say('bye')後存檔、執行。

*此時的程式如下圖:

這階段完成的程式

7. 中文化

語音轉文字的中文化可以透過在執行的時候使用 --language=zh-TW 這樣的參數讓他變成中文。

7.1 將 say(text): 裡面的 tts = gTTS(text) 改成 tts = gTTS(text, lang='zh-TW'),如下圖所示:

7.2 判斷意圖的部分可以用 elif 依樣畫葫蘆,參考下圖:

其實可以自己玩玩看,例如「生日快樂」之類的

然後用 ./cloudspeech_demo.py — language=zh-TW 的方式執行

*因為語言參數會在

text = client.recognize(language_code=args.language, hint_pharses=hints)

被傳入(所以直接改這邊把它寫死也可以)

到這裡,我們就有了一個可以聽懂中文、也能回應中文的 Voice Kit啦~

大家都關心的費用問題

Cloud Speech To Text API (STT)需要付費,目前每 15秒 0.006美元 (約0.18台幣)每個月 60分鐘免費,大概是 240 次,一天平均可使用 8次

Text To Speech API (TTS)的部分主要是進行文字轉語音(還需要確認) ,目前每個月 400萬字以下的轉換是免費的,超過400萬字後,每 100 萬文字收取 4 美元 (約32元台幣)費用

以上兩種 API 如果都是由個人正常使用,應該都不會超過免費使用的額度。

同場加映:串接Maid White

只是控制 Voice Kit 上面的電燈,還不是個合格的隨身助手,如果要控制家中的電器,我們可以利用 Maid White Python SDK 來控制 Maid White 目前可以支援的設備(電燈、冷氣、洗衣機……)喔~

安裝透過 pip3 install maidwhite 進行,並透過以下程式碼進行控制

m = MaidWhite({access_token})
l = m.get_devices()
dl = [x for x in l if x.display_name == 'My lamp']
dl[0].set_state({"power_status": "true"})

完成後如下圖

access_token 的取得方式請參考 如何以qmote控制聲寶冷氣 這篇文章,透過以下命令取得

curl 'https://www.tih.tw/2/token' -d 'username=your@email.tw&password=yourpassword'

參考資料

AIY 多語系

Maid White Python SDK

如何開機自動啟動程式 (避免Voice Kit 插頭被拔掉…)

--

--

TIH

台灣智慧家庭(Taiwan Intelligent Home,TIH),以打造台灣軟體產業鍊為宗旨的公司,致力於智慧家電跨品牌整合,同時協助開發物聯網相關的應用程式。