電子積木(6)LD3320 語音識別模組

No Comments

開發資源與簡介

  • 開發資源就是由賣家所提供的,相當完整。筆者也又補充了些官方說明文件進去,原則上不用怕還少什麼,只要用這包就行了。
  • 並且筆者已將範例程式修改過並試成功後也放進去這包內的代碼資料夾下的 esp8266 那份壓縮檔。
  • 搭配的板子是 esp8266 wemos d1 mini。腳位的連接與設定請參考筆者修改過的範例程式中,有標註與說明。
  • 以下簡介之。
  • 電源,吃 3.3V,且 IO 也只能是 3.3V。不過它內建 5-to-3.3V LDO,故供 3.3V or 5V 皆可。建議就是接 3.3V 即可。
  • 它的 pinout,Vcc/Gnd 除外,有 7 pins,全都得用上。其中腳位 WR 直接接地。關於 MI/MO 腳位對象,程式內也有註明。
  • 故如圖與 d1-mini 對接即可操控。
  • 通訊介面走 spi,官方稱最高頻寬 1.5M;實際跑更高也沒發生問題。
  • 中文轉拼音連結。
  • 中文轉拼音程式。
  • MP3 發音。
  • MP3 發音檔,example。
  • 中文發音。
  • 手動音檔裁切。
  • Freesound。
  • 識別的方式,我們須先建立語音的拼音表。例如我們想識別“小米”,則使用上述連結的轉換工具查出其對應的羅馬拼音字串為“siao mi”,故建立表格加入“siao mi”此字串,依此類推。
  • 以下程式碼,是宣告一個字串的陣列,包含了“小米”及“台灣”兩個字串。
  • 再依其規範的程序傳給 LD3320,我們口語說“小米”或”台灣”便可被識別出來,並接收各自所被指定的 ID 以接續運用。
  • 此 LD3320 支援 ASR(語音識別)及播放 MP3 file。因此當成功識別語音,便可接著播放對應的 MP3,以正確回應使用者。不過 MP3 file 同樣需透過 SPI 即時地傳輸。因此它包含了 ASR & MP3,功能可說相當完整及堪稱是 low-cost solution。
  • 此外,拼音表支援不同的字串對應到同一個 ID,即字串與 ID 是多對一的對應方式。此便能增加識別的準確度/相近音/同一義/都對應到某相同一個 ID。
  • 所載入的表格支援最多 50 條字串,每條字串最多十個漢字或 79 個字節的拼音串;字元使用小寫。並且所對應的 ID 不用連續;但值介於 0 至 255 之間。
  • 其中要注意的是,每一次識別過後,不論有沒有成功,都要重傳此表,以進行下一次的識別。
  • 另外,表格中的第一個元素是沒有作用的/或許是 bug,故必須冗餘之/將它佔掉/若傳大串更是冗餘/單字元即可。
  • 建議可查詢 LD3320 數據手冊,便可了解所提供之功能全貌。
char *table[]={
    "x",
    "siao mi",
    "tai wan"
};
  • 缺點,
  • 充其量,它就是取樣語音後,來與拼音所成的 waveform 做比對而完成的識別。因此很顯然的,相近音,則必會被識別出來。
  • 例如,上表中,若我發音是“湘妹”,則“小米“必然被識別出來。甚至於偽陽性不低單看其貪婪演算的程度。
  • 總結,
  • 使用語音辨識,於此筆者是希望做到能夠”對話“;看到了前二篇文章了嗎?中文轉語音,正是不可或缺的另一半組件。而前一篇文章,就是為了能夠較容易使用階層式的表格,例如我問 A,其為第一階層,則識別出後它必須回答,其出自於另一份表格,階層 i,我們可以依據第一層的內容來取決/跳到第幾層,以此類推。當然,我們於此也只能做到劇本型的即全盤設計過的內容,而非 AI 型的。
  • 換個方式說此模組的確是語音識別;AI 型的,則是多用了顆心,語意識別。

