[GoogleAssistant]把Google助理搬到電腦(Windows)上

因為專題有接觸到Google Assistant的部分,在做的過程中真的是遇到各式各樣的阻礙。一開始是硬體問題,再來是各種套件版本上的Bug,最後甚至是Python的版本問題。總之問題多多,覺得如果不記錄一下我可(肯)能(定)會忘。專題是在樹莓派上做,但我想在Windows上也試試看會不會有其他問題出現(當然有)。

首先剛才有提到途中遇到最多的是套件版本的問題,所以這邊先記錄一下環境和各種套件版本:
 Windows 10
 python3.9.2

以下是相關套件,安裝助理套件時會順便安裝
 cachetools 5.0.0
 certifi 2021.10.8
 cffi 1.15.0
 charset-normalizer 2.0.11
 click 6.7
 google-assistant-grpc 0.0.2
 google-assistant-sdk 0.3.3
 google-auth 2.6.0
 google-auth-oauthlib 0.4.6
 googleapis-common-protos 1.54.0
 grpcio 1.43.0 註1
 idna 3.3
 monotonic 1.6
 oauthlib 3.2.0
 pip 22.0.3
 protobuf 3.19.4
 pyasn1 0.4.8
 pyasn1-modules 0.2.8
 pycparser 2.21
 requests 2.27.1
 requests-oauthlib 1.3.1
 rsa 4.8
 setuptools 60.7.1
 six 1.16.0
 sounddevice 0.3.7
 tenacity 5.0.4
 urllib3 1.21.1

(一)到Actions on Google新增一個項目,並且註冊一個設備

 1.點擊「New Project」新增專案

 2.替專案取名後選擇語言和國家,最後點擊「Create Project」創建專案

 3.點擊底下的「Click here」來註冊一個設備

 4.點擊「REGISTER MODEL」

 5.分別填完以下選項後點擊「REGISTER MODEL」

 6.點擊「Download OAuth 2.0 credentials」下載憑證

 7.下載完成後點擊「Next」

 8.點擊「SAVE TRAITS」儲存

(二)接著到Google Cloud Platform的Google Assistant API替新增的專案啟用Google Assistant API

 1.點擊上方「My First Project」切換專案

 2.選擇剛才所新增的專案

 3.然後啟用Google Assistant API

(四)到Google Cloud Platform的OAuth同意畫面進行設定並新增測試使用者

 1.確認上方為剛才建立的專案後選取「外部」,最後按下「建立」

 2.填第一個電子郵件

 3.再填一次電子郵件,然後點擊下方「儲存並繼續」兩次

 4.然後會到測試使用者頁面,這裡需要新增測試使用者(當時操作時順序不一致因此畫面有些差異無須理會)註2

 5.新增測試使用者帳號

 6.新增後點擊「儲存並繼續」就完成新增了

專案的設定到這邊結束,下面是本機上的操作。打開命令提示字元!(超推Windows Terminal介面超好看還能客製化٩(^ᴗ^)۶)

※以下示範都是在D槽裡執行

(五)替Google助理新增一個專用目錄,把剛剛下載的json檔改個名字移進新增的目錄以便之後使用

D:\>md GoogleAssistant
D:\>copy \json檔\ GoogleAssistant\credentials.json
複製了         1 個檔案。

(六)建立python虛擬環境

D:\>cd GoogleAssistant
D:\GoogleAssistant>python -m venv GoogleAssistant

(七)更新虛擬環境裡的pip

D:\GoogleAssistant>GoogleAssistant\Scripts\python -m pip install --upgrade pip setuptools --upgrade

(八)進入剛剛建立的虛擬環境

D:\GoogleAssistant>GoogleAssistant\Scripts\activate

(九)安裝Google Assistant所需套件

(GoogleAssistant) D:\GoogleAssistant>python -m pip install --upgrade google-assistant-sdk[samples]
(GoogleAssistant) D:\GoogleAssistant>python -m pip install --upgrade google-auth-oauthlib[tool]

(十)Google授權

(GoogleAssistant) D:\GoogleAssistant>google-oauthlib-tool --scope https://www.googleapis.com/auth/assistant-sdk-prototype --save --headless --client-secrets credentials.json

 送出指令後會輸出以下內容

Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=418492389
456-584uerj5520ruio5f8nb5qioi49h1ckd.apps.googleusercontent.com&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=https%3A%2F%2F
www.googleapis.com%2Fauth%2Fassistant-sdk-prototype&state=wgDKFQHAzmM1u2fn4&prompt=consent&access_type=offline
Enter the authorization code:

 前往內容中的網址會看到以下畫面,要求登入帳號。這裡登入的帳號必須是剛才的測試使用者帳號不然會出現像註2的授權錯誤畫面

 給他繼續下去就對了

 這邊會要求存取和提供相關權限,一樣按繼續

 最後會出現一組授權碼,將它複製後貼上命令提示字元

 像這樣⇩

 貼上後按下Enter,如果成功的話會輸出如下訊息

