2025, Oct 30 21:02

Pepper पर NAOqi ALSpeechRecognition में modifiable_grammar त्रुटि: कारण और समाधान

Pepper/NAOqi ALSpeechRecognition में modifiable_grammar त्रुटि का कारण और व्यावहारिक समाधान: भाषा स्वैप के बाद setLanguage व setVocabulary का सुरक्षित क्रम अपनाएँ.

NAOqi पर Pepper के लिए वाणी‑आधारित इंटरैक्शन बनाते समय, दिखने में साधारण‑सी एक कॉल पूरा प्रवाह बिगाड़ सकती है: ALSpeechRecognition पर नया शब्दभंडार सेट करना। बार‑बार चलाने पर कुछ डेवलपर्स को यह रनटाइम क्रैश मिलता है: A grammar named "modifiable_grammar" already exists. नीचे संक्षेप में बताया गया है कि यह त्रुटि किस वजह से आती है, इसे कम से कम सेटअप में कैसे दोहराया जा सकता है, और वह व्यावहारिक उपाय जो वास्तविक उपयोग में कारगर साबित हुआ है।

न्यूनतम इंटरैक्शन फ्लो के साथ समस्या को दोहराना

नीचे दिया उदाहरण Python 2.7 पर Pepper का एक इंटरैक्शन लूप चलाता है। यह ALSpeechRecognition को कॉन्फ़िगर करता है, रनटाइम पर शब्दभंडार तैयार करता है और उपयोगकर्ता के उत्तर पकड़ने के लिए सब्सक्राइब करता है। नाम सिर्फ उदाहरण के लिए हैं, पर व्यवहार वही है जो आमतौर पर इस त्रुटि तक ले जाता है।

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from naoqi import ALProxy
import time

LEXICON_READY_KEY = "MyApp/VocabInitialized"
ALT_LEXICONS = []

class PepperDialogAgent:
    def __init__(self, host="localhost", port=9559):
        self.host = host
        self.port = port
        self.tts_srv = None
        self.asr_srv = None
        self.mem_srv = None
        self._connect_services()

    def _connect_services(self):
        try:
            self.tts_srv = ALProxy("ALTextToSpeech", self.host, self.port)
            self.asr_srv = ALProxy("ALSpeechRecognition", self.host, self.port)
            self.mem_srv = ALProxy("ALMemory", self.host, self.port)
            self.asr_srv.setLanguage("Spanish")
            print("Robot services ready")
        except Exception as e:
            print("Failed to connect: {}".format(e))
            print("Make sure the robot is on, IP is correct, and you are on the same network.")
            raise

    def prompt_user(self, prompt_text, choices_map, wait_limit=15, conf_min=0.5):
        if not self.tts_srv or not self.asr_srv or not self.mem_srv:
            print("Initialization error")
            return (None, None)
        try:
            candidate_terms = list(choices_map.keys())
            self._configure_lexicon(candidate_terms)
            self.tts_srv.say(prompt_text)
            self.asr_srv.subscribe("DynASRSession")
            print("Listening...")
            print("Expected choices: {}".format(", ".join(candidate_terms)))
            self.mem_srv.insertData("WordRecognized", [])
            detected = self._await_input(wait_limit, conf_min)
            if detected:
                return self._map_result(detected, choices_map)
            else:
                self.tts_srv.say("No pude escuchar tu respuesta. Intenta hablar más claro.")
                return (None, None)
        except Exception as e:
            print("Interaction error: {}".format(e))
            return (None, None)
        finally:
            try:
                self.asr_srv.unsubscribe("DynASRSession")
            except:
                pass

    def _configure_lexicon(self, responses_list):
        try:
            self.asr_srv.pause(True)
            terms = []
            for r in responses_list:
                terms.append(r.lower())
                terms.append(r.upper())
                terms.append(r.capitalize())
            terms = list(set(terms))
            self.asr_srv.setVocabulary(terms, False)
            self.asr_srv.pause(False)
            print("Vocabulary loaded: {}".format(terms))
        except Exception as e:
            print("Vocabulary setup failed")
            print(e)
            raise e

    def _await_input(self, wait_limit, conf_min):
        picked = False
        t0 = time.time()
        time.sleep(1.0)
        while not picked and (time.time() - t0) < wait_limit:
            wr = self.mem_srv.getData("WordRecognized")
            if wr and len(wr) > 1:
                heard = wr[0]
                conf = wr[1]
                print("Heard '{}' with confidence {:.2f}".format(heard, conf))
                if conf > conf_min:
                    print("Accepted: {}".format(heard))
                    self.mem_srv.insertData("WordRecognized", [])
                    return heard
                else:
                    print("Confidence too low ({:.2f}), continuing...".format(conf))
                    self.mem_srv.insertData("WordRecognized", [])
            time.sleep(0.1)
        return None

    def _map_result(self, heard_word, choices_map):
        lowered = heard_word.lower()
        for key, payload in choices_map.items():
            if lowered == key.lower():
                msg = payload.get("text", "")
                val = payload.get("value", 0)
                print("User said '{}' - returning: ('{}', {})".format(heard_word, msg, val))
                return (msg, val)
        print("No mapping for: '{}'".format(heard_word))
        return (None, None)


def prompt_pepper_user(prompt_text, choices_map, host="localhost", port=9559, wait_limit=15):
    agent = PepperDialogAgent(host, port)
    return agent.prompt_user(prompt_text, choices_map, wait_limit)


if __name__ == "__main__":
    print("\n=== Example: Multiple options ===")
    q2 = "¿Qué te gustaría hacer? Puedes decir: bailar, cantar o hablar."
    options2 = {
        "bailar": {"text": "¡Perfecto! Vamos a bailar juntos.", "value": 1},
        "cantar": {"text": "¡Qué divertido! Me encanta cantar.", "value": 2},
        "hablar": {"text": "Excelente, podemos tener una buena conversación.", "value": 3}
    }
    txt, val = prompt_pepper_user(q2, options2)
    print("Result: ('{}', {})".format(txt, val))
    if val == 1:
        print("Activating dance mode...")
    elif val == 2:
        print("Activating singing mode...")
    elif val == 3:
        print("Activating conversation mode...")
    else:
        print("No answer recognized")

    print("\n=== Example: Yes/No ===")
    q1 = "¿Tienes alguna otra pregunta?"
    options1 = {
        "si": {"text": "¡Vamos!", "value": 1},
        "no": {"text": "Entiendo. Ha sido un placer. No dudes en volver a consultarme.", "value": 0}
    }
    txt, val = prompt_pepper_user(q1, options1)
    print("Result: ('{}', {})".format(txt, val))
    if txt:
        helper = PepperDialogAgent()
        helper.tts_srv.say(txt)

पर्दे के पीछे क्या हो रहा है

रिपोर्ट की गई विफलता स्पष्ट है: स्पीच स्टैक यह अपवाद फेंकता है — A grammar named "modifiable_grammar" already exists. व्यवहार में यह अक्सर इंटरैक्शन के दूसरे रन पर दिखता है, ठीक तब जब आप नया शब्दभंडार पुश करते हैं। प्लेटफ़ॉर्म संकेत देता है कि उसी नाम का एक ग्रामर संसाधन अभी मौजूद है, और उसी पहचान वाले एक और ग्रामर को जोड़ने का प्रयास असफल हो जाता है। आधिकारिक API संदर्भ में एक अहम बाध्यता भी दर्ज है, जो अक्सर नज़रअंदाज़ हो जाती है: setLanguage को किसी भी ALSpeechRecognition या ALDialog पद्धति के साथ एक ही समय पर नहीं बुलाना चाहिए। इसे ध्यान में रखने से दो बातें साफ होती हैं: भाषा बदलना किसी सक्रिय रिकग्निशन से अलग क्रम में होना चाहिए, और भाषा संदर्भ रीसेट करने से ALSpeechRecognition अपनी आंतरिक ग्रामर स्थिति को फिर से प्रारंभ कर सकता है।

व्यावहारिक समाधान: भाषा स्वैप करें, फिर लक्ष्य भाषा और शब्दभंडार सेट करें

modifiable_grammar टकराव से उबरने का भरोसेमंद तरीका यह है कि नया शब्दभंडार सेट करने और रिकग्निशन शुरू करने से पहले भाषा की साफ री‑इनिशियलाइज़ेशन कराई जाए। तरीका सरल है: पहले इंस्टॉल की गई भाषाओं के बीच अस्थायी रूप से स्वैप करें, फिर जिस भाषा का उपयोग करना है उसे सेट करें, उसके बाद शब्दभंडार लोड करें और सब्सक्राइब करें। नीचे दिया उदाहरण इसी क्रम का पालन करता है और सभी कॉल्स को रिकग्निशन शुरू होने से पहले ही रखता है।

def init_asr_pipeline(asr_proxy, target_language, keyword_list):
    asr_proxy.setLanguage("German")
    asr_proxy.setLanguage("English")

    asr_proxy.setLanguage(target_language)
    asr_proxy.setVocabulary(keyword_list, False)
    asr_proxy.setAudioExpression(True)
    asr_proxy.setVisualExpression(True)

    asr_proxy.pause(False)
    asr_proxy.subscribe("speech_recognition")

इसे पहले वाले इंटरैक्शन फ्लो पर लागू करते हुए, सबसे जरूरी बात यह है कि शब्दभंडार लोड करने और सब्सक्राइब करने से ठीक पहले भाषा स्वैप और अंतिम setLanguage किया जाए। यदि आप शब्दभंडार बदलते समय रिकग्नाइज़र को pause करते हैं, तो वही क्रम बनाए रखें—पहले pause, फिर बदलाव, उसके बाद resume, और अंत में subscribe। इससे API उपयोग उस दस्तावेज़ी निर्देश के अनुरूप रहता है जिसमें setLanguage को अन्य स्पीच कॉल्स के साथ एक ही समय पर न मिलाने की बात कही गई है।

यह बारीकी क्यों मायने रखती है

वॉयस‑ड्रिवेन अनुभव एक सत्र में कई बार दोहराए जाते हैं—खासकर टेस्टिंग या डेमो लूप्स में। यदि स्पीच स्टैक पहले से बना कोई ग्रामर ऑब्जेक्ट पकड़े हुए है और आप बिना सोचे नया setVocabulary भेज देते हैं, तो उसी टकराव से फिर‑फिर टकराने का जोखिम रहता है। भाषा संदर्भ को स्पष्ट रूप से री‑इनिशियलाइज़ करना एक निर्धारित रीसेट बिंदु देता है और बार‑बार चलाने पर स्थिरता लाता है। साथ ही, यह उस गैर‑नियतात्मक व्यवहार की खोज में लगने वाला समय घटाता है जो ऊपर‑से यादृच्छिक विफलता जैसा लगता है, पर असल में इंजन किसी मौजूदा ग्रामर संसाधन की सुरक्षा कर रहा होता है।

समापन

यदि ALSpeechRecognition.setVocabulary के दौरान A grammar named "modifiable_grammar" already exists दिखे, तो सेटअप क्रम को फिर से व्यवस्थित करें। भाषा और शब्दभंडार बदलते समय यह सुनिश्चित करें कि कोई सक्रिय रिकग्निशन न चल रहा हो; संदर्भ ताज़ा करने के लिए छोटा‑सा भाषा स्वैप करें; इच्छित भाषा को अंत में सेट करें, फिर कीवर्ड लागू करके सब्सक्राइब करें। यह क्रम अपनाने से Pepper की स्पीच रिकग्निशन बार‑बार के रन में भी सुसंगत रहती है, और आपकी बाकी इंटरैक्शन लॉजिक को छेड़ने की आवश्यकता नहीं पड़ती।

यह लेख StackOverflow की प्रश्न (लेखक: Manuel) और DrR955 के उत्तर पर आधारित है।