“智能家居”很早就出現在大家的視野中,但目前大部分還是停留在概念階段,如何讓智能家居真正的落地開花呢?我想人工智慧的出現必然會打破這種現狀。 作為一個開發者,你是不是還在糾結如何能夠用語音控制你的家居產品呢? 比如,用戶說:我回家了。 你可以幫他打開燈、打開空調。 ... ...
ZigBee作為一種短距離、低功耗的無線通信區域網協議,其優點是超低功耗、安全性高和自組網,並且可容納多個設備,因此在智能家居控制中占有很大的優勢。
但是,僅僅使用ZigBee技術來控制家居設備顯得比較單薄,或者不夠“智能”。
比如,用戶說:我回家了。
你可以幫他打開燈、打開空調。
用戶說:來點浪漫的氣氛
你可以給他打開音箱,情景燈調整柔和的狀態。
要實現這些功能,需要經過錄音->語音識別->語義識別->根據語義輸出命令給硬體->硬體執行命令的過程。每個過程的實現都不是那麼容易。
而隨著人工智慧、語音識別、自然語義理解的發展,語音控制智能家居將成為可能,但目前為止,大部分還處在概念階段或者開源的不是很多,如何讓智能家居真正的落地開花呢?本實例將做一些探索。
這裡會以window java應用程式為例,講解如何通過語音識別控制智能家居,並輸出ZigBee3.0協議,也很方便和ZigBee協調器進行對接,實現語音直接控制硬體。
下麵詳細介紹程式的功能和代碼實現,希望語音、語義理解今後能廣泛的應用在家居等控制領域。
1. 代碼下載
註:下載代碼後請仔細閱讀說明文檔。
APP 測試請查看第3節。
2.功能分析
2.1 APP 工作流程
APP的工作流程如下圖所示,圖中虛線框部分均由OLAMI開發平臺提供,後面會具體介紹OLAMI開發平臺的使用方法。
其餘部分由APP來完成
- 語音輸入:
OLAMI的語音識別支持兩種格式:
WAV 格式的 PCM 錄音數據,單聲道(mono)、16K 採樣率(16 KHz Sample Rate)、16 bits 位深(Bit
Resolution)。Speex 音頻壓縮,節省數據傳輸量,壓縮參數:Wideband 模式、Quality(壓縮比)= 10、單聲道(mono)、16K
採樣率(16 KHz Sample Rate)。
首先要確保硬體設備沒有問題,可以進行正常的語音錄入。在電腦上安裝好麥克風之後,在“開始菜單”中輸入“錄音機”。
然後在彈出的錄音機中點擊“開始錄音”,使用話筒錄音後點擊“停止錄音”後會彈出保存錄音結果的對話框,保存,聽聽聲音正常即可。當然,也可以使用QQ等第三方測試麥克風的軟體。
確定硬體設備無誤之後,只要通過javax.sound.sampled.TargetDataLine調用windows錄音功能,錄下符合OLAMI語音識別介面的聲音數據即可,我的錄音方式是一邊錄音,同時將原始數據通過speex壓縮的方式post給 OLAMI 語音識別的API介面。不是保存為wav文件之後再上傳,這樣能夠提高語音識別的效率。
文字輸入:
文字輸入即直接文本輸入,比如“打開空調”,“把彩燈調成紅色”。
處理NLI輸出:
即根據OLAMI NLI的語義輸出結果決定如何操作設備,比如當輸入為“打開燈”時,我們可以收到如下JSON數據:
{
"data": {
"asr": {
"result": "打開燈",
"speech_status": 0,
"final": true,
"status": 0
},
"seg": "打開 燈 ",
"nli": [
{
"desc_obj": {
"status": 0
},
"semantic": [
{
"app": "smarthome",
"input": "打開燈",
"slots": [
{
"modifier": [
"open"
],
"name": "control_obj",
"value": "燈"
}
],
"modifier": [],
"customer": "593664ad84ae0a0a3feec056"
}
],
"type": "smarthome"
}
]
},
"status": "ok"
}
slots中的“control_obj”即要操作的設備,上面的結果可以看到需要操作的設備是"燈",動作為"打開"。應用程式根據這兩個信息就可以在自己的設備中尋找“燈”這個設備,併發出“打開”命令。
- 輸出ZigBee 3.0 協議:
根據NLI的輸出我們可以判定要控制的設備是燈,而燈的cluster我們選擇了ZCL_CLUSTER_ID_GEN_ON_OFF, 根據這個cluster以及等的device ID等輸出命令即可。
連接硬體:
這裡沒有提供驅動硬體的代碼,但基本流程就是,將ZigBee協調器的開發版通過串口和電腦相連,軟體發出的命令經串口發送給協調器,再由協調器控制ZigBee協議即可。
2.2 APP功能
- 文字輸入:
通過設備選擇可以切換不同的例句。同時,可以在例句的框里輸入其他控制語句,按回車可以重覆輸入。比如:“請幫我打開燈”,“燈給我打開”,“開一下空調”,“空調的溫度提高一點”
- 語音輸入:
點擊”開始錄音”,如果沒有點擊“停止錄音”,3秒之後會自動停止錄音。如果在這之前點擊了“停止錄音”,那麼會及時停止錄音,併進行語音識別。
識別後的文字會顯示在按鈕的上方,如下圖所示:
- 設備模擬:
如上圖所示,應用程式中會模擬彩燈的顏色和空調的溫度、模式、風力,其原理就是根據輸出的Zigbee3.0協議進行顯示。
- 命令輸出:
即輸出ZigBee3.0的協議。下麵列出例子中的幾種設備的協議信息
燈
功能:僅支持打開和關閉
Device Dype: 0x100
命令:
Cluster ID: 0x0300
Cluster ID 的TI定義:ZCL_CLUSTER_ID_GEN_ON_OFF
actionID | Action_frame(1 bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 無 | Off,關閉 |
0x01 | 0x01 | 無 | On,打開 |
彩燈
功能:打開,關閉,顏色調節(例子僅支持紅、橙、黃、綠、青、藍、紫),氛圍調節,色調調節。比如運動氛圍、浪漫氛圍、冷色調、暖色調等。
Device Dype:0x0102
命令:
Cluster ID: 0x0006
Cluster ID 的TI定義:ZCL_CLUSTER_ID_GEN_ON_OFF
actionID | Action_frame(1 bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 無 | Off,關閉 |
0x01 | 0x01 | 無 | On,打開 |
Cluster ID: 0x0300
Cluster ID 的TI定義:ZCL_CLUSTER_ID_LIGHTING_COLOR_CONTROL
actionID | Action_frame(1 bit ) | 參數組 | 說明 |
---|---|---|---|
0x08 | 0x01 | Attr1,Attr2 | (均為int16,即兩個位元組,數據格式編號為0x29 ) 設置彩燈的顏色,即R,G,B值。 |
第一個參數的高八位表示R值。
第一個參數的低八位表示G值。
第二個參數的高八位表示B值。
第二個參數的低八位無意義。|
電視
功能:打開,關閉,提高降低音量,換台,
Device Dype:0x0006
命令:
Cluster ID: 0x0006
Cluster ID 的TI定義:ZCL_CLUSTER_ID_GEN_ON_OFF
actionID | Action_frame(1 bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 無 | Off,關閉 |
0x01 | 0x01 | 無 | On,打開 |
0x05 | 0x01 | 無 | 提高音量 |
0x06 | 0x01 | 無 | 降低音量 |
Cluster ID: 0x0008
Cluster ID 的TI定義:ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL
actionID | Action_frame(1bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 1個位元組,uint8 | 切換頻道 |
0x01 | 0x01 | 無 | 切換下個頻道 |
0x02 | 0x01 | 無 | 切換上個頻道 |
空調
功能:
開關功能,即打開和關閉空調。
切換模式,順序為“自動-製冷-除濕-送風-加熱”模式按順序迴圈切換,但不支持某個模式的設置。
風力切換,切換順序為“自動-低速-中低速-中速-中高速-高速-超強”。
其中,製冷和制熱模式支持上述7種風力切換。
送風和自動模式沒有“超強”風力
除濕無風力調節。
註意,僅支持切換,無風力設置。
升高溫度,切換一次,溫度上升一度,基礎範圍是16-30.
降低溫度,每切換一次,溫度下降一度,基礎範圍是16-30
狀態查詢,開關、溫度、風力、模式查詢。
Device Dype:0x0102
命令:
Cluster ID: 0x0300
Cluster ID 的TI定義:ZCL_CLUSTER_ID_LIGHTING_COLOR_CONTROL
actionID | Action_frame(1bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 無 | 切換開關 |
0x01 | 0x01 | 無 | 切換模式 |
0x02 | 0x01 | 無 | 切換風速 |
0x03 | 0x01 | 無 | Setup Button |
0x04 | 0x01 | 無 | Setdown Button |
窗帘
功能:
打開,關閉,停止運行,指定窗帘運行的位置。
Device Dype: 0x202
命令:
Cluster ID: 0x102
Cluster ID 的TI定義:ZCL_CLUSTER_ID_CLOSURES_WINDOW_COVERING
actionID | Action_frame(1bit ) | 參數組 | 說明 |
---|---|---|---|
0x00 | 0x01 | 無 | 打開 |
0x01 | 0x01 | 無 | 關閉 |
0x02 | 0x01 | 無 | 窗帘電機停止移動,並返回當前位置的百分比 |
0x04 | 0x01 | 窗帘號房間號,2bytes | 設置窗帘號,房間號.其中高8位是房間號,低8位是窗帘號 |
0x05 | 0x01 | 百分比 | 單位百分比 |
其餘設備和感測器的ZigBee 輸出協議不再一 一列出,可以直接在APP中測試。
3 APP測試
代碼下載解壓之後,可以在根目錄找到 smarthome.jar,在windows7 環境下雙擊即可以運行。
應用程式支持的語料除了選項里的,其他的相似說法也支持。
4 APP源碼解析
4.1 OLAMI語法載入
因為APP調用了OLAMI的自然語言理解介面,所以首先是必須先寫語法,來匹配智能家居控制語句。比如:“打開燈”,“幫我打開空調”,必須在完成語法之後,才能從OLAMI的介面中獲取NLI結果。語法相關定義和寫法等請參考博客:告訴你如何使用OLAMI自然語言理解開放平臺API製作自己的智能對話助手
如果你希望修改語法,添加更多的句子支持,必須將語法文件導入到歐拉蜜NLI系統。
下載包解壓之後,根目錄找到smarthome.osl,這個就是智能家居支持的語法。然後註冊並登錄歐拉蜜官網,在自己的賬號下找到“應用管理”,併進入NLI系統。如下圖所示。
接著新增模塊,並將智能家居語法smarthome.osl導入,如下圖所示,點擊“新建”並輸入APP的名字“smarthome”,這個名字必須與smarthome.osl的名字相同,否則導入時會報錯。當然也可以修改,但同時要修改smarthome.ols中APP name相關欄位。
模塊創建之後,選擇“上傳OSL文件”,然後選擇smarthome.osl並確認即可。上傳成功之後會進入該模塊內部,然後在例句庫中可以看到很多智能家居控制的句子,同時也可以查看Grammar,Rule等。至此OLAMI語法載入完畢。
4.2 獲取應用程式的APP KEY和APP Secreat
如果希望獲取句子解析後的結果,必須在歐拉蜜平臺中創建自己的應用程式,名字任意,我的叫smarthome。
回到“應用管理”界面-----創建應用程式。
應用程式創建成功之後,還需要把剛纔創建的smarthome 語法模塊添加到應用程式中,一個應用程式可以支持多個語法模塊。
點擊圖中的“測試”,輸入“打開燈”,就可以看到JSON格式的語義輸出結果了:
語法模塊配置好之後,點擊應用程式的”查看Key”的按鈕,可以看到平臺分配的APP Key和APP Secret.
4.3 源碼分析
源碼工程是smarthome_source_code.jar,解壓之後,添加入Eclispe工程,我的開發環境是JDK1.8.
Eclipse Version: Mars.2 Release (4.5.2).
註意:導入工程後,如果出現文字報錯,請將預設編碼修改為UTF-8,方法 Project->Properties->Resource
代碼結構:
替換KEY
在smarthome packge中的NLIProcess.java中,替換之前創建語法應用時的APP Key和APP Secreat:
// * Replace your APP KEY with this variable.
private static String appKey = "*****your APP Key******";
// * Replace your APP SECRET with this variable.
private static String appSecret = "****your APP Secret*****";
程式入口:
程式入口為smarthome packge下的window.java,可以安裝windows Builder插件,直接操作界面。
smarthome packge:
smarthome包里的源碼包括了APP應用的基本框架,其中:
window.java為APP入口,即界面。
NLIProces.java表示處理來自OLAMI NLI介面的語義結果.
錄音處理為:getSemanticBySpeech()
文字處理為:getSemanticByText(String inputText)
windowVariable.java是window.java和NLIProces.java的數據傳遞媒介, window.java中會將NLIProcess.java 需要的控制項傳過去:
private void initialize() {
nliwindowdata.setCmdTable(cmd_table);
nliwindowdata.setcolortext(color_text);
nliwindowdata.setAnswerText(answer_Text);
nliwindowdata.setModetext(mode_text);
nliwindowdata.setTempetext(tempe_text);
nliwindowdata.setVoicetext(voice_text);
nliwindowdata.setWindtext(wind_text);
nliwindowdata.setisRed(isred);
nliprocess.SetAnswerConfigCom(nliwindowdata);
........
smartHomeApp.java用來處理智能家居APP的語法解析和命令輸出。是NLIProces.java中其中一個小模塊。 你還可以在NLI處理中添加其他處理模塊,比如天氣查詢、詩歌背誦等等。目前NLIprocess.java中僅處理了smarthome相關的NLI輸出:
private void ProcessNLIResults(NLIResult[] nliResults) {
// TODO Auto-generated method stub
String answer="對不起,你說的話我還不能理解";
boolean isnormal=false;
for(int i=0;i<nliResults.length;i++){
NLIResult tempNlI=nliResults[i];
//tempNlI.
//voice_text
if(tempNlI.getType()!=null&&tempNlI.getType().equals(nliDefinitions.smarthome_app)){
.......
APPSlotEntry.java----處理NLI返回的JSON數據中slots相關信息
OutputMap.java------存放smartHomeApp.java返回給NLIProcess.java的輸出語句和命令。
Smarthome.definition packge
該包是智能家居處理中用到的定義和設備狀態解析。
Demo中模擬了燈,彩燈,電視,空調,感測器等設備,初始化數據見smartHomeApp.java的InitDeviceData()。
所有設備信息通過ClientHomeAutomation.java解析並存儲。
//key is deviceID
Map<String,HomeAutodeviceObjectNew> addedDeviceMapNew=new ConcurrentHashMap<String,HomeAutodeviceObjectNew>();
****Smarthome.util****
DataBuffer.java 和Microphone.java用來進行麥克風錄音;錄音格式按照歐拉蜜平臺的要求,參數為16位深採樣率,16KHZ頻率,單聲道。
源碼為
public Microphone() {
this.sampleRate = 16000;
this.bigEndian = false;
this.signed = true;
this.desiredFormat = new AudioFormat
(sampleRate, 16, 1, signed, bigEndian);
//this.closeBetweenUtterances = closeBetweenUtterances;
this.msecPerRead = 100;
//this.keepDataReference = keepLastAudio;
//this.stereoToMono = stereoToMono;
//this.selectedChannel = selectedChannel;
//this.selectedMixerIndex = selectedMixerIndex;
this.audioBufferSize = 9600;
recorderData = new DataBuffer();
}
麥克風的錄音開始和停止通過線程監控完成。直到沒有聲音錄入時,錄音線程才會觸發錄音停止機制,因此希望停止錄音時必須通Microphone.stopRecording()關閉錄音,程式才能停止錄音。
因此錄音時最好設置預設的錄音時長或者通過標誌來停止錄音,並調用Microphone.stopRecording(),我這裡的預設錄音時長為3s.
代碼見NLIProcess.java的
//最多錄3秒數據,因為採樣頻率是16000點每秒,每個點占兩個位元組。
// readcount<=0表示錄音結束
int num=0;
int srcint=0;
while(readcount > 0 )
{
if(total_count >= 48000*2||needstop)
break;
num++;
System.out.println("數據"+(num+1));
total_count += readcount;
System.out.println("單數"+readcount);
speechrecoginzer.appendAudioFramesData(databytes);
readcount = mic.getData(databytes, 0, temsize);
}
mic.stopRecording();
WaveFileWriter.java可以為錄音數據添加wav頭。
5 和硬體設備對接
和硬體設備對接,需要串口或者USB等將輸出的ZigBee協議發給協調器,由協調器控制各智能設備做出反應。
6 技術交流
QQ群:656580961