credentials saved: C:\Users\username\AppData\Roaming\google-oauthlib-tool\credentials.json

(十一)測試助理

 輸入以下指令來使用Google助理註3

(GoogleAssistant) D:\GoogleAssistant>googlesamples-assistant-pushtotalk

 很遺憾地出了一點問題註4

Traceback (most recent call last):
  File "C:\Program Files\Python\Python39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\Python\Python39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "D:\GoogleAssistant\GoogleAssistant\Scripts\googlesamples-assistant-pushtotalk.exe\__main__.py", line 4, in 
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\googlesamples\assistant\grpc\pushtotalk.py", line 29, in 
    from tenacity import retry, stop_after_attempt, retry_if_exception
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\tenacity\__init__.py", line 292
    from tenacity.async import AsyncRetrying
                  ^
SyntaxError: invalid syntax

 爬了文之後找到了解決方式如下:

  1.解安裝tenacity套件

(GoogleAssistant) D:\GoogleAssistant>python -m pip uninstall tenacity -y
Found existing installation: tenacity 4.1.0
Uninstalling tenacity-4.1.0:
  Successfully uninstalled tenacity-4.1.0

  2.安裝tenacity套件5.0.4版

(GoogleAssistant) D:\GoogleAssistant>python -m pip install --upgrade tenacity==5.0.4
Collecting tenacity==5.0.4
  Using cached tenacity-5.0.4-py2.py3-none-any.whl (38 kB)
Requirement already satisfied: six>=1.9.0 in d:\googleassistant\googleassistant\lib\site-packages (from tenacity==5.0.4) (1.16.0)
Installing collected packages: tenacity
Successfully installed tenacity-5.0.4

 然後再試一次

(GoogleAssistant) D:\GoogleAssistant>googlesamples-assistant-pushtotalk

 這次程式是可以執行了,照指示按下Enter鍵後讀取語音沒有問題,但是在助理語音回應的部分卻輸出錯誤提示,而且像下面這樣很長的錯誤。同樣爬了文之後發現好像是Python3.9以上才會出現的Bug

