目次
第 1 章 はじめに 1 第 2 章 本研究の構成要素 2 2.1 音声認識 2 2.2 RAPIRO 2 第 3 章 設計及び製作 5 3.1 音声認識を用いたロボット制御 5 3.1.1 認識実験 5 3.1.2 改善とシステムの設計 5 3.2 システムの詳細 6 第 4 章 システムの動作実験 9 4.1 実験 9 4.2 結果 9 4.3 考察 9 第 5 章 まとめと展望 11 参考文献 12 謝辞 13 付録 1 単語リスト 14 付録 2 文パターンリスト 17 付録 3 音声認識テスト例文 19 付録 4 動作コマンドに書き変えた辞書ファイル 20 付録 5 Julius の設定ファイル 21 付録 6 ロボット制御プログラム① 22 付録 7 ロボット制御プログラム② 26 i第
1 章 はじめに
本研究は、指導教員である白井英俊教授の研究テーマである自然言語処理に ついて研究をしてみようと思いたったことから始まった。そしてすぐに、自然 言語処理・音声認識とロボットを使った研究という方針をたてた。最終的に、 研究の目的を「人間の言葉を聞いて動くロボットをつくる」と定め、次の 3 つ の目標をたてた。 1.音声認識を用いてロボットを動かす 2.文脈に基づいて発話を解釈する 3. 人間に言われるままではなく、ロボットが考えて行動する この 3 つの目標の達成のため、音声認識ではオープンソースでフリーソフト である Julius を用いた。そしてこれらを統合し、音声によってロボットを動かすプログラム作成にはArduino IDE と Python を用いた。Python は Raspberry
Pi を含めたパソコンでサポートされ、機械学習や自然言語処理、画像処理など の様々なモジュールを組み込無事で容易に機能を拡張することができ、かつ学 習しやすいことが採用した理由である。また、使用するロボットをRapiro とし た。この理由は、Rapiro が最近発売された新しいロボットで、目新しいものに 惹かれたこともあるが、Raspberry Pi を搭載でき、小さいながらも拡張性に優 れているからである。Julius と Rapiro の詳しい説明については第 2 章で行う。 研究の結果、音声認識を用いて Rapiro を動かすことには成功した。しかし、 音声認識の精度が悪いなどの様々な問題点も明らかになった。これらの問題点 の検討と、それに対する解決策についても考察を行う。 本論文の構成は以下の通りである。 第2 章では前提の知識として、本研究に用いたロボット Rapiro や音声認識ソ フトJulius について説明する。 第 3 章ではロボットを制御する仕組みと方法について説明する。特に音声認 識ソフトの精度を調べる目的で行った実験とその結果について述べ、この実験 から分かったシステムの設計上考慮すべき問題について述べる。 第 4 章では、第 3 章で挙げた問題点を改善することを目的に行った実験とそ の結果について説明する。 第 5 章では本研究のまとめと展望について述べる。なお、研究で使用した実 験例文や、作成したプログラムは付録として載せた。 1
第 2 章 本研究の構成要素
音声認識によってロボットを制御するという研究目的のため、音声認識ソフト として Julius を、またロボットとして Rapiro を用いた。本章では、それぞれ について説明する。 2.1 音声認識 音声認識とは今までのキーボード入力に代わる新しい文字の入力方法である。 私たちが話す音声言語をコンピュータによって解析し、発話を文字に変換する。 本研究では、Julius(Julius 2014)というフリーで使える高性能音声認識ソ フトを用いた。このソフトの特徴は、不特定話者の音響モデルと汎用言語モデ ルを提供していることである。発音辞書や言語モデル、音響モデルなどの音声 認識の各モジュールを組み替えてさまざまな使い方が可能であり、また、高速 な音声認識を一般的な機能の PC(OS は Windows、Mac iOS, Linux)で実現して いることも特徴である。その認識率は 2 万語彙の読み上げ音声で 90%以上とさ れている。Julius 4.3.1 には GMM (Gausian Mixture model)版と DNN(Deep Neural Net)版とがある。GMM 版は DNN 版と比べ精度が低いが処理は軽いという特徴があ る。それとは逆に DNN 版は GMM 版に比べて高精度だが処理は重い。 この 2 つの違いは「DNN 版には (1)音声の入力、(2)特徴量への変換、 (3) 音声認識をそれぞれ別のプログラムで実行する」(Aikawa 2014)ことである。 しかし、Julius の初期設定の辞書ファイルがとても大きいこともあり、音声を 認識させてから結果が表示されるまでとても一般的な機能の PC では時間がかか る。Julius はマイクから入力された音声をあらかじめ登録されている単語辞書 と比較して、入力音声に最も近い単語を選びだす処理をしている。辞書ファイ ルに登録された膨大な単語の中から最適な言葉を検索しているので、結果が出 るまでに時間がかかるのである、発音が曖昧な音声を別の単語と誤認識してし まう問題がある。そこで本研究では「方向」、「行動」など、ロボットの行動に 関係する単語だけを認識するような辞書を作成し、Julius に登録した。本シス テムは、登録された単語だけを認識し、それに関連付けられた命令によってロ ボットを制御している。辞書を登録するとどのように変わるのか、第 4 章で実 験をする。 2.2 RAPIRO Rapiro とは、ボードコンピュータ Arduino 互換の制御基板により、12 個のサ ーボモーターを用いて、首や胴体、手足を動かすことができる小型の人型ロボ 2ットである。Arduino のプログラムを組むことによってサーボモーターを制御し、 その動きによって前進する、後退する、手を上げる、パンチを繰り出すような 動作をする、さらにはダンスまでさせることができる。また、LED ライトが目に ついているので赤、青、黄、緑などさまざまな色に変えることが出来る。(Rapiro 基板の行動コマンドについては付録 4 参照)さらに PC ボード Raspberry Pi を頭 に組み込めるように設計されており、Raspberry Pi による制御に加えて、声を 出す、音楽を鳴らす、音声を認識する、遠隔操作によりロボットを動かすなど、 さまざまな機能をつけ加えることができる。予め Rapiro に組込まれているプロ グラムでは、表 2.1 に上げるような動作が可能である(#M から始まる動作は、次 の命令が来ない限り、無限に繰り返される): 表 2.1 Rapiro が組込みのプログラムでできる基本動作(カッコ内は目の色)と、 それを実行するための送信命令(文字列) 停止(青) #M0 前進(青) #M1 後退(青) #M2 左旋回(青) #M3 右旋回(青) #M4 両手を横に振る(緑) #M5 右手を縦に振る(黄) #M6 両手を縦に振る(青) #M7 左手を振る(赤) #M8 右手を前に押し出す(パンチ) (青) #M9 左右に頭を動かす #PS00A000T010#PS00A180T010 左右に腰を動かす #PS01A000T010#PS01A180T010 上下に右肩を動かす #PS02A000T010#PS02A180T010 上下に右腕を動かす #PS03A000T010#PS03A180T010 右手を開閉する #PS04A000T010#PS04A180T010 上下に左肩を動かす #PS05A000T010#PS05A180T010 上下に左腕を動かす #PS06A000T010#PS06A180T010 左手を開閉する #PS07A000T010#PS07A180T010 右足を左右にねじる #PS08A000T010#PS08A180T010 右足を上下に動かす #PS09A000T010#PS09A180T010 左足を左右にねじる #PS10A000T010#PS10A180T010 左足を上下に動かす #PS11A000T010#PS11A180T010 3
これを踏まえて、我々は、このロボットに次のような行動を行わせることを 考え、それを音声で指示するシステムの実現を目指した:前進する、後退する、 手を上げる、パンチを出す。またこの動きを無限に繰り返すのではなく、指定 された回数だけ繰り返す。さらに、直前の命令に基づいて「もうちょっと」、「も う少し」のような命令を適切に解釈し、実行すること。 これらの動きを音声によって実行させるためのシステム作成については次章 で述べる。 4
第
3 章 設計及び製作
本章では実際に音声認識ソフト(Julius)を用いてロボット制御をしていく過程 と、その実験について述べ、実験で明らかになった問題と、それに対する改善 策を述べる。 3.1 音声認識を用いたロボット制御 本研究で設計した「音声認識を用いたロボット制御」は、次の要素から構成 されるものである。 (1) マイクで音声をパソコンに入力、音声認識ソフトで言葉を読み取る (2) 読み取った言葉をロボットの行動命令を表す単語列に変換し、パソコンか らロボットに送信する (3) ロボットの制御システムがパソコンからの行動命令を受け取り、その命令 に従って、サーボモーターを動かす。 ここで、それぞれには次のような拡張可能性が考えられる。(1)に対しては、 パソコンではなく、ロボットに Raspberry Pi を載せて、それが音声認識する。 (2)に対しては、読み取った言葉が誤認識などにより無意味と考えられる場合に どのように対処するか、また「もうちょっと」「もう少し」「まだまだ」「やり直 し」「もとに戻せ」のように前の命令やロボットとの対話文脈に依存する命令を 解釈する。(3)に対しては、2.2 節で述べた Rapiro に組み込みの動作以外の行動 (例、ダンス・カメラを搭載しての撮影と保存、送信・右手上げる左手下げると いうゲーム)を可能にすることである。これらの拡張については 5 章で検討する。 3.1.1 認識実験 まず 3.1 節(1)の「マイクで音声をパソコンに入力、音声認識ソフトで言葉を 読み取る」ことがどの程度実現できているかを調べるため、ロボット制御に用 いようとする文に対するJulius の認識率を調べた。具体的には 26 例文(付録 3 参照)を用意し、5 名の研究協力者にランダムな順番で発話してもらい、 Julius(grammar-kit 4.2-win)による認識率を調べた。平均では 26 例文中 5.8 文 の認識ができた(およそ 22%の文認識率)。特徴的な誤りとして、同音異義語の間 違いがあった。例えば「三歩 前に 進め」を「散歩 前に 進め」と誤認識 した。また、周囲のノイズを拾って何も言っていないのに言葉に変換して、読 み上げた単語以外の単語が認識結果とされたことがあった。 3.1.2 改善とシステムの設計 5認識実験の結果、デフォルトで用意されているJulius の認識率が 22%と、と ても低いことがわかったので、音声認識率が上がるように改善策を考えた。 原因の多くは誤認識する同音異義語が多いこと、それにロボットの行動に無 関係な単語が誤認識されてしまうことであった。これを防ぐために、(1) Julius の認識する単語をロボット制御に用いるもの(方向、距離、行為などを表す単 語、およそ80 語。付録 1 参照)に制限、(2) 認識する文のパターンも同様にロボ ット制御に用いる文パターンに制限(付録2 参照)することにした。 具体的には次の作業を行った。(1)ロボットに行わせたい動きは前進、後退な ど2.2 節で述べたとおりである。この制御に必要な単語は「進め」や「5」など の「ロボットの行動の方向」、「距離」、「行為」の 3 種類、および「もうちょっ と」や「もう少し」などのキーワード(第 5 章参照)のリストを作成した。そ れらの単語を、対応する音素の並びとともに Rapiro.dic(付録 4 参照)として ファイルを作成した。これにより副次効果として、「三歩」と「散歩」のような 同音異義語の可能性を排除することができた。(2)認識する文のパターンの制限 も行った。本研究ではあまり複雑な文を扱わず、ロボットに行わせたい動きを 表す単文だけを対象とした。例えば「前に 進め」(これは「方向 行為」のパ ターン)、「3 歩 進め」(これは「距離 行為」のパターン)、「ラピロ 前に 進 め」(これは「名前 方向 行為」のパターン)である。これに加えて「もうちょ っと」や「もう少し」のような過去の発話に依存した動きも扱えるように考え た。 なお、Rapiro に組み込みのプログラムを修正し(付録 7 参照)、Rapiro に組み 込みの前進、後退などの基本動作を「1 回」限り行うようにした。それにより「3 歩」などの動作回数が命令されれば、基本動作を指定回数繰り返すことができ るようになった。
3.2 システムの詳細
本研究の「音声認識によるロボット」は次のように動くよう設計した: (1) Rapiro と PC を USB ケーブルで繋ぎ、Rapiro の電源を入れる。 (2) 音声認識で使うマイクを PC に繋ぎ、マイクの音量調整を行う。 (3) Julius を同じ PC 上のモジュールモードで立ち上げた後で、ロボット制御の ためのPython プログラムを立ち上げる。 (4) 付録 1 の単語を用いて付録 2 にある文パターンで発話する。例えば「前に 3 歩 進め」: つまり、「ロボットの行動の方向」、「距離(歩数)」、「行為」か らなる文をマイクへ発話する. (5) Rapiro がその発話通りに動く 6例えば、3 歩前に進ませたいときには「3 歩 前に 進め」と発話すると、 前に進む動きの「#M1」(表 2.1 参照)を 3 回繰り返し 3 歩だけ前に進む。4 歩後 ろに下がらせたいときには「4 歩 後ろに 進め」と発話すると、後ろに進む動 きの「#M2」を 4 回繰り返し 4 歩だけ後ろに下がる。まだ足りなくてもう少し 下がらせたいときには「もうちょっと 後ろ」と発話すると、先に 4 歩進んだ ので、その半分の2 歩さらに下がる。 これらはプログラムリスト3.1 の(B)で行われている。関数 catchSent (詳細 は付録6 参照) により Julius が認識した文(単語のリスト)が得られる。次に 関数analyze(詳細は付録 6 参照)で、動きと回数を得る。これにより変数 count に は回数が代入され、変数move には動きを表す値が代入される。例えば、「3 歩 前に 進め」が入力された場合は、count には 3、move には 1 が入る。なお、 move の値として、「停止」なら 0 「後退」なら 1、「ちょっと」なら”R” など を定めた。ここでの工夫として、複数回の動作をするとき、それらが滑らかに 連続した動作となるよう、動作ごとに待ち時間を設定した(プログラムの(A))。 プログラムリスト3.1 (全体は付録 6 参照) def main(): host = 'localhost' # どこで動かすのか port = 10500 # ポート番号
clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientsock.connect((host, port)) # Julius と通信を行う
import serial
ser = serial.Serial('COM32',57600) # Rapiro と通信を行う normalMovement = range(4) # 基本動作 4 つ lastCommand = (0, 0) # 過去の発話の動き discount = 0.5 # 繰り返し動かすための値 waitingTime = [0.5, 3.5, 4.0, 4.0, 4.0, 5,0, 2.5, 5.0, 2.5, 8.5] # 各動作 1 回 ごとの待機時間 while True: sent = catchSent(clientsock) show(sent)
result = analyze(sent) # Julius から受け取った文を動きに変える
(A)
if ("ラピロ" in sent): # 「ラピロ」という呼びかけがないと動かない (move, count) = result
print "move = ",move, "count = ",count if (move == "R"):
move = lastCommand[0]
count = int(max([1, lastCommand[1]*discount])) # R なら
先の動きの1/2 動く
elif (count == "R"):
count = int(max([1, lastCommand[1]*discount])) # R なら
先の動きの1/2 動く
elif (move in normalMovement) and (count == 0): count = 1
else: pass #
if (move in normalMovement):
lastCommand = (dir, count) # memorize #
for i in range(count): # change if count == 0 ser.write("#M"+str(move))
time.sleep(waitingTime[move])
(B)
第
4 章 システムの動作実験
本章では第 3 章で述べたシステムの動作実験について述べる。その結果と新 たに見つかった問題点について考察し、その問題点に対する解決策について考 える。 4.1 実験 第2 章で述べたように、Julius にはとても大きな辞書ファイルがあるため誤 認識を生み、影響を与えている。では、辞書を作るとどのような違いが生まれ るのか実験をしてみる。26 例文を用意し、5 名の研究協力者に発話してもらっ た。すると、改良前に26 例文中で認識文数が平均 5.8、分散 0.2 だったものの、 改良後には26 例文中で認識文数が平均 20.2、分散 1.7 となった。t 検定の結果 は t = -23.4 df = 4.93 p-value =3.1×10−6となり、有意水準0.1%以下で、 有意な差があったので改善されたといえる。 また、「もう少し」や「もうちょっと」の使った実験では、「もう少し」や「も うちょっと」と言うことによって、直前の動きと同じ動作(前進、後退)を、 プログラム通りにその前の距離の半分程度の距離を動くことが確認できた。 4.2 結果 前に挙げた3 つの目標を達成できているか考えてみる。1 つ目の「音声認識を 用いてロボットを動かす」は実際に音声でロボットを動かすことに成功したの で達成できたといえる。2 つ目の「文脈に基づく発話の解釈」は「もう少し」や 「もうちょっと」の言葉の認識からプログラム通りに直前の動作と同じ動作で 半分の距離だけ動いたことから、これも達成できたといえよう。ただし、『もう 少し』や「もうちょと」の解釈には文脈や個人差があり、さらなる研究が必要 であろう。3 つ目の「ロボットが考えて行動する」は、人間の発話によってロボ ットが動いているのであり、ロボット自身で考えて動いているとはいえないこ とから、達成したとは考えられない。 なお、実験をして気づいたことであるが、スイッチを入れたままにして他の 人と会話をしていると、ロボットが動き出すということが度々あった。これは、 音声認識ソフトが周囲の言葉を拾い、それを命令としてロボットに送ったため であろう。これを防止する方法を考えることが必要である。 4.3 考察 「ロボットが考えて行動する」についての解決策を考える。それには 2 つ通 9りの方法があると考える。その 1 つ目は、距離センサーやカメラを用いて障害 物を検知することにより、ロボット自身が危険を検知して、その動きを変える ことである。これにより、例えば崖の端で人間が『前進せよ』と命令しても、 それに背いて前進しない、ということが可能になる。 2 つ目は、カメラを搭載し、より細かい動きの指示がこちら側からできるように する工夫である。 10
第
5 章 まとめと展望
本章では本研究のまとめと今後の展望について述べる。 本研究では、音声認識を用いてRapiro を動かすことには成功した。音声認識 によってロボットを動かすまでの過程として、Arduino や Python プログラムに ついて深く学ぶことができた。この研究が後輩たちに引き継がれていつか世の 中に役立つものになってほしいと願っている。 今後の展望として、5 点挙げられる。1. Raspberry Pi を Rapiro に搭載し Raspberry Pi によって無線(Wi-Fi)で命令を
送れるようにすること。(現在は USB ケーブルによるシリアル通信を用いて いる) この課題が達成できることによって、今まで有線で通信していたものが無線に なり、動ける範囲が格段に広がる。 2. Rapiro の動きの種類を増やす。(ダンスする、歌う、など) これまで以上に可能な動きを増やすようにプログラムしていく。また、そのた めにはその動きをさせるための単語、文パターンをさらに作成していく必要が ある。 3. Rapiroの動きの種類を増やしても音声認識率はさらに上げていく。(78%以上) 発話をできるだけ正確に読み取れるように音声認識率をさらに上げる必要が ある。考えられる方法としては、音響ファイルを既存のものに頼らずに新たに 作るなどが挙げられる。また、本研究の失敗点として付録3 にある音声認識テ ストの何が読み取れて、何が読み取れていなかったのかをしっかり記録してお くべきであった。読み取りにくかったものからどのように改善していくかの手 がかりになると考える。 4. 動きに関係のない発話に反応しないような工夫をする。(キーワードを付けな いと動かないようにする、など) 電源を入れたまま会話をしているとその会話を指令の発話だと勘違いし勝手 に動いてしまうことがあった。ので、「Rapiro 前に 進め」というようにキ ーワードを決めることが必要なのではないかと考える。 5. カメラを積んで写真を撮ったり、データを送ったりできるようにすること。 映像を記録することで様々な用途に使うことが出来る。ユーザー間の情報伝達 や、Rapiro 目線でしか分からない危険の発見に気づくことのきっかけになる かもしれないと考える。 11
参考文献
Aikawa, N. (2014) DNN による音声認識の実行
https://github.com/awakia/julius-dictation-kit-v4.3.1-osx/blob/master/HOWTO-dnn. txt (2014年5 月アクセス)
Julius (2014) http://julius.sourceforge.jp/ (2014 年 5 月アクセス) Miyazawa, K. (2014) Julius と Julian
http://shower.human.waseda.ac.jp/~m-kouki/pukiwiki_public/24.html Rapiro (2014a) http://www.rapiro.com/ja/ (2014 年 5 月アクセス)
Rapiro (2014b) http://wiki.rapiro.com/page/index/ (2014 年 5 月アクセス) Rapiro (2014c) https://www.facebook.com/project.rapiro/timeline (2014 年 11
月アクセス)
謝辞
本研究の作成にあたり、厳しくも優しいご指導を賜りました白井英俊教授に 深謝いたします。また、本研究を温かく支えてくれた白井研究室の同期や後輩 の皆様に感謝いたします。
付録
1 単語リスト
%SONOTA えーと e: えーと e: t o えーと e: q t o えーと e: q t o: えーと a: えーと a: t o えーと a: q t o えーと N N えーと N N t o えーと N N q t o えーと a n o: えーと s o n o: えーと t o えーと q t o えーと u: N えーと u: N t o %NOISE NS sp %B silB silB %E silE silE %HOUKOU 前 m a e n i 前 m a e 前 m a e e 後ろ u sh i r o n i 後ろ u sh i r o 14後ろ u sh i r o e 右 m i g i e 右 m i g i n i 右 m i g i 左 h i d a r i n i 左 h i d a r i 左 h i d a r i e %PANCHI パンチ p a N ch i %HUKUSI もう m o u もう m o: %KAZU 一 i ch i 一 i q 二 n i 二 n i: 三 s a N 四 y o N 五 g o 五 g o: 六 r o k u 六 r o q 七 n a n a 七 sh i ch i 七 h i ch i 八 h a ch i 八 h a q 九 ky u: 九 k u 十 j u: 十 j u q 15
%ZYOSUSI ほ p o ほ h o %UGOKI 進め s u s u m e 進め s u s u m u 行け i k e 行け i k u 行け g o: まわれ m a w a r e まわれ m a w a r u まわれ m a g a r e まわれ m a g a r u とまれ t o m a r e とまれ t o m a r u とまれ s u t o q p u さがれ s a g a r e さがれ s a g a r u さがれ b a q k u %CHOTTO ちょっと ch o q t o もっと m o q t o ちょっと ch o i ちょっと m o u ch o q t o %NAMAE ラピロ p i r o ラピロ p i l o ラピロ r a p i r o 16
付録
2 文パターンリスト
S : B SONOTA NOISE HOUKOU NOISE HUKUSI NOISE KYORI
NOISE UGOKI E
S : B SONOTA NOISE HOUKOU NOISE KYORI NOISE UGOKI E
S : B SONOTA NOISE HUKUSI NOISE KYORI NOISE UGOKI E
S : B SONOTA NOISE KYORI NOISE UGOKI E
S : B SONOTA NOISE HOUKOU NOISE UGOKI E
S : B SONOTA NOISE UGOKI E
S : B HOUKOU NOISE HUKUSI NOISE KYORI NOISE UGOKI E
S : B HOUKOU NOISE KYORI NOISE UGOKI E
S : B HUKUSI NOISE KYORI NOISE UGOKI E
S : B KYORI NOISE UGOKI E
S : B HOUKOU NOISE UGOKI E
S : B UGOKI E
S : B HUKUSI NOISE KYORI E
S : B SONOTA NOISE HOUKOU NOISE HUKUSI NOISE KYORI E
S : B SONOTA NOISE HOUKOU NOISE KYORI E
S : B SONOTA NOISE HUKUSI NOISE KYORI E
S : B SONOTA NOISE KYORI E
S : B SONOTA NOISE HOUKOU E
S : B HOUKOU NOISE HUKUSI NOISE KYORI E
S : B HOUKOU NOISE KYORI E
S : B HUKUSI NOISE KYORI E
S : B KYORI HOUKOU E
S : B PANCHI E
S : B NAMAE SONOTA NOISE HOUKOU NOISE HUKUSI NOISE
KYORI NOISE UGOKI E
S : B NAMAE SONOTA NOISE HOUKOU NOISE KYORI NOISE
UGOKI E
S : B NAMAE SONOTA NOISE HUKUSI NOISE KYORI NOISE
UGOKI E
S : B NAMAE SONOTA NOISE KYORI NOISE UGOKI E
S : B NAMAE SONOTA NOISE HOUKOU NOISE UGOKI E
S : B NAMAE SONOTA NOISE UGOKI E
S : B NAMAE HOUKOU NOISE HUKUSI NOISE KYORI NOISE
UGOKI E
S : B NAMAE HOUKOU NOISE KYORI NOISE UGOKI E
S : B NAMAE HUKUSI NOISE KYORI NOISE UGOKI E
S : B NAMAE KYORI NOISE UGOKI E
S : B NAMAE HOUKOU NOISE UGOKI E
S : B NAMAE UGOKI E
S : B NAMAE HUKUSI NOISE KYORI E
S : B NAMAE SONOTA NOISE HOUKOU NOISE HUKUSI NOISE
KYORI E
S : B NAMAE SONOTA NOISE HOUKOU NOISE KYORI E
S : B NAMAE SONOTA NOISE HUKUSI NOISE KYORI E
S : B NAMAE SONOTA NOISE KYORI E
S : B NAMAE SONOTA NOISE HOUKOU E
S : B NAMAE HOUKOU NOISE HUKUSI NOISE KYORI E
S : B NAMAE HOUKOU NOISE KYORI E
S : B NAMAE HUKUSI NOISE KYORI E
S : B NAMAE KYORI HOUKOU E
S : B NAMAE PANCHI E
KYORI : KAZU ZYOSUSI KYORI : CHOTTO
付録
3 音声認識テスト例文
1. 五歩 さがれ 2. 二歩 右 3. とまれ 4. ストップ 5. 一歩 進め 6. 一歩 左 7. もうちょっと 進め 8. 三歩 さがれ 9. 三歩 進め 10. 一歩 右 11. 四歩 さがれ 12. 四歩 左 13. 二歩 左 14. もうちょっと 右 15. 二歩 進め 16. 一歩 さがれ 17. もうちょっと さがれ 18. 四歩 右 19. 四歩 進め 20. 五歩 進め 21. 三歩 右 22. 五歩 左 23. もうちょっと 左 24. 三歩 左 25. 五歩 右 26. 二歩 さがれ 19付録 4 動作コマンドに書き換えた辞書ファイル
<sil> silB //文頭の無音 <sil> silE //文末の無音 <sp> sp //無音 #M0 t e i sh i #M1 z e N sh i N #M2 k o u t a i #M3 h i d a r i #M4 m i g i #M9 p a N ch i 20付録 5 Julius の設定ファイル
-gram rapiro
-C ../hmm_ptm.jconf -input mic -demo
付録 6 ロボット制御プログラム①
「Trial4b.py」 #!/usr/bin/env python # -*- coding: shift-jis -*- import socket, re, time import serial def show(line): for x in line: print x," ", print def catchSent(clientsock): flag=False answer=[] while True: recv_data = clientsock.recv(512) # print ">>",recv_data if re.search('<RECOGOUT>',recv_data): flag=True elif (flag==True): tmp = re.findall('<WHYPO WORD="[^"]+"',recv_data) # print ">>> ",tmp for one in tmp: answer.append(one.split('"')[1]) if re.search('</RECOGOUT>',recv_data): return(answer) def analyze(sent): move = "S" count = 1 # if ("ラピロ" in sent): 22
if ("とまれ" in sent) or ("STOP" in sent) or ("止まれ" in sent): move = 0
elif ("行け" in sent) or ("進め" in sent) or ("前" in sent): move = 1
elif ("後ろ" in sent) or ("さがれ" in sent): move = 2 elif "左" in sent: move = 3 elif "右" in sent: move = 4 elif "パンチ" in sent: move = 9
elif ("ちょっと" in sent) or ("少し" in sent) or ("もっと" in sent): move = "R"
else:
move = 8
hosuu = ["一","二","三","四","五"] # ここで繰り返す歩数を決めている if ("ちょっと" in sent) or ("少し" in sent) or ("もっと" in sent):
count = "R" # 「ちょっと」「少し」「もっと」という言葉があれば else: # 「R」という文字を伝える for x in range(len(hosuu)): if hosuu[x] in sent: count = x+1 break # return( (move,count) ) def main(): host = 'localhost' port = 10500
clientsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientsock.connect((host, port))
import serial
ser = serial.Serial('COM32',57600)
# robot = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # robot.connect((RobotHost, RobotPort)) # com = serial.Serial('/dev/ttyAMA0',57600,timeout=10) normalMovement = range(4) lastCommand = (0, 0) discount = 0.5 waitingTime = [0.5, 3.5, 4.0, 4.0, 4.0, 5,0, 2.5, 5.0, 2.5, 8.5] while True: sent = catchSent(clientsock) show(sent) # print "==>", analyze(sent) result = analyze(sent) # ser.write(analyze(sent)) # move = result[0] # count = result[1] if ("ラピロ" in sent):
(move, count) = result
print "move = ",move, "count = ",count if (move == "R"):
move = lastCommand[0]
count = int(max([1, lastCommand[1]*discount])) elif (count == "R"):
count = int(max([1, lastCommand[1]*discount])) elif (move in normalMovement) and (count == 0): count = 1
else: pass #
if (move in normalMovement):
lastCommand = (dir, count) # memorize #
for i in range(count): # change if count == 0 ser.write("#M"+str(move)) time.sleep(waitingTime[move]) # com.write(analyze(sent)) if (__name__ == "__main__"): main() 25
付録
7 ロボット制御プログラム②
「rapiro-ugoku」
// by ShotaIshiwatari is licensed under the Creative Commons - Public Domain Dedication license.
#include <Servo.h> #define SHIFT 7
#define R 0 // Red LED #define G 1 // Green LED #define B 2 // Blue LED
#define TIME 15 // Column of Time
#define MAXSN 12 // Max Number of Servos #define MAXMN 10 // Max Number of Motions #define MAXFN 8 // Max Number of Frames
#define POWER 17 // Servo power supply control pin #define ERR -1 // Error
int i = 0; int t = 1;
Servo servo[MAXSN]; uint8_t eyes[3] = { 0, 0, 0};
// Fine angle adjustments (degrees) int trim[MAXSN] = { 10, // Head yaw 10, // Waist yaw 50, // R Sholder roll 0, // R Sholder pitch 0, // R Hand grip 0, // L Sholder roll 10, // L Sholder pitch 0, // L Hand grip 5, // R Foot yaw -4, // R Foot pitch 26
0, // L Foot yaw -8}; // L Foot pitch
int nowAngle[MAXSN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initialize array to 0
int targetAngle[MAXSN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initialize array to 0
int deltaAngle[MAXSN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initialize array to 0
uint8_t bufferAngle[MAXSN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initialize array to 0
uint8_t tempAngle[MAXSN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Initialize array to 0
int nowBright[3] = { 0, 0, 0}; // Initialize array to 0 int targetBright[3] = { 0, 0, 0}; // Initialize array to 0 int deltaBright[3] = { 0, 0, 0}; // Initialize array to 0 uint8_t bufferBright[3] = { 0, 0, 0}; // Initialize array to 0 uint8_t tempBright[3] = { 0, 0, 0}; // Initialize array to 0
double startTime = 0; // Motion start time(msec) double endTime = 0; // Motion end time(msec)
int remainingTime = 0; // Motion remaining time(msec) uint8_t bufferTime = 0; // Motion buffer time (0.1sec) uint8_t motionNumber = 0; uint8_t frameNumber = 0; char mode = 'M'; uint8_t motion[MAXMN][MAXFN][16]={ { // 0 Stop { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0,255, 10}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, 27
{ 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 1 Forward { 90, 90, 0, 90, 90,180, 90, 90, 80,110, 80,120, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 70, 90, 70, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 70, 70, 70, 80, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90,100, 60,100, 70, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90,110, 90,110, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90,110,100,110,110, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 2 Back { 90, 90, 0, 90, 90,180, 90, 90,100,110,100,120, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90,110, 90,110, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90,110, 70,110, 80, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 80, 30, 80, 70, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 70, 90, 70, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 70,100, 70,110, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 3 Right { 90, 90, 0, 90, 90,180, 90, 90, 95,110, 85,120, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90,100, 90, 80, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90,100, 70, 80, 80, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 85, 60, 95, 70, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 80, 90,100, 90, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 80,100,100,110, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 4 Left { 90, 90, 0, 90, 90,180, 90, 90, 95, 60, 85, 70, 0, 0, 0, 5}, 28
{ 90, 90, 0, 90, 90,180, 90, 90,100, 90, 80, 90, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90,100,100, 80,110, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 85,110, 95,120, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 80, 90,100, 90, 0, 0, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 80, 70,100, 80, 0, 0,255, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 5 Green { 90, 90,120, 90, 90, 60, 90, 90, 90, 90, 90, 90, 0, 0, 0, 10}, {100, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0,255, 0, 5}, { 90, 90,120, 90, 90, 60, 90, 90, 90, 90, 90, 90, 0,255, 0, 5}, { 80, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0, 0, 0, 5}, { 90, 90,120, 90, 90, 60, 90, 90, 90, 90, 90, 90, 0,255, 0, 10}, { 90, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0,255, 0, 5}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 6 Yellow { 90,120,120,130, 90,180, 90, 90, 90, 90, 90, 90,255,255, 0, 7}, { 90,120,120, 90, 90,180, 90, 90, 90, 90, 90, 90,255,255, 0, 7}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 7 Blue { 90, 90,120,130, 70, 60, 50,110, 90, 90, 90, 90, 0, 0,255, 10}, { 90, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0, 0,255, 5}, { 90, 90,120,130, 70, 60, 50,110, 90, 90, 90, 90, 0, 0,255, 5}, { 90, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0, 0,255, 5}, { 90, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0, 0,255, 15}, { 90, 90, 90,130,110, 90, 50, 70, 90, 90, 90, 90, 0, 0,255, 3}, { 90, 90,120,130,110, 60, 50, 70, 90, 90, 90, 90, 0, 0,255, 3}, 29
{ 90, 90, 90,130,110, 90, 50, 70, 90, 90, 90, 90, 0, 0,255, 3} }, { // 8 Red { 90, 60, 0, 90, 90, 60, 50, 90, 90, 90, 90, 90,255, 0, 0, 7}, { 90, 60, 0, 90, 90, 60, 90, 90, 90, 90, 90, 90,255, 0, 0, 7}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} }, { // 9 Push { 90, 90, 90,130,110,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 10}, { 90, 90, 90,130,110,180, 50, 90, 90, 90, 90, 90, 0, 0,255, 5}, { 90, 90, 90,130,110,180, 50, 90, 90, 90, 90, 90, 0, 0,255, 25}, { 90, 90, 90,130, 90,180, 50, 90, 90, 90, 90, 90, 0, 0, 0, 5}, { 40,140, 90, 70, 90,180, 90, 90, 90, 90, 90, 90, 0, 0,255, 10}, { 40,140, 90, 70, 90,180, 90, 90, 90, 90, 90, 90, 0, 0,255, 25}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0}, { 90, 90, 0, 90, 90,180, 90, 90, 90, 90, 90, 90, 0, 0, 0, 0} } }; void setup() {
servo[0].attach(10); // Head yaw servo[1].attach(11); // Waist yaw servo[2].attach(9); // R Sholder roll servo[3].attach(8); // R Sholder pitch servo[4].attach(7); // R Hand grip servo[5].attach(12); // L Sholder roll servo[6].attach(13); // L Sholder pitch servo[7].attach(14); // L Hand grip servo[8].attach(4); // R Foot yaw servo[9].attach(2); // R Foot pitch servo[10].attach(15); // L Foot yaw
servo[11].attach(16); // L Foot pitch eyes[R] = 6; // Red LED of eyes eyes[G] = 5; // Green LED of eyes eyes[B] = 3; // Blue LED of eyes
for( i = 0; i < MAXSN; i++) {
targetAngle[i] = motion[0][0][i] << SHIFT; nowAngle[i] = targetAngle[i];
servo[i].write((nowAngle[i] >> SHIFT) + trim[i]); }
for(i = 0; i < 3; i++) {
targetBright[i] = 0 << SHIFT; nowBright[i] = targetBright[i];
analogWrite(eyes[i], nowBright[i] >> SHIFT); } Serial.begin(57600); delay(500); pinMode(POWER, OUTPUT); digitalWrite(POWER, HIGH); } void loop() { int buf = ERR;
if(Serial.available()) { if(Serial.read() == '#') { while(!Serial.available()){} switch(Serial.read()) { case 'M': buf = readOneDigit(); if(buf != ERR){ motionNumber = buf; mode = 'M'; digitalWrite(POWER, HIGH); 31
Serial.print("#M"); Serial.print(motionNumber); } else { Serial.print("#EM"); } break; case 'P': buf = getPose(); if(buf != ERR) { mode = 'P'; digitalWrite(POWER, HIGH); Serial.print("#PT"); printThreeDigit(buf); } else { Serial.print("#EP"); } break; case 'Q': Serial.print("#Q"); if(mode == 'M') { Serial.print("M"); Serial.print(motionNumber); Serial.print("T"); buf = (endTime-millis()) /100; if(buf < 0) { buf = 0;} printThreeDigit(buf); } if(mode == 'P') { Serial.print("PT"); buf = (endTime-millis()) /100; if(buf < 0) { buf = 0;} printThreeDigit(buf); } break; case 'C': Serial.print("#C"); 32
if(bufferTime > 0) { Serial.print("F"); } else { Serial.print("0"); } break; default: Serial.print("#E"); break; } } } if(endTime > millis()) {
remainingTime = (endTime - millis()) / 10; for( i = 0; i < MAXSN; i++) {
nowAngle[i] = targetAngle[i] - (deltaAngle[i] * remainingTime); servo[i].write((nowAngle[i] >> SHIFT) + trim[i]);
}
for( i = 0; i < 3; i++) {
nowBright[i] = targetBright[i] - (deltaBright[i] * remainingTime); analogWrite(eyes[i], nowBright[i] >> SHIFT);
} } else if(mode == 'M') { nextFrame(); } else if(mode == 'P') { if(bufferTime > 0){ nextPose();
} else if(endTime + 500 < millis()){ //digitalWrite(POWER, LOW); } } } //Motion Play void nextFrame() { frameNumber++; 33
if(frameNumber >= MAXFN) {
// frameNumber = 0; //ここをコメントアウトして motionNumber = 0; //この
return; //2 つを書き加えると動きのループが止まる }
for(i = 0; i < MAXSN; i++) {
bufferAngle[i] = motion[motionNumber][frameNumber][i]; } for( i = 0; i < 3; i++) { bufferBright[i] = motion[motionNumber][frameNumber][MAXSN+i]; } bufferTime = motion[motionNumber][frameNumber][TIME]; nextPose(); } //Make a pose int nextPose() { if(bufferTime > 0) {
for(i = 0; i < MAXSN; i++) {
targetAngle[i] = bufferAngle[i] << SHIFT;
deltaAngle[i] = ((bufferAngle[i] << SHIFT) - nowAngle[i]) / (bufferTime * 10);
}
for( i = 0; i < 3; i++) {
targetBright[i] = bufferBright[i] << SHIFT;
deltaBright[i] = ((bufferBright[i] << SHIFT) - nowBright[i]) / (bufferTime * 10);
} } else {
for(i = 0; i < MAXSN; i++) { deltaAngle[i] = 0; } for(i = 0; i < 3; i++) { deltaBright[i] = 0; } 34
}
startTime = millis();
endTime = startTime + (bufferTime * 100); bufferTime = 0;
}
//get buffer values of the next pose from serial data int getPose() {
int buf = 0; int value = 0;
int maximum = 255; boolean readPose = true; if(bufferTime == 0) {
//Initialize array to target angle for(i = 0; i < MAXSN; i++) { tempAngle[i] = bufferAngle[i]; } for( i = 0; i < 3; i++) { tempBright[i] = bufferBright[i]; } } else { buf = ERR; readPose = false; } //Read data while(readPose) { while(!Serial.available()) {} switch(Serial.read()) { case 'S': buf = readOneDigit(); if(buf != ERR) { value = buf *10; buf = readOneDigit(); if(buf != ERR) { value += buf;
if(0 <= value && value < MAXSN) {
while(!Serial.available()) {} if(Serial.read() == 'A') { maximum = 180; buf = readThreeDigit(maximum); if(buf != ERR) { tempAngle[value] = buf; } else { readPose = false; } } else { buf = ERR; readPose = false; } } else { buf = ERR; readPose = false; } } } break; case 'R': maximum = 255; buf = readThreeDigit(maximum); if(buf != ERR) { tempBright[R] = buf; } else { readPose = false; } break; case 'G': maximum = 255; buf = readThreeDigit(maximum); if(buf != ERR) { tempBright[G] = buf; } else { readPose = false; 36
} break; case 'B': maximum = 255; buf = readThreeDigit(maximum); if(buf != ERR) { tempBright[B] = buf; } else { readPose = false; } break; case 'T': maximum = 255; buf = readThreeDigit(maximum); if(buf > 0) { bufferTime = buf;
for(i = 0; i < MAXSN; i++){ bufferAngle[i] = tempAngle[i]; } for( i = 0; i < 3; i++) { bufferBright[i] = tempBright[i]; } } readPose = false; break; default: buf = ERR; readPose = false; break; } } return buf; }
int printThreeDigit(int buf) { String s = String(buf);
if(s.length() == 2){ Serial.print("0"); } else if (s.length() == 1) { Serial.print("00"); } Serial.print(s); } int digit;
//Read ASCII Three-digit
int readThreeDigit(int maximum) { int buf; buf = readOneDigit(); if(buf != ERR) { digit = buf * 100; buf = readOneDigit(); if(buf != ERR) { digit += buf * 10; buf = readOneDigit(); if(buf != ERR) { digit += buf; if(digit <= maximum) { buf = digit; } else { buf = ERR; } } } } return buf; }
//Read ASCII One-digit int readOneDigit() { int buf;
while(!Serial.available()) {}
buf = Serial.read() - 48; if(buf < 0 || 9 < buf){ buf = ERR; } return buf; } 39