範例程式初探

  • 上述提到表格的第一個元素是無效的,並不然。它程式有去錯開所致。改正確即可。
  • 並非每次都須從 init 開始。不過,在㧅換 MP3 or ASR 時就要從 init 開始。
  • 有兩種做法,看 spec 重寫,尤其又是中文的,當是首選才對,不過筆者先選擇第二種分析他的範例程式。若因而花太多時間或分析不下去再選擇回第一種。因此首次改的程式如附;很亂就是了。。。

整理好的範例程式

筆者結果還是搭配了說明文件及程式碼一起看;故除了 MP3 的部份沒使用上之外,其他的應是沒有疑漏什麼了。
故最後已整理得相當乾淨與幾近完整了,並且在 LD3320 的指令後也都加上了註解了。
它必須使用前篇文章的 Tables.Lib。

整理好的完整的範例程式

  • 連同 MP3 的播放也全都搞好了,
  • 換言之,只要錄好回應的語音檔並且轉成 MP3,放到 MCU 的 flash file system 上,便可透過(與控制)相同的 spi 介面驅使 LD3320 播放,即,是透過填 ld3320 fifo-data register 來實現傳輸 mp3 data 的。
  • 所以只要單這片 LD3320,加上 MCU,便成一個互動性完整的語音控制模組。應是說,搭配 ESP8266 的 ESP12E 有 on board 4MB flash,足夠存放相當多的 MP3 檔案。真的是相當 low-cost solution,並也可稱相當小巧。
  • 而 MP3 files 筆者使用 32kbps,才能播得順,若使用 128k,會大量間斷;是可嘗試透過改變填 fifo 的方式來看看能不能播得順不過此點筆者沒需求就不再嘗試了,不然,若真可行,那此 solution 還可充當網路 mp3 播放器或線上 radio/網路串流播放器。
  • 此份程式筆者有加上 IoT 的扣,除了啟用網路功能外,也主要為了輕易地透過 http 將 mp3 files upload 到 esp8266 的 flash 上。
  • 而此份程式都是對應官方的範例程式修改來的,故,嚴格講所有函式與架構仍有不小的重整空間,來模組化與更易明白程式的運作方式。
  • 但目前能用就好,應是不會重整或改版了。
  • 後續若有改版置於此,純是筆者自己的應用。
  • 若當筆者的應用告一段落,再來錄 demo 影片。

非常重要

  • 在程式中有一支 LD3320MP3 的類別,當中有建構子是 LD3320MP3(const char *str) 的形式,
  • 而在使用時,可以如此使用,
  • LD3320MP3(“sample01.mp3”);
  • 這表示建立一個暫時物件,並傳入 “sample01.mp3” 此字串的左值,且由區域變數 str 所接收,故處理無誤。
  • 然而,假設有此一字串指標 const char *error=”sample02.mp3″;
  • 那麼我做如此呼叫,則出錯,
  • LD3320MP3(error);
  • 因為,它將建立暫存物件,或許稱為 error,並且建立此參數型別的暫存變數(其值未初始化)或許同樣叫 error,再傳入,則我們將參考不到意欲的變數。
  • 因此,該如此做才行,
  • LD3320MP3 tmp_obj(error);

花很多時間除錯才有這版

  • 因為珍貴所以先放上來,只有函式庫檔。
  • 範例檔將在完成後再結合起來再改版。不過本版內容應都齊全了,在整個架構都重新編排的前提下。。。因此很多爸哥才蜂湧出現。。。
  • 此版真的抵定了。。。

