之前所做的有一個(gè)特點(diǎn)就是需要在樹莓派上連接一個(gè)USB麥克風(fēng)蘸炸,通過這個(gè)麥克風(fēng)來進(jìn)行語(yǔ)音的輸入靠粪,但是在實(shí)際使用場(chǎng)景上來看震桶,這是不合理的休傍,因此需要一個(gè)可以遠(yuǎn)程操控智能小車的方案,因此萌發(fā)了將控制端移植到手機(jī)上的想法蹲姐,移植到手機(jī)上也有幾個(gè)不同的方案磨取。
- 通過Zigbee無線傳輸技術(shù)進(jìn)行數(shù)據(jù)的傳輸
優(yōu)點(diǎn):易于進(jìn)行擴(kuò)展,可方便的與其他智能家居連接起來
缺點(diǎn):傳輸?shù)臄?shù)據(jù)量很小柴墩,不適用于語(yǔ)音數(shù)據(jù)以及圖像數(shù)據(jù)的傳遞 - 通過搭建一個(gè)服務(wù)器忙厌,開發(fā)一個(gè)手機(jī)APP,通過服務(wù)器進(jìn)行數(shù)據(jù)的傳輸
優(yōu)點(diǎn):自己搭建服務(wù)器拐邪, 可控制性強(qiáng)
缺點(diǎn):開發(fā)門檻高(需要進(jìn)行服務(wù)器的搭建以及手機(jī)應(yīng)用的開發(fā)慰毅,不易實(shí)現(xiàn))以及使用門檻高(需要單獨(dú)在手機(jī)上安裝一個(gè)APP) - 通過微信平臺(tái),進(jìn)行數(shù)據(jù)(文本扎阶、語(yǔ)音汹胃、圖像、視頻)的傳遞
優(yōu)點(diǎn):人人都有微信东臀,使用門檻低着饥,功能相對(duì)全面
缺點(diǎn):可控制性相對(duì)較弱
最終經(jīng)過權(quán)衡,選取第三個(gè)方案惰赋,通過微信控制智能小車宰掉。
itchat
itchat是一個(gè)開源的微信個(gè)人號(hào)接口,使用python調(diào)用微信從未如此簡(jiǎn)單赁濒」煅伲可以通過它進(jìn)行微信消息的讀取,回復(fù)拒炎,聯(lián)系人的讀取等等
1. 安裝
pip install itchat
2. 簡(jiǎn)單入門
1.登陸配置
登陸使用的是itchat提供了auto_login方法挪拟,調(diào)用即可完成登錄。
一般而言击你,會(huì)在完成消息的注冊(cè)之后再進(jìn)行登陸玉组。
這里需要特別強(qiáng)調(diào)的是兩點(diǎn)谎柄,分別是短時(shí)間關(guān)閉重連、命令行二維碼惯雳。
- itchat提供了登陸狀態(tài)暫存朝巫,關(guān)閉程序后一定時(shí)間內(nèi)不需要掃碼即可登錄。由于目前微信網(wǎng)頁(yè)版提供上一次登錄的微信號(hào)不掃碼直接手機(jī)確認(rèn)登陸石景,所以如果開啟登陸狀態(tài)暫存將會(huì)自動(dòng)使用這一功能劈猿。
- 為了方便在無圖形界面使用itchat,程序內(nèi)置了命令行二維碼的顯示鸵钝。
短時(shí)間關(guān)閉程序后重連
這樣即使程序關(guān)閉糙臼,一定時(shí)間內(nèi)重新開啟也可以不用重新掃碼。
最簡(jiǎn)單的用法就是給auto_login方法傳入值為真的hotReload恩商。
該方法會(huì)生成一個(gè)靜態(tài)文件itchat.pkl,用于存儲(chǔ)登陸的狀態(tài)必逆。
import itchat
from itchat.content import TEXT
@itchat.msg_register(TEXT)
def simple_reply(msg):
print(msg.text)
itchat.auto_login(hotReload=True)
itchat.run()
命令行二維碼顯示
通過以下命令可以在登陸的時(shí)候使用命令行顯示二維碼:
itchat.auto_login(enableCmdQR=True)
部分系統(tǒng)可能字幅寬度有出入怠堪,可以通過將enableCmdQR賦值為特定的倍數(shù)進(jìn)行調(diào)整:如部分的linux系統(tǒng),塊字符的寬度為一個(gè)字符(正常應(yīng)為兩字符)名眉,故賦值為2
itchat.auto_login(enableCmdQR=2)
2.聊天對(duì)象
在使用個(gè)人微信的過程當(dāng)中主要有三種賬號(hào)需要獲取粟矿,分別為:
- 好友
- 公眾號(hào)
- 群聊
itchat為這三種賬號(hào)都提供了整體獲取方法與搜索方法。群聊多出獲取用戶列表方法以及創(chuàng)建群聊损拢、增加陌粹、刪除用戶的方法。
接下來對(duì)三種分別介紹如何使用以及如何通過唯一的Uin確定好友與群聊福压。
好友
好友的獲取方法為get_friends掏秩,將會(huì)返回完整的好友列表。
- 其中每個(gè)好友為一個(gè)字典
- 列表的第一項(xiàng)為本人的賬號(hào)信息
- 傳入update鍵為True將可以更新好友列表并返回
好友的搜索方法為search_friends荆姆,有四種搜索方式:
- 僅獲取自己的用戶信息
- 獲取特定UserName的用戶信息
- 獲取備注蒙幻、微信號(hào)、昵稱中的任何一項(xiàng)等于name鍵值的用戶
- 獲取備注胆筒、微信號(hào)邮破、昵稱分別等于相應(yīng)鍵值的用戶
下面是示例程序:
獲取自己的用戶信息,返回自己的屬性字典
itchat.search_friends()
獲取特定UserName的用戶信息
itchat.search_friends(userName='@abcdefg1234567')
獲取任何一項(xiàng)等于name鍵值的用戶
itchat.search_friends(name='littlecodersh')
獲取分別對(duì)應(yīng)相應(yīng)鍵值的用戶
itchat.search_friends(wechatAccount='littlecodersh')
其中3仆救、4項(xiàng)功能可以一同使用抒和,將返回同時(shí)滿足兩個(gè)條件的好友
itchat.search_friends(name='LittleCoder機(jī)器人', wechatAccount='littlecodersh')
更新用戶信息的方法為update_friend。
- 該方法需要傳入用戶的UserName彤蔽,返回指定用戶的最新信息
- 也可以傳入U(xiǎn)serName組成的列表摧莽,那么相應(yīng)的也會(huì)返回指定用戶的最新信息組成的列表
memberList = itchat.update_friend('@abcdefg1234567')
公眾號(hào)
公眾號(hào)的獲取方法為get_mps,將會(huì)返回完整的公眾號(hào)列表铆惑。
- 其中每個(gè)公眾號(hào)為一個(gè)字典
- 傳入update鍵為True將可以更新公眾號(hào)列表并返回
公眾號(hào)的搜索方法為search_mps范嘱,有兩種搜索方法:
- 獲取特定UserName的公眾號(hào)
- 獲取名字中含有特定字符的公眾號(hào)
如果兩項(xiàng)都做了特定送膳,將會(huì)僅返回特定UserName的公眾號(hào),下面是示例程序:
獲取特定UserName的公眾號(hào)丑蛤,返回值為一個(gè)字典
itchat.search_mps(userName='@abcdefg1234567')
獲取名字中含有特定字符的公眾號(hào)叠聋,返回值為一個(gè)字典的列表
itchat.search_mps(name='LittleCoder')
以下方法相當(dāng)于僅特定了UserName
itchat.search_mps(userName='@abcdefg1234567', name='LittleCoder')
群聊
群聊的獲取方法為get_chatrooms,將會(huì)返回完整的群聊列表受裹。
- 其中每個(gè)群聊為一個(gè)字典
- 傳入update鍵為True將可以更新群聊列表并返回通訊錄中保存的群聊列表
- 群聊列表為后臺(tái)自動(dòng)更新碌补,如果中途意外退出存在極小的概率產(chǎn)生本地群聊消息與后臺(tái)不同步
- 為了保證群聊信息在熱啟動(dòng)中可以被正確的加載,即使不需要持續(xù)在線的程序也需要運(yùn)行
itchat.run()
- 如果不想要運(yùn)行上述命令棉饶,請(qǐng)?jiān)谕顺龀绦蚯罢{(diào)用
itchat.dump_login_status()
厦章,更新熱拔插需要的信息
群聊的搜索方法為search_chatrooms肴掷,有兩種搜索方法:
- 獲取特定UserName的群聊
- 獲取名字中含有特定字符的群聊
如果兩項(xiàng)都做了特定男翰,將會(huì)僅返回特定UserName的群聊,下面是示例程序:
獲取特定UserName的群聊崔拥,返回值為一個(gè)字典
itchat.search_chatrooms(userName='@@abcdefg1234567')
獲取名字中含有特定字符的群聊幸缕,返回值為一個(gè)字典的列表
itchat.search_chatrooms(name='LittleCoder')
以下方法相當(dāng)于僅特定了UserName
itchat.search_chatrooms(userName='@@abcdefg1234567', name='LittleCoder')
群聊用戶列表的獲取方法為update_chatroom群发。
- 如果想要更新該群聊的其他信息也可以用該方法
- 群聊在首次獲取中不會(huì)獲取群聊的用戶列表,需要調(diào)用該命令才能獲取群聊的成員
- 該方法需要傳入群聊的UserName发乔,返回特定群聊的詳細(xì)信息
- 也可以傳入U(xiǎn)serName組成的列表熟妓,那么相應(yīng)的也會(huì)返回指定用戶的最新信息組成的列表
memberList = itchat.update_chatroom('@@abcdefg1234567', detailedMember=True)
創(chuàng)建群聊、增加栏尚、刪除群聊用戶的方法如下所示:
- 由于之前通過群聊檢測(cè)是否被好友拉黑的程序起愈,目前這三個(gè)方法都被嚴(yán)格限制了使用頻率
- 刪除群聊需要本賬號(hào)為群管理員,否則會(huì)失敗
- 將用戶加入群聊有直接加入與發(fā)送邀請(qǐng)译仗,通過useInvitation設(shè)置
- 超過40人的群聊無法使用直接加入的加入方式抬虽,特別注意
memberList = itchat.get_friends()[1:]
chatroomUserName = itchat.create_chatroom(memberList, 'test chatroom') # 創(chuàng)建群聊,topic鍵值為群聊名
itchat.delete_member_from_chatroom(chatroomUserName, memberList[0]) # 刪除群聊內(nèi)的用戶
itchat.add_member_into_chatroom(chatroomUserName, memberList[0], useInvitation=False) # 增加用戶進(jìn)入群聊
Uins
Uin 就是微信中用于標(biāo)識(shí)用戶的方式古劲,每一個(gè)用戶斥赋、群聊都有唯一且不同的Uin。通過Uin产艾,即使退出了重新登錄疤剑,也可以輕松的確認(rèn)正在對(duì)話的是上一次登陸的哪一個(gè)用戶。但注意闷堡,Uin與其他值不同隘膘,微信后臺(tái)做了一定的限制,必須通過特殊的操作才能獲取杠览。
最簡(jiǎn)單來說弯菊,首次點(diǎn)開登陸用的手機(jī)端的某個(gè)好友或者群聊,itchat就能獲取到該好友或者群聊的Uin踱阿。
如果想要通過程序獲取管钳,也可以用程序?qū)⒛硞€(gè)好友或者群聊置頂(取消置頂)钦铁。
這里提供一個(gè)提示群聊更新的程序:
import re, sys, json
import itchat
from itchat.content import *
itchat.auto_login(True)
@itchat.msg_register(SYSTEM)
def get_uin(msg):
if msg['SystemInfo'] != 'uins': return
ins = itchat.instanceList[0]
fullContact = ins.memberList + ins.chatroomList + ins.mpList
print('** Uin Updated **')
for username in msg['Text']:
member = itchat.utils.search_dict_list(
fullContact, 'UserName', username)
print(('%s: %s' % (
member.get('NickName', ''), member['Uin']))
.encode(sys.stdin.encoding, 'replace'))
itchat.run(True)
每當(dāng)Uin更新了,就會(huì)打印相應(yīng)的更新情況才漆。
3.消息處理
消息內(nèi)容
微信初始化消息牛曹、文本消息、圖片消息醇滥、小視頻消息黎比、地理位置消息、名片消息鸳玩、語(yǔ)音消息阅虫、動(dòng)畫表情、普通鏈接或應(yīng)用分享消息不跟、音樂鏈接消息颓帝、群消息、紅包消息躬拢、系統(tǒng)消息
回復(fù)方法
- 方法:
send(msg='Text Message', toUserName=None)
- 所需值:
- msg:消息內(nèi)容
- '@fil@文件地址'將會(huì)被識(shí)別為傳送文件
- '@img@圖片地址'將會(huì)被識(shí)別為傳送圖片
- '@vid@視頻地址'將會(huì)被識(shí)別為小視頻
- toUserName:發(fā)送對(duì)象躲履,如果留空將會(huì)發(fā)送給自己
- 返回值:發(fā)送成功->True, 失敗->False
- 程序示例:使用的素材可以在這里下載
#coding=utf8
import itchat
itchat.auto_login()
itchat.send('Hello world!')
# 請(qǐng)確保該程序目錄下存在:gz.gif以及xlsx.xlsx
itchat.send('@img@%s' % 'gz.gif')
itchat.send('@fil@%s' % 'xlsx.xlsx')
itchat.send('@vid@%s' % 'demo.mp4')
4.注冊(cè)方法
itchat將根據(jù)接收到的消息類型尋找對(duì)應(yīng)的已經(jīng)注冊(cè)的方法。如果一個(gè)消息類型沒有對(duì)應(yīng)的注冊(cè)方法聊闯,該消息將會(huì)被舍棄。
注冊(cè)
可以通過兩種方式注冊(cè)消息方法
import itchat
from itchat.content import *
# 不帶具體對(duì)象注冊(cè)米诉,將注冊(cè)為普通消息的回復(fù)方法
@itchat.msg_register(TEXT)
def simple_reply(msg):
return 'I received: %s' % msg['Text']
# 帶對(duì)象參數(shù)注冊(cè)菱蔬,對(duì)應(yīng)消息對(duì)象將調(diào)用該方法
@itchat.msg_register(TEXT, isFriendChat=True, isGroupChat=True, isMpChat=True)
def text_reply(msg):
msg.user.send('%s: %s' % (msg.type, msg.text))
消息類型
向注冊(cè)方法傳入的msg包含微信返回的字典的所有內(nèi)容。
本api增加Text史侣、Type(也就是參數(shù))鍵值拴泌,方便操作。
itchat.content中包含所有的消息類型參數(shù)惊橱,內(nèi)容如下表所示:
參數(shù) | 類型 | Text鍵值 |
---|---|---|
TEXT | 文本 | 文本內(nèi)容 |
MAP | 地圖 | 位置文本 |
CARD | 名片 | 推薦人字典 |
NOTE | 通知 | 通知文本 |
SHARING | 分享 | 分享名稱 |
PICTURE | 圖片/表情 | 下載方法 |
RECORDING | 語(yǔ)音 | 下載方法 |
ATTACHMENT | 附件 | 下載方法 |
VIDEO | 小視頻 | 下載方法 |
FRIENDS | 好友邀請(qǐng) | 添加好友所需參數(shù) |
SYSTEM | 系統(tǒng)消息 | 更新內(nèi)容的用戶或群聊的UserName組成的列表 |
比如你需要存儲(chǔ)發(fā)送給你的附件:
@itchat.msg_register(ATTACHMENT)
def download_files(msg):
msg['Text'](msg['FileName'])
值得注意的是蚪腐,群消息增加了三個(gè)鍵值:
- isAt: 判斷是否@本號(hào)
- ActualNickName: 實(shí)際NickName
- Content: 實(shí)際Content
可以通過本程序測(cè)試:
import itchat
from itchat.content import TEXT
@itchat.msg_register(TEXT, isGroupChat=True)
def text_reply(msg):
print(msg.isAt)
print(msg.actualNickName)
print(msg.text)
itchat.auto_login()
itchat.run()
注冊(cè)消息的優(yōu)先級(jí)
優(yōu)先級(jí)分別為:后注冊(cè)消息先于先注冊(cè)消息,帶參數(shù)消息先于不帶參數(shù)消息税朴。
以下面的兩個(gè)程序?yàn)槔?/p>
import itchat
from itchat.content import *
itchat.auto_login()
@itchat.msg_register(TEXT)
def text_reply(msg):
return 'This is the old register'
@itchat.msg_register(TEXT)
def text_reply(msg):
return 'This is a new one'
itchat.run()
在私聊發(fā)送文本時(shí)將會(huì)回復(fù)This is a new one回季。
import itchat
from itchat.content import *
itchat.auto_login()
@itchat.msg_register
def general_reply(msg):
return 'I received a %s' % msg.type
@itchat.msg_register(TEXT)
def text_reply(msg):
return 'You said to me one to one: %s' % msg.text
itchat.run()
僅在私聊發(fā)送文本時(shí)將會(huì)回復(fù)You said to me one to one,其余情況將會(huì)回復(fù)I received a ...正林。
STT (Speak To Text)
通過上述的itchat模塊泡一,就可以在樹莓派上用python進(jìn)行微信的信息接收等,文本消息我們可以利用之前類似的方法進(jìn)行識(shí)別觅廓、控制鼻忠,那么僅僅用文本消息控制就顯得不夠智能了,需要用語(yǔ)音來控制杈绸,那么如何用語(yǔ)音來控制小車呢帖蔓?這就要用到STT模塊矮瘟,這個(gè)模塊做的就是把語(yǔ)音識(shí)別為文本信息,目前有多家公司有開源項(xiàng)目可以使用塑娇,比如Google澈侠、百度、訊飛钝吮,綜合考慮語(yǔ)音識(shí)別準(zhǔn)確率埋涧、易用性上,我選擇了百度的API接口奇瘦。
百度AI平臺(tái)
要想使用百度的語(yǔ)音識(shí)別API棘催,有如下幾個(gè)準(zhǔn)備工作。
- 首先要成為百度開發(fā)者
- 創(chuàng)建應(yīng)用
- 獲取密鑰
- 合成Access Token
前三部分可以在百度開發(fā)者平臺(tái)上很簡(jiǎn)單的注冊(cè)申請(qǐng)耳标,不再贅述醇坝。要注意的是保留好上述申請(qǐng)到的密鑰(API KEY、Secret KEY)次坡,會(huì)在下面用到呼猪;第四部分下面會(huì)詳細(xì)介紹。
語(yǔ)音識(shí)別簡(jiǎn)介
百度語(yǔ)音識(shí)別通過 REST API 的方式給開發(fā)者提供一個(gè)通用的 HTTP 接口砸琅。 上傳需要完整的錄音文件宋距,錄音文件時(shí)長(zhǎng)不超過60s。
調(diào)用流程
1. 獲取Access Token
請(qǐng)求URL數(shù)據(jù)格式
向授權(quán)服務(wù)地址https://aip.baidubce.com/oauth/2.0/token
發(fā)送請(qǐng)求(推薦使用POST)症脂,并在URL中帶上以下參數(shù):
- grant_type: 必須參數(shù)谚赎,固定為client_credentials;
- client_id: 必須參數(shù)诱篷,應(yīng)用的API Key壶唤;
- client_secret: 必須參數(shù),應(yīng)用的Secret Key棕所;
Python獲取access_token示例代碼
import urllib, urllib2, sys
import ssl
# client_id 為官網(wǎng)獲取的AK闸盔, client_secret 為官網(wǎng)獲取的SK
host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=【官網(wǎng)獲取的AK】&client_secret=【官網(wǎng)獲取的SK】'
request = urllib2.Request(host)
request.add_header('Content-Type', 'application/json; charset=UTF-8')
response = urllib2.urlopen(request)
content= response.read()
if (content):
print(content)
上述代碼中content是一個(gè)JSON格式信息,其中包含你請(qǐng)求到的授權(quán)碼琳省,會(huì)在調(diào)用API時(shí)用到迎吵,有限期一般為一個(gè)月。
服務(wù)器返回的JSON文本參數(shù)如下:
- access_token: 要獲取的Access Token岛啸;
- expires_in: Access Token的有效期(秒為單位钓觉,一般為1個(gè)月);
- 其他參數(shù)忽略坚踩,暫時(shí)不用;
例如:
{
"access_token": "25.b55fe1d287227ca97aab219bb249b8ab.315360000.1798284651.282335-8574074",
"expires_in": 2592000,
"scope": "public wise_adapt",
"session_key": "9mzdDZXu3dENdFZQurfg0Vz8slgSgvvOAUebNFzyzcpQ5EnbxbF+hfG9DQkpUVQdh4p6HbQcAiz5RmuBAja1JJGgIdJI",
"access_token": "24.6c5e1ff107f0e8bcef8c46d3424a0e78.2592000.1485516651.282335-8574074",
"session_secret": "dfac94a3489fe9fca7c3221cbf7525ff"
}
其中access_token
即為我們需要的信息荡灾,通過Python的json
模塊,把它提取出來。
data = json.loads(content)
token = data["access_token"]
2. 請(qǐng)求方式
- 如果音頻在本地批幌,需要將音頻數(shù)據(jù)放在body中础锐。(推薦方式)
- 如果音頻在互聯(lián)網(wǎng)上,可以讓百度服務(wù)器下載荧缘,然后回調(diào)到自己服務(wù)器的接口
- 音頻在本地皆警,有JSON和raw兩種方式提交。
- 音頻在在互聯(lián)網(wǎng)上截粗,需要百度服務(wù)器下載信姓,只能通過JSON方式提交
這里只介紹通過json 方式,上傳本地文件(官方推薦绸罗,更高效)
JSON方式上傳
語(yǔ)音數(shù)據(jù)和其他參數(shù)通過標(biāo)準(zhǔn) JSON 格式串行化 POST 上傳意推, JSON 里包括的參數(shù):
字段名 | 是必填或選填 | 描述 |
---|---|---|
format | 必填 | 語(yǔ)音文件的格式,pcm 或者 wav 或者 amr珊蟀。不區(qū)分大小寫菊值。推薦pcm文件 |
rate | 必填 | 采樣率, 8000 或者 16000育灸, 推薦 16000 采用率 |
channel | 必填 | 聲道數(shù)腻窒,僅支持單聲道,請(qǐng)?zhí)顚懝潭ㄖ?1 |
cuid | 必填 | 用戶唯一標(biāo)識(shí)磅崭,用來區(qū)分用戶儿子,計(jì)算UV值。建議填寫能區(qū)分用戶的機(jī)器 MAC 地址或 IMEI 碼砸喻,長(zhǎng)度為60字符以內(nèi)典徊。 |
token | 必填 | 開放平臺(tái)獲取到的開發(fā)者[ access_token](http://yuyin.baidu.com/docs/tts/135#獲取 Access Token "access_token") |
lan | 選填 | 語(yǔ)種選擇,默認(rèn)中文(zh)恩够。 中文=zh、粵語(yǔ)=ct羡铲、英文=en蜂桶,不區(qū)分大小寫 |
url | 選填 | 可下載的語(yǔ)音下載地址,與callback連一起使用也切,確保百度服務(wù)器可以訪問扑媚。 |
callback | 選填 | 用戶服務(wù)器的識(shí)別結(jié)果回調(diào)地址,確保百度服務(wù)器可以訪問 |
speech | 選填 | 本地語(yǔ)音文件的的二進(jìn)制語(yǔ)音數(shù)據(jù) 雷恃,需要進(jìn)行base64 編碼疆股。與len參數(shù)連一起使用。 |
len | 選填 | 本地語(yǔ)音文件的的字節(jié)數(shù)倒槐,單位字節(jié) |
上傳示例(speech, len 參數(shù))
JSON格式POST上傳本地文件,固定頭部header:Content-Type:application/json
,4K大小的pcm文件(普通話錄音)請(qǐng)求: speech 參數(shù)填寫為文件內(nèi)容base64后的結(jié)果:
{
"format":"pcm",
"rate":16000,
"channel":1,
"token":xxx,
"cuid":"baidu_workshop",
"len":4096,
"speech":"xxx", // xxx為 base64(FILE_CONTENT)
}
返回示例
{
"corpus_no":"6433214037620997779",
"err_msg":"success.",
"err_no":0,
"result":["北京科技館旬痹,"],
"sn":"371191073711497849365"
}
注意事項(xiàng)
- len 字段表示原始語(yǔ)音大小字節(jié)數(shù),不是 base64 編碼之后的長(zhǎng)度。
核心代碼
# -*- coding: utf-8 -*-
"""
語(yǔ)音識(shí)別功能模塊(語(yǔ)音輸入)
參考:http://open.duer.baidu.com/doc/dueros-conversational-service/device-interface/voice-input_markdown
"""
import urllib.request
import json
import base64
def listen(file):
os.system('ffmpeg -y -i %s -acodec pcm_s16le -f s16le -ac 1 -ar 16000 16k_dai.pcm' % file)
#打開音頻文件两残,并進(jìn)行編碼
f = open(WAVE_FILE, "rb")
speech = base64.b64encode(f.read()).decode('utf-8')
size = os.path.getsize(WAVE_FILE)
update = json.dumps({"format":WAVE_TYPE, "rate":VOICE_RATE, 'channel':1,'cuid':USER_ID,'token':token,'speech':speech,'len':size}).encode('utf-8')
update_length = len(update)
headers = { 'Content-Type' : 'application/json' }
url = "http://vop.baidu.com/server_api"
req = urllib.request.Request(url)
req.add_header("Content-Type", "application/json")
req.add_header("Content-Length", update_length)
r = urllib.request.urlopen(url=req, data=update)
t = r.read().decode('utf-8')
result = json.loads(t)
if result['err_msg']=='success.':
command = result['result'][0].encode('utf-8').decode()
return command
else:
return '我沒有聽懂你說什么S酪恪!人弓!'
#設(shè)置應(yīng)用信息
baidu_server = "https://openapi.baidu.com/oauth/2.0/token?"
grant_type = "client_credentials"
client_id = "*****************" #填寫API Key 需自行申請(qǐng)
client_secret = "****************" #填寫Secret Key
#合成請(qǐng)求token的URL
url = baidu_server+"grant_type="+grant_type+"&client_id="+client_id+"&client_secret="+client_secret
#獲取token
res = urllib.request.urlopen(url).read().decode()
data = json.loads(res)
token = data["access_token"]
print ( token )
#設(shè)置音頻屬性沼死,根據(jù)百度的要求,采樣率必須為8000崔赌,壓縮格式支持pcm(不壓縮)意蛀、wav、opus健芭、speex县钥、amr
VOICE_RATE = 16000
WAVE_FILE = "16k_dai.pcm" #音頻文件的路徑
USER_ID = "hail_hydra" #用于標(biāo)識(shí)的ID,可以隨意設(shè)置
WAVE_TYPE = "pcm"
TTS(Text To Speak)
智能小車識(shí)別命令做出相應(yīng)動(dòng)作后要進(jìn)行反饋吟榴,同樣采用百度的語(yǔ)音合成API魁蒜,將要說的話合稱為語(yǔ)音,這部分我們采用與上面的REST API不同的方式吩翻,采用Python SDK的方式獲取兜看。
安裝語(yǔ)音合成 Python SDK
語(yǔ)音合成 Python SDK目錄結(jié)構(gòu)
├── README.md
├── aip //SDK目錄
│ ├── __init__.py //導(dǎo)出類
│ ├── base.py //aip基類
│ ├── http.py //http請(qǐng)求
│ └── speech.py //語(yǔ)音合成
└── setup.py //setuptools安裝
安裝使用Python SDK有如下方式:
- 如果已安裝pip,執(zhí)行pip install baidu-aip即可狭瞎。
- 如果已安裝setuptools细移,執(zhí)行python setup.py install即可。
此種方式比較簡(jiǎn)單熊锭,不做詳細(xì)介紹弧轧,直接上核心代碼
核心代碼
# -*- coding: utf-8-*-
import os
import sys
from imp import reload
reload(sys)
def say(what):
#配置賬戶信息
from aip import AipSpeech
""" 你的 APPID AK SK """
APP_ID = '10462370'
API_KEY =
SECRET_KEY =
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
result = client.synthesis( what, 'zh', 1, {
'vol': 5,
'per': 3,
})
# 識(shí)別正確返回語(yǔ)音二進(jìn)制 錯(cuò)誤則返回dict 參照下面錯(cuò)誤碼
if not isinstance(result, dict):
with open('auido.mp3', 'wb') as f:
f.write(result)
os.system("auido.mp3")
if __name__ == '__main__':
say('李博是最最最帥的')
控制代碼
這部分在教程人工智能-樹莓派小車(4)——通過語(yǔ)音玩轉(zhuǎn)智能小車已經(jīng)介紹過,原理相同碗殷,這里只放代碼精绎。
def recognize():
file = '/home/pi/Rascar-DuerOS-Python-Client/ord.txt' # 讀取文件
f = open(file,'r')
out = f.read()
command = out.decode('unicode-escape').encode('utf-8')
print command
GPIO.output(LED_CTR, GPIO.LOW)
############################################
###############命令識(shí)別######################
############################################
if command.find(u"開") !=-1 and command.find(u"大") !=-1 and command.find(u"燈") !=-1:
print ("打開前大燈")
say(random.choice(['已經(jīng)幫您開燈','開燈了','好的,開燈','是锌妻,正在開燈']))
robot.Open_Flight()
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"關(guān)") !=-1 and command.find(u"大") !=-1 and command.find(u"燈") !=-1:
print ("關(guān)閉前大燈")
say(random.choice(['已經(jīng)幫您關(guān)燈','關(guān)燈了','好的代乃,關(guān)燈','是,正在關(guān)燈']))
robot.Close_Flight()
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"前") !=-1 and command.find(u"進(jìn)") !=-1:
print ("前進(jìn)")
say(random.choice(['好的仿粹,前進(jìn)','向前出發(fā)','是搁吓,前進(jìn)一步']))
robot.Motor_Forward()
time.sleep(2)
robot.Motor_Stop()
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"后") !=-1 and command.find(u"退") !=-1:
print ("后退")
say(random.choice(['好的,后退','倒車請(qǐng)注意','是吭历,后退一點(diǎn)']))
robot.Motor_Backward()
time.sleep(2)
robot.Motor_Stop()
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"左") !=-1 and command.find(u"轉(zhuǎn)") !=-1:
print ("左轉(zhuǎn)")
say(random.choice(['好嘞堕仔,向左轉(zhuǎn)','拐啦,拐啦晌区,向左拐啦','好的摩骨,向左轉(zhuǎn)彎','是通贞,向左轉(zhuǎn)彎']))
robot.Motor_TurnLeft()
p = GPIO.PWM(11, 3)
p.start(20)
time.sleep(0.5)
p.stop()
robot.Motor_Stop()
GPIO.output(11, GPIO.LOW)
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"右") !=-1 and command.find(u"轉(zhuǎn)") !=-1:
print ("右轉(zhuǎn)")
say(random.choice(['好嘞,向右轉(zhuǎn)','拐啦仿吞,拐啦滑频,向右拐啦','好的,向右轉(zhuǎn)彎','是唤冈,向右轉(zhuǎn)彎']))
robot.Motor_TurnRight()
p = GPIO.PWM(8, 3)
p.start(20)
time.sleep(0.5)
p.stop()
robot.Motor_Stop()
GPIO.output(8, GPIO.LOW)
GPIO.output(LED_CTR, GPIO.HIGH)
elif command.find(u"黑") !=-1 and command.find(u"線") !=-1:
print ("黑線")
robot.TrackLine()
elif command == "狀態(tài)":
os.system('cat deng.txt door.txt tv.txt >all.txt')
itchat.send_file("/home/pi/dai/command/all.txt",toUserName='filehelper')
return
語(yǔ)音聊天
語(yǔ)音聊天國(guó)內(nèi)做的比較好的有圖靈機(jī)器人峡迷、小i機(jī)器人,百度的小度也不錯(cuò)你虹,不過還沒有什么開放接口绘搞,所以目前在使用圖靈機(jī)器人(采用WEB API 2.0 接口),下面簡(jiǎn)單介紹一下申請(qǐng)傅物、使用方法等基本流程夯辖。
簡(jiǎn)介
圖靈機(jī)器人API是在人工智能的核心能力(包括語(yǔ)義理解、智能問答董饰、場(chǎng)景交互蒿褂、知識(shí)管理等)的基礎(chǔ)上,為廣大開發(fā)者卒暂、合作伙伴和企業(yè)提供的一系列基于云計(jì)算和大數(shù)據(jù)平臺(tái)的在線服務(wù)和開發(fā)接口啄栓。
開發(fā)者可以利用圖靈機(jī)器人的API創(chuàng)建各種在線服務(wù),靈活定義機(jī)器人的屬性也祠、編輯機(jī)器人的智能問答內(nèi)容昙楚,打造個(gè)人專屬智能交互機(jī)器人。
使用流程
注冊(cè)申請(qǐng)圖靈帳號(hào)
登錄圖靈機(jī)器人官方網(wǎng)站http://www.tuling123.com/诈嘿,點(diǎn)擊右上角“注冊(cè)”按鈕進(jìn)行注冊(cè)并激活帳號(hào)堪旧,如下圖所示:
獲取APIKEY
每一個(gè)激活用戶都可以通過圖靈機(jī)器人開放平臺(tái)獲取多個(gè)APIKEY(當(dāng)前每個(gè)用戶可最多獲取5個(gè)APIKEY),用戶可以根據(jù)自己的需要獲取不同的圖靈APIKEY來應(yīng)用于多種場(chǎng)景奖亚,獲取成功后就等于拿到了開啟圖靈服務(wù)的鑰匙淳梦。
登錄圖靈帳號(hào),進(jìn)入個(gè)人中心昔字,在“我的機(jī)器人》機(jī)器人詳情》接入”頁(yè)面即可看到每一個(gè)機(jī)器人的API KEY谭跨,如下圖所示:
編碼方式
UTF-8(調(diào)用圖靈API的各個(gè)環(huán)節(jié)的編碼方式均為UTF-8)
接口地址
http://openapi.tuling123.com/openapi/api/v2
請(qǐng)求方式
HTTP POST
請(qǐng)求參數(shù)
請(qǐng)求參數(shù)格式為 json
請(qǐng)求示例:
{
"reqType":0,
"perception": {
"inputText": {
"text": "附近的酒店"
},
"inputImage": {
"url": "imageUrl"
},
"selfInfo": {
"location": {
"city": "北京",
"province": "北京",
"street": "信息路"
}
}
},
"userInfo": {
"apiKey": "",
"userId": ""
}
}
參數(shù)說明
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
reqType | int | N | - | 輸入類型:0-文本(默認(rèn))、1-圖片李滴、2-音頻 |
perception | - | Y | - | 輸入信息 |
userInfo | - | Y | - | 用戶參數(shù) |
perception
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
inputText | - | N | - | 文本信息 |
inputImage | - | N | - | 圖片信息 |
inputMedia | - | N | - | 音頻信息 |
selfInfo | - | N | - | 客戶端屬性 |
注意:輸入?yún)?shù)必須包含inputText或inputImage或inputMedia!
inputText
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
text | String | Y | 1-128字符 | 直接輸入文本 |
inputImage
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
url | String | Y | 圖片地址 |
inputMedia
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
url | String | Y | 音頻地址 |
selfInfo
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
location | - | N | - | 地理位置信息 |
location
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
city | String | Y | - | 所在城市 |
province | String | N | - | 省份 |
street | String | N | - | 街道 |
userInfo
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
apiKey | String | Y | 32位 | 機(jī)器人標(biāo)識(shí) |
userId | String | Y | 長(zhǎng)度小于等于32位 | 用戶唯一標(biāo)識(shí) |
groupId | String | N | 長(zhǎng)度小于等于64位 | 群聊唯一標(biāo)識(shí) |
userIdName | String | N | 長(zhǎng)度小于等于64位 | 群內(nèi)用戶昵稱 |
輸出參數(shù)
輸出示例:
{
"intent": {
"code": 10005,
"intentName": "",
"actionName": "",
"parameters": {
"nearby_place": "酒店"
}
},
"results": [
{
"groupType": 1,
"resultType": "url",
"values": {
"url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF"
}
},
{
"groupType": 1,
"resultType": "text",
"values": {
"text": "親蛮瞄,已幫你找到相關(guān)酒店信息"
}
}
]
}
參數(shù)說明
參數(shù) | 類型 | 是否必須 | 取值范圍 | 說明 |
---|---|---|---|---|
intent | - | Y | - | 請(qǐng)求意圖 |
results | - | N | - | 輸出結(jié)果集 |
intent
參數(shù) | 類型 | 是否包含 | 取值范圍 | 說明 |
---|---|---|---|---|
code | int | Y | - | 輸出功能code |
intentName | String | N | - | 意圖名稱 |
actionName | String | N | - | 意圖動(dòng)作名稱 |
parameters | String | N | - | 功能相關(guān)參數(shù) |
results
參數(shù) | 類型 | 是否包含 | 取值范圍 | 說明 |
---|---|---|---|---|
resultType | String | Y | 文本(text);連接(url);音頻(voice);視頻(video);圖片(image);圖文(news) | 輸出類型 |
values | - | Y | - | 輸出值 |
groupType | int | Y | - | ‘組’編號(hào):0為獨(dú)立輸出所坯,大于0時(shí)可能包含同組相關(guān)內(nèi)容 (如:音頻與文本為一組時(shí)說明內(nèi)容一致) |
核心代碼
#coding=utf8
import urllib.request
import json
KEY = 'd4473a5b08da492cad0bc98bb8bc77bb'
def get_response(msg):
apiUrl = 'http://openapi.tuling123.com/openapi/api/v2'
data = json.dumps({
"reqType":0,
"perception": {
"inputText": {
"text": msg
},
"selfInfo": {
"location": {
"city": "北京",
"province": "北京",
"street": "中關(guān)村南大街"
}
}
},
"userInfo": {
"apiKey": KEY,
"userId": "123555"
}
}).encode('utf-8')
try:
r = urllib.request.urlopen(url=apiUrl, data=data)
t = r.read().decode('utf-8')
result = json.loads(t)
return result
except:
return
if __name__ == '__main__':
#get_response('給我一張小狗照片').get('results')('image')
print (get_response('給我一張小狗照片')['results'][0]['values']['url'].encode('utf-8').decode())
完整代碼
所有的代碼都上傳到了我的GitHub上
Rascar-Wechat — A COOL Raspberry Intelligent car based on the Wechat
系列教程
人工智能-樹莓派小車(1)——DuerOS語(yǔ)音喚醒
人工智能-樹莓派小車(2)——GPIO接口介紹
人工智能-樹莓派小車(3)——GPIO控制小車
人工智能-樹莓派小車(4)——通過語(yǔ)音玩轉(zhuǎn)智能小車
人工智能-樹莓派小車(5)——用微信控制智能小車