VR 앱에서 사용할 수 있는 음성 명령을 간단하게 구현해보기(Oculus Rift CV1 + Windows10)

이 기사는 Tech-Circle Hands on Advent Calendar 2016의 10일째 기사입니다.
어제는 @negokaz 씨에 의한 Akka Camel로 외부 시스템과 액터를 쉽게 연결 이었습니다.

소개



애플리케이션 기술 주간도 오늘 마지막입니다.
VR 원년의 2016년도 곧 끝납니다만, 여러분 어떠셨습니까.
나로서는 올해는 Tech-Circle Unity VR 핸즈온 를 개최할 수 있었던 것이 매우 컸다. 참가해 주신 여러분 감사합니다. 처음 뵙겠습니다(분)편으로 흥미가 있는 분은 사전 준비와 당일의 자료는 링크처의 이벤트 페이지에 공개하고 있으므로, 꼭 봐 주세요.
자, 오늘은 Tech-Circle의 VR 담당으로 VR 앱에서 사용할 수있는 음성 명령의 구현에 대해 씁니다.

개발 환경


  • Windows 10 64bit
  • Unity 5.4.2 (5.5 나왔네)
  • Oculus Rift CV1

  • Unity 음성 인식 API



    Unity의 음성 인식은 구구 보면 Julius를 사용하는 예가 많습니다만, 이번은 Oculus Rift CV1을 사용하므로 Unity5.4에서 추가된 음성 인식 API의 UnityEngine.Windows.Speech을 사용합니다. Mac에서 Oculus CV1을 사용하는 것은 어렵기 때문에 플랫폼은 제한되지만 문제는 없다고 생각합니다.

    UnityEngine.Windows.Speech 체계



    UnityEngine.Windows.Speech의 체계에 대해서는 스크립트 레퍼런스가 아니고 Microsoft 개발자 페이지 에 정리한 자료가 있습니다.
    위대한 구글 선생님의 힘을 빌리면서 정리하면 다음과 같은 느낌입니다.

    우선 기능으로서는 이하의 크게 2개로 분류됩니다.
  • Phrase Recognition(어구 인식)
  • Dictation (듣기)

  • Dictation은 말하고 있는 내용을 문자로 변환해 주는 기능입니다. Phrase Recognition은 등록된 구를 인식할 때 이벤트가 실행되는 기능입니다. 이 두 가지는 동시에 실행할 수 없으므로 앱에서 둘 다 사용하는 경우 전환이 필요합니다.

    다음에 구현에 대해서는 Dictation은 그대로 DictationRecognizer로 구현되고 있습니다(이번은 음성 커멘드의 구현이므로 자세한 것은 생략합니다). Phrase Recognition의 경우 다음 두 가지 클래스로 나뉩니다.
  • KeywordRecognizer (키워드 인식)
  • GrammarRecognizer (문법 인식)

  • KeywordRecognizer는 배열에서 지정한 키워드와의 일치를 GrammarRecognizer는 SRGS라는 XML로 작성된 음성 인식 문법과의 일치를 인식했을 때 OnPhraseRecognized 메서드가 호출됩니다. 이번에는 "쉽게"이므로 KeywordRecognizer를 사용합니다.

    사용해 보았습니다.



    VR로 음성 커맨드라는 것으로 유니티 짱에게 깃발을 받았습니다. (음성 커맨드보다 깃발을 만드는 데 시간이 걸린다고...)
    음성 명령 데모 - YouTube (※ 음성이 있습니다)

    구현



    HataAgeGame.cs
    using System;
    using System.Text;
    using UnityEngine;
    using UnityEngine.Windows.Speech;
    
    public class HataAgeGame : MonoBehaviour
    {
    
        //音声コマンドのキーワード
        private string[] m_Keywords = { "あかあげて", "しろあげて", "あかさげて", "しろさげて" };
    
        private KeywordRecognizer m_Recognizer;
    
        //右手の位置ターゲット
        private GameObject RightHandTarget;
        //左手の位置ターゲット
        private GameObject LeftHandTarget;
    
        //右手と左手の上下それぞれの座標
        private Vector3 RightHandUpPos = new Vector3(-0.51f, 2.159f, 0f);
        private Vector3 LeftHandUpPos = new Vector3(0.588f, 2.108f, 0f);
        private Vector3 RightHandDownPos = new Vector3(-0.3f, 0.838f, 0f);
        private Vector3 LeftHandDownPos = new Vector3(0.341f, 0.838f, 0f);
    
        void Start()
        {
            LeftHandTarget = GameObject.FindWithTag("LeftHandTarget");
            RightHandTarget = GameObject.FindWithTag("RightHandTarget");
            m_Recognizer = new KeywordRecognizer(m_Keywords);
            m_Recognizer.OnPhraseRecognized += OnPhraseRecognized;
            m_Recognizer.Start();
        }
    
        private void OnPhraseRecognized(PhraseRecognizedEventArgs args)
        {
            //ログ出力
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("{0} ({1}){2}", args.text, args.confidence, Environment.NewLine);
            builder.AppendFormat("\tTimestamp: {0}{1}", args.phraseStartTime, Environment.NewLine);
            builder.AppendFormat("\tDuration: {0} seconds{1}", args.phraseDuration.TotalSeconds, Environment.NewLine);
            Debug.Log(builder.ToString());
    
            //認識したキーワードで処理判定
            switch (args.text)
            {
                case "あかあげて":
                    AkaAgete();
                    break;
                case "しろあげて":
                    ShiroAgete();
                    break;
                case "あかさげて":
                    AkaSagete();
                    break;
                case "しろさげて":
                    ShiroSagete();
                    break;
            }
        }
    
        private void AkaAgete()
        {
            RightHandTarget.transform.position = RightHandUpPos;
        }
        private void ShiroAgete()
        {
            LeftHandTarget.transform.position = LeftHandUpPos;
        }
    
        private void AkaSagete()
        {
            RightHandTarget.transform.position = RightHandDownPos;
        }
    
        private void ShiroSagete()
        {
            LeftHandTarget.transform.position = LeftHandDownPos;
        }
    
    }
    

    OnPhraseRecognized의 인수의 키워드로 판정하는 것만이므로 간단하네요!
    Oculus Rift의 마이크는 Virtual Reality Supported에서 Oculus를 지정해 두면 특별한 설정 없이 사용할 수 있습니다.

    마지막으로



    솔직히 평소에 PC를 사용하고 있는 동안에 Cortana씨에게 마이크를 사용해 말을 걸는 것은 별로 없다고 생각합니다. 그러나 Oculus 등의 HMD를 사용하는 동안에는 입력 수단이 현재 상당히 제한되므로 음성 명령은 유효한 UI 중 하나입니다. 꼭 활용해 보세요. ※HMD 입고 음성 커맨드 하고 있으면 꽤 수상한 사람으로 보이므로, 주위에 사람이 없는지 주의가 필요하네요.

    다음 번



    Tech-Circle Hands on Advent Calendar 2016은 다음 주부터 인프라 기술 주간입니다.
    톱 타자는 @papa_dacchi입니다. 기대하세요.

    좋은 웹페이지 즐겨찾기