再來這一版是很多問題的 0.4 版

  • 要完善,就差一步之遙了,但這一步的距離卻是很長很長。娓娓道來,
  • 目前都僅提問題,尚無想法。
  • LittleFS,筆者原意就是要用時打開不用時關閉,故一直以來都這樣用它,但看來它並不適合這樣用,故若要配合它可能用到的地方都要改掉了。
  • 此 LD3320,有 mp3,asr 兩大功能但同時也是問題起源,簡單講,
  • MP3 loop,ASR loop,main loop,ISR loop,file producer,file consumer,多方協作下,很難不出錯,我是說,當前版本的程式碼,並未對狀態做有效的控管,即,未有獨立的狀態變數於 mp3/asr 之間,及之於 files,及之於全域等,故,簡單講一個錯誤例子,
  • 檔案已全載入完,但仍在播放,便跳到 asr 執行而寫了某些暫存器(或做了 reset),致 mp3 無法完整播放完而被中斷。經驗證 fifo 已經清空也發完中斷了/還在播放,即便使用 dsp busy state,仍無法阻擋此狀況。故此該歸為 ic 短板了。
  • 因此,0.4 版的架構還不夠正確,當需再改架構並引入更多狀態變數以控管。
  • 本版已做到 mp3 連續播放模式,實測下其原只能每播一首就得實體重置才能播下一首(否則聲音音量異常)。故 workaround 方式便是在餵 data 的地方不間斷地送下一個檔案的資料。以致能做到例如有三個英文單字音檔,how.mp3,are.mp3,you.mp3,接續播放成一個句子 how are you。
  • 筆者用了好半天時間,抓取了常用的 290 個英文單字,如上連結,只有 2MB 左右足以置入例如 ESP-12F。
  • 故我們能做任何常用的英文回應,例如,我說,Mary,則她則回應,yes,what can i help you。當然當前版本還未作拆字對檔的部份,之後會做,但眼前問題是,
  • 這些單字音檔有尾空,即 how 完後會留一些空白時間,故組合起來,非常地不口語化,解法就是有程式能批量地截掉這些空白。則將再好不過了。故此一問題仍待解中;再來每個檔還要批量轉成更小的 32kbps 以更合用。
  • 再一個實際的問題是,每次從 littlefs 取一個 byte 出來填 fifo。雖然 fs 會預取 current block,所以若干個 bytes 後才會做一次 flash 的讀取。然而,已發生 fifo 餵不完,或說餵的速度低於 fifo 消耗的速度。且經查,lfs_file_flushedread,資料是由此取出,其需若干的處理時間,故,我們把資料 buffer 起來將會是有可觀的幫助的。
  • 以上,故先停頓休息了,單字音檔的問題要先解決。
  • 對了,已改好可同時上傳多個檔案(以可一次上傳 290 個 mp3 檔案),uri 是 /upload。但肯定會出錯,則網頁會提示在哪個檔案出錯,再從那個檔案與剩下的都再重傳即可,比照此類推(因後續會一直有出錯)。而問題應是出在上傳的函式庫。
  • 結果,話說完才沒兩三個小時,音檔的問題就解決掉了。
  • 處理的指令,不規則地列於以下程式片斷,自行取用。
  • 透過修改這個參數,start_threshold=-25dB,來截掉空白。
  • 並再附上處理好的英文單字音檔;雖沒有搞成 32kbps,但現在已只有 1.1 MB 了,若往後還有空間問題來再瘦身。
  • 句子的朗讀的順暢度,真的可以打上六十分了,堪用了。
#!/bin/sh
find *.mp3 -type f | while read FILE
do

# echo $FILE
# echo

# from -25dB to -35dB.
# echo $(echo ffmpeg -i $FILE -af silenceremove=start_periods=1:start_silence=0.1:start_threshold=-25dB,areverse,silenceremove=start_periods=1:start_silence=0.1:start_threshold=-25dB,areverse x$FILE )
# echo

# $(echo ffmpeg -i $FILE -af silenceremove=start_periods=1:start_silence=0.1:start_threshold=-25dB,areverse,silenceremove=start_periods=1:start_silence=0.1:start_threshold=-25dB,areverse x$FILE )

# rm $FILE

# mv $FILE $( echo $FILE | sed 's/--_gb_1//g' )

# mv $FILE $( echo $FILE | sed 's/_//g' )

# mv $FILE _$FILE

# mv $FILE $( echo $FILE | sed 's/^x//g' )

# note: use command "mediainfo" to inspect format.
#
# https://stackoverflow.com/questions/42947957/how-to-convert-high-bitrate-mp3-to-lower-rate-using-ffmpeg
#
# convert to 32k; since some of the ffmpeg/finds file, may failed, rerun the failed ones is fine.
# so the following 2 lines sould be run together. move new-gened files to other folder before re-run.
#$(echo ffmpeg -i $FILE -codec:a libmp3lame -b:a 32k y$FILE )
#rm $FILE

echo
done

20220628 除錯註記

  • 曾有人問過該如何除錯,當若沒有任何軟硬體除錯工具的話;那麼答案就是使用 print。
  • 當前除錯的結果程式碼附了出來,用了相當大量的 print,足顯筆者的程度 XD,但更足顯此 bug 之不食人間煙火之乖張氣焰。而程式碼有秀出 bug 在哪嗎,並沒有,因為這是一隻隱形空𤫊的 bug,所以程式碼不得不留存以紀念之;而能碰到 bug 還得看運氣就是了/即不同時間不同平台不見得能跑出 bug。我真的有幸遇到才能突破此一盲點。
  • 還有更重要的一點,即便抓到了 bug,依舊沒有解法/非 limitation,或是沒有好的解法。
  • 以下註記幾點,
  • 直接餵 fifo 空資料,2032 bytes,抱著這疑問好久,不過現在知道了也沒幫助。
  • 若生産者速度快於消費者,則程式運作如所預期,fifo 滿後退出等待中斷再來填飽肚子。
  • 若生産者速度低於消費者,則會頻繁發中斷,當然就會去備資料來餵,甚至於最差的情況會一直停留在生産階段過久而 wdt-reset,這也是可預期的。
  • 程式原本是單檔模式,或說官方模式,上面兩點都不構成問題。
  • 改成連續模式後,問題就浮現了;即一檔播完即在餵 data 的地方再開一檔接著餵。
  • 先說官方模式是,開檔,餵 data,啟動播放 mp3,開啟 fifo 中斷。若中斷發生,會在 isr 內再餵 data。
  • 連續模式就是在餵 data 的地方持續一檔接著一檔開,來餵 data。
  • 問題來了,主程式初始化 mp3 play 會去餵一次資料,再打開中斷。但主程式接續開檔以致於取到 data 這中間費時過長(時長時短並非是問題)致中斷發生,那麼,同一個餵 data 的函式,被 isr 重進入。筆者就是納悶,”nice to meet you”,為何 you 會被唸了兩次,才發現這隻 bug。該強調的,此處,程式怎麼寫本身,並不是 bug,此 bug 正是 isr 重進入某函式,而恰恰此函式,main loop 也同時會去存取;衝突便是於此。
  • 故讓我們返回來看,官方模式沒有問題嗎?是的,它僅在開啟中斷前 main loop 才會存取此函式。
  • 因此,筆者在就現有架構下去改成連續模式,靜態邏輯上看起來是沒有問題的,但卻忽略了動態行為上 isr 的觸手所及之處有無衝突。
  • 解法如此 debug 所試, main_busy,flag 去排開 main loop & isr 的衝突。但此屬 workaround。
  • 另一解法,main loop 首填 data 時只填 0 或不填,而讓中斷來處理所有開檔及資料等,但失了一次填資料的機會,非好方法。
  • 再不然就是改架構了但,main loop 本就該填一次 data,或說 main loop 接續開檔不能等 intr 發生再處理,而 isr,本就該處置即時的 data access,阿這樣不就先決衝突條件就已先立在此了?就再想想了。。。

20220702 終於破關 LD3320 lib v.0.6

  • 搞了好久,除錯了好久,這次真的把 LD3320 破關了。即,能用的功能與基礎應用都做完了,測試過都沒問題了。
  • 當中,main loop & isr 的衝突問題,筆者也只能採用 flag 去避開了。否則可行者應該就是整個架構重新來過了。
  • 另外,有聲音被截尾的問題,已 workaround 成追加尾部靜音,若被截掉就是那段多餘的靜音被截。
  • 事實上跟官方查過我也自驗過,確實有 dac out end 的狀態可查,也可解決問題;但用了它,接續會發出例外中斷,原因未知,故便沒用它了,讀者可自行嘗試或許有哪些地方筆者沒顧慮到。
  • 聲音檔的部份,已全收錄在這版 v.0.6 的函式庫內了,已纍計有 372 個檔案了,仍不到 1MB。故可應用到 esp8285 上,但,由於 littlefs 的 block size is 8192,故,此不到 1MB 的所有檔案放到 littlefs 上會增胖成 3MB,當前已塞不下了,只能求 esp8266::littlefs 能支援更小的 block size 了。故,轉圜做法就是刪去那些沒有使用的檔案了。
  • 我們在程式中讓 ld3320 發聲的文字收集在一個陣列內,故用以下指令將它轉成逐列的檔案,便可輕易將這些檔案移至目的資料夾。
cat 1.txt | grep -o "\w*[[:alnum:]]\w*" | sort -u | sed 's/\([[:alnum:]].*\)/_\1.mp3/'
  • 此版的範例也全都完善了,故此函式庫此版應是終版。
  • 接續我會改成自己的應用並放上來,再來放些 demo。
  • 案!前面所有已知問題全都解決了!案!破關了!終於可讓它好用實用開心用了!!!
  • v.0.6-1:改了一隻小爸哥。及加入有使用到的兩支 libs;其在前面的文章有完整版。

20220704 v.0.7

  • 此版加入了範例二,使用語音識別,也是真正的重點。
  • 之後會將兩支範例放在一起,並試著抓出/解掉那隻已知的 bug,即偶發,狀態會回到 idle 不再進工作模式。
  • 事實上本版,功能已完成筆者所預期的。除非想到什麼新的。不然都是小修而已。
  • 至此我好像沒強調它的什麼使用感受,因此,以這支範例二來講,我僅一個字做感想。。。
  • amazing!
  • 因為要我真人發音來操作,所以 demo 遲遲未錄,不然,豈是 amazing 能夠形容。。。哈,自吹自擂 XD

20220705 LD3320 Lib v.0.8 all-included

  • 收錄兩支範例及所有 MP3 files,另兩支用到的函式庫也收進來了。該有的全都在此版。
  • 並在(wemos d1 mini 及)ESP-M3 上測試過了,請注意本版的設定是 for M3 的。
  • 設置 in-isr 的,本就會 wdt-reset 若連續模式播放過久的話,但在此範例中是關掉 wdt 的所以可以盡情撥放/非正常的應用;請特別注意。
  • 設置 not in-isr 的,會偶發脫離工作模式,算是 bug,但除非很常出現不然不打算修正了。
  • 除此之外,
  • 這一包完整的 solution,應無人能出其右了 XD,非自信而是投入太多時間在這上面了不給自己打氣說不過去。
  • 最後應是要錄放 demo 的時候了。
  • 若再更新應只是放自己的應用而已。。。應沒東西改版了。。。例如調整𤫊敏度等,再說了吧。。。

demo

  • 請注意,我用上了 wemos d1 mini pro,flash 是 16MB,淘貨,已經使用過三片,全都出現問題,而問題該歸屬於誰,全然未知。
    因此,建議最好找非淘貨最常見的版本試試。
    附註,它使用上 hdm 的 ch9102,我看來是沒有 ch340 佳。flash 是 winbond 的應不成問題。猜測是電路設計問題。(p.s. 不過後來對其中一張用熱風槍吹一吹後就會正常。。。一陣子。。。)
    (再更新,)
    ch9102 & ch340 的比較是不對的;應是尚無結論。現在已確定之所以 ch9102 在燒錄時會停留那麼久,乃因 16MB flash 的 erase 動作,當然是更久了。
    其次,筆者這次買的 d1 mini pro 16MB version,四片中就有三片出現問題,且當中一片一開始就有問題,加熱與補焊都無法解決;隔了一天,再次插上使用居然好了。。。因此同前結論,避用 d1 mini pro
    當然,講得這麼乾脆,就是有腹案了也試成功了:
    使用 d1 mini 4MB version 或 ESP-12F,將屏蔽蓋摘除,將 4MB 換成 w25q128/sop8/SPI,即 16MB/最大支援。
    因筆者用過的 d1 mini 4MB 版本除了自己玩壞沒遇過質差的。
    故如此不僅不再有上述問題,看來花費上也比較少;1 顆 16MB 淘貨約 4 RMB 上下。
example01

20220709 update