D:\GoogleAssistant\GoogleAssistant\lib\site-packages\cffi\cparser.py:163: UserWarning: Global variable 'stderr' in cdef(): for consistency with C it should have a storage class specifier (usually 'extern')
  warnings.warn("Global variable '%s' in cdef(): for consistency "
D:\GoogleAssistant\GoogleAssistant\lib\site-packages\cffi\cparser.py:163: UserWarning: Global variable '__stderrp' in cdef(): for consistency with C it should have a storage class specifier (usually 'extern')
  warnings.warn("Global variable '%s' in cdef(): for consistency "
INFO:root:Connecting to embeddedassistant.googleapis.com
Press Enter to send a new request...
INFO:root:Recording audio request.
INFO:root:End of audio request detected
INFO:root:Transcript of user request: "早安".
INFO:root:Playing assistant response.
Traceback (most recent call last):
  File "C:\Program Files\Python\Python39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\Python\Python39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "D:\GoogleAssistant\GoogleAssistant\Scripts\googlesamples-assistant-pushtotalk.exe\__main__.py", line 7, in 
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\click\core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\click\core.py", line 697, in main
    rv = self.invoke(ctx)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\click\core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\click\core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\googlesamples\assistant\grpc\pushtotalk.py", line 317, in main
    continue_conversation = assistant.converse()
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\tenacity\__init__.py", line 292, in wrapped_f
    return self.call(f, *args, **kw)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\tenacity\__init__.py", line 358, in call
    do = self.iter(retry_state=retry_state)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\tenacity\__init__.py", line 319, in iter
    return fut.result()
  File "C:\Program Files\Python\Python39\lib\concurrent\futures\_base.py", line 433, in result
    return self.__get_result()
  File "C:\Program Files\Python\Python39\lib\concurrent\futures\_base.py", line 389, in __get_result
    raise self._exception
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\tenacity\__init__.py", line 361, in call
    result = fn(*args, **kwargs)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\googlesamples\assistant\grpc\pushtotalk.py", line 123, in converse
    self.conversation_stream.write(resp.audio_out.audio_data)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\googlesamples\assistant\grpc\audio_helpers.py", line 317, in write
    buf = normalize_audio_buffer(buf, self.volume_percentage)
  File "D:\GoogleAssistant\GoogleAssistant\lib\site-packages\googlesamples\assistant\grpc\audio_helpers.py", line 57, in normalize_audio_buffer
    buf = arr.tostring()
AttributeError: 'array.array' object has no attribute 'tostring'

 不過沒事,改個東西就能解決。找到上面紅字標註的audio_helpers.py檔,然後打開它(記事本或VScode都行能編輯就好),照著下方修改:

57    buf = arr.tostring()
改成
57    buf = arr.tobytes()

 最後再試一次

(GoogleAssistant) D:\GoogleAssistant>googlesamples-assistant-pushtotalk

 哈!完美!

不過這個程式還有個小缺點,就是它必須要按下Enter鍵後才會開始讀取語音,雖然實測後發現任意鍵都可以但還是有點麻煩。於是挖出了程式碼稍微修改一下。在剛剛audio_helpers.py檔同一個目錄底下找到pushtotalk.py這個檔案,照著以下修改:

315            if wait_for_user_trigger:
316                click.pause(info='Press Enter to send a new request...')
317            continue_conversation = assistant.converse()
318            # wait for user trigger if there is no follow-up turn in
319            # the conversation.
320            wait_for_user_trigger = not continue_conversation
321
322            # If we only want one conversation, break.
323            if once and (not continue_conversation):
324                break
改成
315            assistant.converse()

對,不要懷疑就是把上面那坨刪掉改成一行。不過這邊建議用註解的方式啦,說不定有天又想按Enter了。然後很重要的是注意縮排!!!!(來自寫C寫習慣換到PY一直很不適應的學生的怒吼)

最後就是,這個語音助理套件並不像手機一樣可以「Ok Google」喚醒,有點小小遺憾。在2019年被棄用的「google-assistant-library」套件有支援,據說還可以更改喚醒詞,不過它已經被棄用了。不過不過呢,樹莓派上還是可以安裝「google-assistant-library」套件喔(„ಡωಡ„),當然是趁還能安裝時安裝好把程式碼複製下來啦哈哈~

註1:先前使用Raspberry Pi4做安裝時使用googlesamples-assistant-pushtotalk指令時會出現以下錯誤:

Traceback (most recent call last):
  File "/home/pi/env/bin/googlesamples-assistant-pushtotalk", line 5, in 
    from googlesamples.assistant.grpc.pushtotalk import main
  File "/home/pi/env/lib/python3.9/site-packages/googlesamples/assistant/grpc/pushtotalk.py", line 28, in 
    import grpc
  File "/home/pi/env/lib/python3.9/site-packages/grpc/__init__.py", line 22, in 
    from grpc import _compression
  File "/home/pi/env/lib/python3.9/site-packages/grpc/_compression.py", line 15, in 
    from grpc._cython import cygrpc
ImportError: /home/pi/env/lib/python3.9/site-packages/grpc/_cython/cygrpc.cpython-39-arm-linux-gnueabihf.so: undefined symbol: __atomic_exchange_8

之後爬到這篇帖子才解決,是要將grpcio這個套件降版本,我是降到1.30.0就解決了。順帶一提這個卡了我半個月,超感謝底下回覆的人(;´༎ຶД༎ຶ`)(雖然他不是在回覆我)

註2:之前做的時候都不需要這個步驟,不知道為甚麼這次就要了耶(大膽猜測可能是之前用的是學校帳號),不然授權的時候會出現授權錯誤:

註3:之前第一次使用googlesamples-assistant-pushtotalk指令都要加上「–project-id <專案ID>」和「–device-model-id <設備ID>」,但不知為何這次居然顯示找不到參數(๑•̌.•̑๑)ˀ̣ˀ̣ 最奇妙的是居然可以正常使用……

(GoogleAssistant) D:\GoogleAssistant>googlesamples-assistant-pushtotalk --project-id assistant-4edfg --device-model-id assistant-5rty8-googleassistant-gt67u
D:\GoogleAssistant\GoogleAssistant\lib\site-packages\cffi\cparser.py:163: UserWarning: Global variable 'stderr' in cdef(): for consistency with C it should have a storage class specifier (usually 'extern')
  warnings.warn("Global variable '%s' in cdef(): for consistency "
D:\GoogleAssistant\GoogleAssistant\lib\site-packages\cffi\cparser.py:163: UserWarning: Global variable '__stderrp' in cdef(): for consistency with C it should have a storage class specifier (usually 'extern')
  warnings.warn("Global variable '%s' in cdef(): for consistency "
Error: no such option: --project-id

註4:這個之前做都沒遇到過【・_・?】

參考資料(一)
參考資料(二)
參考資料(三)
參考資料(四)
參考資料(五)

在 “[GoogleAssistant]把Google助理搬到電腦(Windows)上” 中的1則留言

發表留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *