科普|智能座艙是如何實(shí)現(xiàn)可見即可說的?

????可見即可說也叫語音觸摸屏,是指在汽車車機(jī)/pad/手機(jī)等具備智能屏的設(shè)備上,通過語音來操控當(dāng)前頁面元素,比如你打開了QQ音樂,首頁有一個(gè)我的收藏的按鈕,你說查看收藏,就等于點(diǎn)擊了這個(gè)按鈕;目前市面上大部分新能源汽車都具有此功能,比如小米、問界、蔚來、小鵬、理想、極越等等。
????可見即可說簡言之就是:把用戶語音轉(zhuǎn)換為控件點(diǎn)擊、滑動等事件,具體流程如下:

可見即可說流程

????一般來說,可見即可說不是單個(gè)App的能力,而是系統(tǒng)全局能力,所以從用戶所說到用戶意圖,信息流轉(zhuǎn)在獨(dú)立的語音進(jìn)程中,最后執(zhí)行點(diǎn)擊才進(jìn)入三方App-QQ音樂進(jìn)程中,抓手能夠?qū)⒁鈭D跨進(jìn)程傳遞給具體的控件或者方法。目前市面上有各種供應(yīng)商提供的語音語義識別方案,一般使用就是簡單的API調(diào)用,這里不再詳細(xì)展開;所以可見即可說另一個(gè)關(guān)鍵問題就是如何獲取當(dāng)前頁面抓手集合,常見的方案有三種:

  • 三方應(yīng)用客戶端運(yùn)行時(shí)注冊;

  • 在語音進(jìn)程通過無障礙、OCR、圖片識別等手段自動注冊。

  • 語音模塊云端手動配置;

一 、三方應(yīng)用注冊

????三方應(yīng)用注冊是指QQ音樂、愛奇藝視頻等三方App在每個(gè)頁面進(jìn)入前臺的時(shí)候,通過跨進(jìn)程通信方式把當(dāng)前頁面所有元素的名稱+對應(yīng)方法名(也叫抓手)添加到語音進(jìn)程的一個(gè)集合中,這個(gè)集合叫“當(dāng)前頁面元素抓手集”。
????當(dāng)通過語義識別得到用戶意圖后,比如是:點(diǎn)擊控件“我的收藏”,接下來就從頁面元素名稱列表中尋找是否有"我的收藏"或者同意詞,如果匹配到了,就得到了"我的收藏"這個(gè)key綁定的方法(比如是onClickFavorite),接下來跨進(jìn)程調(diào)用這個(gè)方法就OK了。
????三方應(yīng)用注冊具備較好的精確性和穩(wěn)定性,但是這種方法需要語音和應(yīng)用密切配合,應(yīng)用中侵入了很多的語音注冊和控制回調(diào)的代碼。

二、自動注冊

一種自動注冊流程

????無障礙服務(wù)是Android和IOS等系統(tǒng)提供的一種系統(tǒng)服務(wù),當(dāng)一個(gè)進(jìn)程啟動無障礙服務(wù)后,它就能一直監(jiān)聽前臺頁面元素變化,并能夠獲取所有元素節(jié)點(diǎn)信息(文本、描述)和索引;在通過用戶意圖匹配到特定節(jié)點(diǎn)后,能夠根據(jù)這個(gè)節(jié)點(diǎn)的索引發(fā)起對該節(jié)點(diǎn)代表的頁面元素的點(diǎn)擊、滑動等操作,在Android中關(guān)鍵代碼如下:

/**
 * MyAccessibilityService類擴(kuò)展自AccessibilityService,用于提供無障礙服務(wù)。
 * 這個(gè)類監(jiān)聽系統(tǒng)中發(fā)生的可訪問性事件,并可以根據(jù)事件類型執(zhí)行相應(yīng)的自定義操作。
 */
public class MyAccessibilityService extends AccessibilityService {

    private static final String TAG = "MyAccessibilityService";
    private final Map<String, AccessibilityNodeInfo> mAccessibilityNodes = new HashMap<>();

    /**
     * 獲取訪問性節(jié)點(diǎn)信息的映射表。
     *
     * 該方法不接受任何參數(shù)。
     *
     * @return 返回一個(gè)包含訪問性節(jié)點(diǎn)信息的映射表,其中鍵為節(jié)點(diǎn)的唯一標(biāo)識,值為對應(yīng)的AccessibilityNodeInfo對象。
     */
    public Map<String, AccessibilityNodeInfo> getAccessibilityNodesMap() {
        return mAccessibilityNodes;
    }

    /**
     * 當(dāng)訪問性事件發(fā)生時(shí)的回調(diào)方法。此方法會在窗口狀態(tài)改變或窗口內(nèi)容改變時(shí)被調(diào)用。
     * 主要用于遍歷當(dāng)前活動窗口的根節(jié)點(diǎn),以執(zhí)行特定的操作或獲取特定的信息。
     *
     * @param event 代表發(fā)生的訪問性事件的 AccessibilityEvent 對象。
     */
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 檢查事件類型是否為窗口狀態(tài)改變或窗口內(nèi)容改變
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED ||
                event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
            // 獲取當(dāng)前活動窗口的根節(jié)點(diǎn)
            AccessibilityNodeInfo rootNode = getRootInActiveWindow();
            // 根節(jié)點(diǎn)非空時(shí),遍歷根節(jié)點(diǎn)
            if (rootNode != null) {
                traverseNode(rootNode);
            }
        }
    }


    @Override
    public void onInterrupt() {
        // Handle interruption of the accessibility service
    }

    /**
     * 遍歷并記錄AccessibilityNodeInfo樹中的每個(gè)節(jié)點(diǎn)。
     * 該函數(shù)遞歸地訪問給定節(jié)點(diǎn)的所有子節(jié)點(diǎn),并將每個(gè)節(jié)點(diǎn)的文本或內(nèi)容描述以及對應(yīng)的節(jié)點(diǎn)對象存儲在一個(gè)全局映射中。
     *
     * @param node 要遍歷的 AccessibilityNodeInfo 對象。如果為 null,則不執(zhí)行任何操作。
     */
    private void traverseNode(AccessibilityNodeInfo node) {
        if (node == null) {
            return;   // 如果節(jié)點(diǎn)為null,則直接返回,不進(jìn)行任何操作
        }
        

        // 獲取當(dāng)前節(jié)點(diǎn)的文本或內(nèi)容描述,并存儲該節(jié)點(diǎn)
        CharSequence contentDescription = node.getContentDescription();
        CharSequence text = node.getText();
        // 使用文本或內(nèi)容描述作為鍵,將節(jié)點(diǎn)存儲在 mAccessibilityNodes 映射中
        String key = text != null ? text.toString() : contentDescription.toString();
        mAccessibilityNodes.put(key, node);

        // 日志記錄當(dāng)前節(jié)點(diǎn)的內(nèi)容描述和文本
        Log.d(TAG, "Node Content Description: " + contentDescription + ", Text: " + text);

        // 遞歸地訪問當(dāng)前節(jié)點(diǎn)的每個(gè)子節(jié)點(diǎn)
        for (int i = 0; i < node.getChildCount(); i++) {
            traverseNode(node.getChild(i));
        }
    }

    /**
     * 對給定的無障礙節(jié)點(diǎn)信息執(zhí)行點(diǎn)擊操作。
     * @param node 無障礙節(jié)點(diǎn)信息對象,代表要執(zhí)行點(diǎn)擊操作的UI元素。
     */
    public void performClick(AccessibilityNodeInfo node){
        // 執(zhí)行節(jié)點(diǎn)的點(diǎn)擊動作
        node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    }


}

????但在實(shí)際開發(fā)中使用無障礙服務(wù)來獲取元素節(jié)點(diǎn)信息時(shí)會遇到兩種無法處理的情況:
????1. 控件無描述無標(biāo)題,但是控件內(nèi)的圖像中有文本表明這個(gè)控件的作用;
????2. 控件無描述無標(biāo)題,但是可以根據(jù)控件的圖像知道這個(gè)控件的意義,比如搜索圖標(biāo).
對于情況1,我們可以使用OCR來獲取控件中文本,OCR(Optical Character Recognition,光學(xué)字符識別)是一種技術(shù),用于識別和提取圖像中的文本信息;對于情況2 ,我們可以使用圖像識別方法給出代表控件圖像意義的文本。
????小結(jié):自動注冊原理和實(shí)現(xiàn)過程比較復(fù)雜,但可以實(shí)現(xiàn)三方應(yīng)用跟語音完全解耦,三方App中也不用包含語音代碼。

三、云端手工配置

????云端手工配置是指當(dāng)前頁面的抓手集合是從云端下發(fā)的,語音開發(fā)同學(xué)需要收集每個(gè)頁面的抓手集合,并提前將它配置到云端;在三方App運(yùn)行時(shí)候,可以通過跨進(jìn)程、無障礙等方式獲取當(dāng)前頁面唯一標(biāo)記id,然后語音進(jìn)程根據(jù)此id去云端配置平臺獲取對應(yīng)的抓手集合,具體實(shí)現(xiàn)略。
????云端手工配置具備較好的精確性和一定的靈活性,并一定程度解耦了三方App和語音客戶端;但是云端配置下發(fā)過程中依賴網(wǎng)絡(luò),在網(wǎng)絡(luò)較差情況下會影響體驗(yàn),另外云端配置是語音云端耦合三方App業(yè)務(wù),跟三方應(yīng)用客戶端運(yùn)行時(shí)注冊一樣,都是適合比較封閉的語音生態(tài)。

總結(jié)與思考

????不管是哪種獲取元素抓手的方式都是有優(yōu)勢和劣勢,在實(shí)際中場景中,我們可以根據(jù)具體情況,組合搭配使用,以達(dá)到更優(yōu)的可見即可說效果
????目前語言大模型得到了前所未有的發(fā)展,在實(shí)現(xiàn)可見即可說時(shí)候,可以引入TA來提升整體的兼容性和準(zhǔn)確度,比如在抓手匹配中,引入AI語言大模型進(jìn)行匹配,就能使得用戶只要說類似得意思就能匹配成功,大大增加了這個(gè)可見即可說的泛化水平(對同一個(gè)意思不同說法都兼容,所以具備更加廣泛的適用性)。您還想到有什么是可以改進(jìn),可以在評論區(qū)交流下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容