with ESP-M3-1
with ESP-M3-2
with ESP-M3-3
with ESP-M3-4
example02
  • 對了,怕忘記強調幾點/但應沒 juice 了,
  • 最好還是有機會可以改 littlefs 的 block size,它為了 wear leveling 而設為 8192;但我們並不常寫 flash/除了那支 settings。不然每支聲音檔都遠小於 8k 超級浪費,也限制了小 mcu 的使用。
  • 在辨識與播 mp3 間,反復切換,其實是很快的。之所以會有延遲是因加了 sl 靜音檔。因此還是有需要再試試 dac-out end flag 的畢竟每一次切換都會做一次 hard reset/mp3,soft reset/asr,所以例外中斷應是有機會排除的,若然將變得感覺更有質感些。
  • 使用 in-isr 並不劣於 not-in-isr,前提只要連續模式費時不能太長或 file buffer 開大點(<=最大檔)仍有機會避掉 wdt reset/或單試過最長句子跑過不 wdt-reset 應就安心了。範例中關掉 wdt 那段本就該拿掉。
  • 短板,mic 在嘈雜環境下,整個板子等同廢板;當然,非板子之過而是 mic 當必須降噪處理而這是硬體上必須加鞍的。故實際拿此板在戶外使用就別太意外結果。。。當然此點就非筆者能力上所碰觸得到了XD
  • 而此場合下此板有盡責地做濾波處理/找有效段,故它識別時間會花費/設定是六秒,六秒才給出較佳結果故此時別急著重講會變得一直沒回應。過六秒再重講。
  • 故扣有啟用𤫊敏度,但別期待其助益。原則上𤫊敏度設大收音設小,亦反之。但場合運用下不與之對應或說效果非預期。
  • 事實上後來筆者又淘了兩片降噪模組,待取得後再來玩玩看。
  • 第二層已接近 50 條,當加個設定層有些可納入以可增益每次識別的成果或時間。筆者的 tables lib 雖是線性但仍可化簡樹枝狀結構。
  • tables 本身的部份,目前是使用 sentences 與 functions 一對一對應,但實際狀況多是不同的 sentences 對應同一個 functions,故簡單講 functions tables 是可以化簡的以大大節省記憶體;待實務上 tables 長一點再來修改。
  • 前面提到的 tables 的進化/存於 flash,也需實現。
  • 下次改版要補單字元“%”。
  • 註記,如圖筆者的測試方式使用杜邦線連接,姑且是可用/即他人或許可能嘗試失敗。簡單講,當 SPI 頻率愈高,線材及線長將愈深影響 SPI 通訊的成功與否。反之降低頻率使導線的變因降低。而前面提到的可能存在的 bug 偶爾會回到 idle 不再在工作模式,或許有可能是因為 SPI 通訊出錯而導致。

20220730 更新

  • 加入了降噪模組,
  • SPI 使用的線長如此板子,不得不有一定的長度,因此便會發生通訊失敗的機會。軟體的避開方式為/試著讓處理速度低些;開啟 UR;使用非 ESP8266 的快速 IO 模式;取消使用 IRAM_ATTR 等。結果是此三樣都做才有改善。
  • 當 wemos d1 mini & ld3320 合用時,若電源 5V 進來,則若 5V 同時供給此二板,將造成 esp8266 bootup 失敗。因此只能讓 ld3320 使用 wemos d1 mini 的 3.3V 才不會出現此問題;當然如此使 LDO 負擔很大不過 so far so good。
  • 降噪效果時好時壞;好的時候非常好,普通環境下,距離可長達約兩米。不好的時候常常近距離/二十公分,也有約三成的比不使用降噪差。最差的時候,即,拿熱風槍在旁干擾,則完全無辨識效果/猜測降噪的輸出純然是單一聲頻或被濾成無聲輸出。
  • 降噪的輸出是 20K-Ohm,2V + 2Vpp,mic 輸入是 300K-Ohm,150mVpp。目前直接串接至 ld3320 mic 看來是 ok 的;已試過 line-in-mono 一路無作用,目前唯 mic 一路可用。
  • 這顆降噪模組還比 ld3320 貴些,不過確實有效果。另有稍微試了另一顆約 1/4 價格的降噪不過沒結論/應是我阻抗匹配沒弄好。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

PHP Code Snippets Powered By : XYZScripts.com