ロボホンアプリ開発ことはじめ
ここではロボホンでのアプリ開発について解説していきます。モバイル型ロボット電話のロボホンがどんな仕組みで動いているのか知りたい人、ロボホンでアプリを開発したい人のための記事です。
ロボホンは独自の音声ユーザーインターフェイスやプロジェクター、アドレス帳APIを備えています。背中には液晶を備え、Android 5.0を搭載しており、開発環境もAndroid Studioが使えます。Androidアプリの開発経験があれば知識がそのまま使えることも多く、経験が活きるでしょう。
一方、普段の開発と異なる点としてユーザーインターフェイスは画面ではなく音声による会話が主体であることがあげられます。ロボホンではロボットらしく振る舞うために音声入力&応答用のAPIが充実しているのです。
アプリ開発においても、自然なインタラクションを提供することが重要なポイントとなります。このあたりの変化はモバイルアプリ開発に長けたひとほど戸惑う点であり、また面白く感じる点でもあるでしょう。デバッグや動作確認ではロボホンに話しかけては返事を調整することが多く、ロボットそのものを開発しているような感覚が湧いてきます。
ここからは開発環境、サンプルコード、オリジナルアプリと順番に進みながらロボホンのアプリ開発をざっくり紹介していきます。
開発環境
シャープのロボホン公式サイトではロボホンの開発環境であるRoBoHoN SDKを配布しています。当然、ダウンロードしてセットアップするところから開発が始まります。執筆時点での最新バージョンは1.0.1、入手するためにアカウント登録が必要です。
※残念ながらエミュレータの提供はありませんのでアプリ開発にはロボホン本体が必要です。
SDKにはサンプルプロジェクトや新規プロジェクト用のテンプレートが用意されているなど導入でつまづかないように工夫されています…と言いたいのですが、執筆時点の最新版であるAndroid Studio 2.2ではテンプレートを利用してもエラーが発生してプロジェクトが作成できません。このあたりは独自SDKゆえのトラブルと言えます。RoBoHoN SDKは2016年6月29日の初版リリース以降、9月15日にアップデートされるなど改善が行われています。このようなエラーも今後、対策されると思いますので当該サイトを確認して最新版を試してみてください。
筆者はSDKを導入するにあたってAndroid Studio 2.2でサンプルアプリの動作を確認しました。インストール方法はSDK同梱のReadme.txtを確認してください。手順そのものに難しいところはありませんが上記のトラブルのほか、手順の途中でAndroid Studioのインストールフォルダ内のPluginsファイルを上書きします。オリジナルをバックアップするか、普段の開発に影響がないように別途Android Studioをインストールして共存させるとよいでしょう。
音声を認識/発話するHVML
ロボホンアプリの音声認識と応答はHVML(Hyper Voice Markup Language)というXMLをベースとした言語で記述します。初見で「あっこんなところにもXMLが!」と驚くかもしれませんが、Androidでは馴染み深いレイアウトXMLのようなもので、インターフェイス部分をソースコードから分離するための構造です。HVMLでは、XML内で変数が使えるなどData Bindingっぽい挙動も定義されています(後述)。シャープの開発者さんによればHVMLは、ココロボなど喋るプロダクトと共通の仕組みとのことでした。
さっそく音声を認識し、ロボホンが発話するサンプルプロジェクト「SampleSimple」をみてみましょう。
-> SDK/0701_SR01MW_SampleCode_V01_00_01.zip/SampleSimple
アプリの起動は「ロボホン、サンプルアプリを起動して」と話しかけるところから始まります。聞き取れれば「オッケー、サンプルアプリを起動するね」と返事が帰ってきて、アプリが起動します。
SampleSimpleでは次の通りです。
[simple_home]-> SampleSimple/app/src/main/assets/home/jp_co_sharp_sample_simple_home.hvml
<?xml version="1.0" ?>
<hvml version="2.0">
<head>
<producer>jp.co.sharp.sample.simple</producer>
<description>サンプルアプリのホーム起動シナリオ</description>
...省略...
<!--(ロボホン名)、App_name(を)起動(して)-->
<situation priority="78" topic_id="t1" trigger="user-word"> eq さんぷるあぷり</situation>
<situation priority="78" topic_id="t1" trigger="user-word"> eq さんぷるあぷりしよう</situation>
</head>
<body>
<topic id="t1" listen="false">
<action index="1">
<speech>${resolver:speech_ok()}</speech>
<behavior id="${resolver:motion_ok()}" type="normal"/>
</action>
<next href="#t2" type="default"/>
</topic>
<topic id="t2" listen="false">
<action index="1">
<speech>サンプルアプリを起動するね</speech>
<behavior id="assign" type="normal"/>
<control function="start_activity" target="home">
...省略...
</control>
</action>
</topic>
</body>
</hvml>
HVML内のhead要素で音声認識内容を定義して、body要素で応答メッセージを定義しています。サンプルコードでは「さんぷるあぷり」となっている認識候補は、アプリごと自由に書き換えられます。また、候補はひとつでなくてもよく、複数を指定できます。実際の認識では「アプリ名を起動して」という起動用のセリフも多少ゆらいでも問題なく起動します。音声認識ではロボホンが自分で考えて、ゆらぎを解釈してくれるので細かい部分はロボホン自身に任せることができます。
サンプルコードのbody要素をよく見るとtopic要素にはt1とt2の2つのIDがあり、next要素のhref属性を通じて、t1のあとにt2を発話するようになっています。この部分が「オッケー、サンプルアプリを起動するね」と繋がって、ロボホンの発話を構成します。
ロボホンのアプリ開発では、このような会話内容を記述したHVMLをシナリオと呼んでいます。サンプルコードのシナリオは直接手でかけるぐらいの規模ですが、複雑なものになると手間もかかります。そのためSDKにはGUIベースのシナリオ生成ツールの手引も含まれています。GUIでの支援を受けながら、色々な会話パターンを作り出すことができます。
-> SDK/0901_SR01MW_HVML_Authoring_Tool_V01_00_00.zipオリジナルのRSSリーダーを作る
HVMLの概要もわかってきたので、少し工夫してRSSで取得した内容を読み上げる機能をアプリとしてつくってみましょう。登録したRSSサイトの最新情報を「今日のニュースは・・・(ニュースタイトル)だよ」とロボホンが読み上げてくれるシナリオを考えてみます。
[simple_memory]-> SampleSimple/app/src/main/assets/home/jp_co_sharp_sample_simple_get_memory_p.hvml
<?xml version="1.0" ?>
<hvml version="2.0">
<head>
<producer>jp.co.sharp.sample.simple</producer>
<description>今日のニュースを伝える</description>
<scene value="jp.co.sharp.sample.simple.scene_common"/>
<version value="1.0"/>
<tool_version>1.00</tool_version>
<accost priority="75" topic_id="t1" word="jp.co.sharp.sample.simple.get_memoryp.t1"/>
</head>
<body>
<topic id="t1" listen="false">
<action index="1">
<speech>今日のニュースは</speech>
<behavior id="assign" type="normal"/>
</action>
<next href="#t2" type="default"/>
</topic>
<topic id="t2" listen="false">
<action index="1">
<speech>${memory_p:jp.co.sharp.sample.simple.body}だよ</speech>
<behavior id="assign" type="normal"/>
</action>
</topic>
</body>
</hvml>
topic要素のt1で「今日のニュースは」と発話したあとt2では「${memory_p:jp.co.sharp.sample.simple.body}だよ」と呪文のような単語が続きます。この${}で囲まれた部分は変数として扱うことができ、ソースコード側から任意の日本語を指定可能です。
発話時には漢字にルビを振ったり、身振りや手振りを指定したりする必要はありません。さすがに人名などは読み間違えることがあるものの、一般的な文章であればかなり正確に発話できています。またセリフにあわせて自動で身振り、手振りをつけてくれます。
実際のソースコードは次のとおりです。VoiceUIManagerというクラスのupdateAppInfoAndSpeechメソッドを使い、ロボホンが話す「${memory_p:jp.co.sharp.sample.simple.body}だよ」の部分を指定します。
発話するRssReaderの実装例
public class RssReader {
RssReader(VoiceUIManager manager){
voiceManager = manager;
handler = new Handler(){
public void handleMessage(Message message) {
String body = (String) message.obj;
// 「今日のニュースは${memory_p:jp.co.sharp.sample.simple.body}だよ」 変数にbodyを格納する
int ret = VoiceUIVariableUtil
.setVariableData(voiceManager,"memory_p:jp.co.sharp.sample.simple.body", body);
if(ret == VoiceUIManager.VOICEUI_ERROR){
Log.w("RSS", "setVariableData:VARIABLE_REGISTER_FAILED");
}
// 会話の準備。jp_co_sharp_sample_simple_get_memory_p.hvmlを設定する
VoiceUIVariableUtil.VoiceUIVariableListHelper helper =
new VoiceUIVariableUtil.VoiceUIVariableListHelper().
addAccost(ScenarioDefinitions.ACC_GET_MEMORYP);
// ロボホンが発話する
VoiceUIManagerUtil.updateAppInfo(voiceManager, helper.getVariableList(), true);
};
};
}
...省略(XMLネットワーク通信部分)...
}
ロボホンのサンプルコードはいずれも、VoiceUIManagerを使うためのUtilクラスとしてVoiceUIVariableUtilが用意されています。たとえばサンプルコード中の ${memory_p:jp.co.sharp.sample.simple.body} 変数の指定では、VoiceUIVariableUtilクラスのsetVariableDataメソッドを利用して文字列bodyを指定します。
発話の準備には、VoiceUIVariableListHelperクラスを使い、VoiceUIManagerUtilのupdateAppInfoメソッドを経由して会話をはじめています。実際、VoiceUIManagerをきちんと使いこなすにはそれなりに手間がかかるのですがUtilクラスのおかげで細かい実装を気にしなくても開発が可能です。Utilという名前はいかにも、という気がしますが開発時の苦労が忍ばれます。※
※発話という非同期処理をUIに使う関係で発声終了待ちなどの非同期ウェイトや対話によるコールバック、Activityとの連携のためのフックポイントの整備など既存の仕組みに+αを行った結果、自然と複雑になっています。Utilクラスを使い、開発の負担を軽減する試みが行われています。
さて肝心のRSSの読込処理はロボホンに特化した話ではありませんね。このあたりはさすがAndroid、お試しぐらいならサクッと作れてしまいます。次のコードはAscii.jpのRSSを購読する場合のサンプルです。先程登場したRssReaderクラスを継承してAsciiReaderを作成します。
Ascii.jpのニュースをロボホンが届ける
public class AsciiReader extends RssReader {
AsciiReader(VoiceUIManager manager){
super(manager);
}
@Override
public Article getArticle() {
List<Article> articles = new ArrayList<Article>();
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
URL url = new URL("http://rss.rssad.jp/rss/ascii/rss.xml");
URLConnection connection = url.openConnection();
xmlPullParser.setInput(connection.getInputStream(), "UTF-8");
int eventType;
while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && "title".equals(xmlPullParser.getName())) {
Article article = new Article();
article.body = xmlPullParser.nextText();
Log.d("XmlPullParserSampleUrl", article.body);
articles.add(article);
}
}
} catch (Exception e) {
Log.e("RssReader", "Parse Error");
}
int target = new Random().nextInt(articles.size());
return articles.get(target);
}
}
getArticleメソッドではRSSの解析がメインです。ロボホンが同じセリフを言いにくくするため、取得したニュースのなかからランダムで1つ選ぶようにしています。今回のサンプルでは単純なシナリオでRSSを連携先に選びましたが、IFFFTやSlack Bot、CIの監視など色々置き換えられそうだ、ということは伝わったのではないでしょうか。
紹介していないサンプルのなかにはユーザーが話した内容を聞き取るもの、プロジェクターを利用するものなどがあります。これまでの記事の内容に興味をもった人なら十分楽しめると思います。
音声インターフェイスの今後
ロボホンのアプリ開発の特徴は音声インターフェイスをフル機能で試せることです。最近ではSiriやGoogle Assistant、Amazon Echoなど多くの音声インターフェイスが登場していますが、これらのSDKでできることは限定的でした。また実際に使うときも、無機物に話しかけるのは気恥ずかしくて結局フリック入力してしまう、なんて経験はよくあるのではないでしょうか。ロボホンに限った話ではないのかもしれませんが、音声インターフェイスという新しい世界の中でロボットらしいインタラクションが今後どのように活きてくるのか楽しみです。
快適IoT2016
家を楽しく、便利にするアプリとハードウェアのコンテストⅡ
開催概要
■ テーマ
QOL(quality of life)=快適で楽しい暮らしの実現
■応募締め切り
2016年10月31日(月)23時59分
■各賞
グランプリ 賞金30万円 + 副賞(モバイル型ロボット電話"RoBoHoN")
準グランプリ 賞金20万円
HEMSアライアンス特別賞(4作品) 賞金 各10万円
・生活を豊かにするデータ活用賞
・スマートメータ活用賞
・ロボホン賞
・スマホ活用賞
自作ホームオートメーション賞 賞金10万円
主婦のお悩み解決賞 賞金10万円
■最終審査会/受賞発表と表彰式
2016年12月13日予定
※予備審査を通過した作品制作者は最終審査会に出席し、制作者自ら作品のプレゼンテーションを行うこと。
■公式サイトはこちらから
この連載の記事
-
第55回
プログラミング+
AIに味はわかるか? AIが考えた斬新すぎる豆腐バーガーを作ってみた -
第54回
プログラミング+
GPAIシンポジウム「AI原則と実践の橋渡しに関する国際的な最前線」開催 -
第52回
プログラミング+
濃厚接触者を追跡するコンタクト・トレーサーなど、事業家は準備を始めている -
第51回
プログラミング+
なぜテスラはトヨタの時価総額を超えたのか。日本の歩むべき道を考察する -
第50回
プログラミング+
天才ジム・ケラーに学ぶ。テクノロジー業界では倒産寸前でも逆転できる -
第49回
プログラミング+
ジェットエンジンの原理は?冤罪で捕まったら?専門的な議論や体験談を役立てよう -
第48回
プログラミング+
大混乱なのに株価はほぼ最高額のアメリカ。混迷の時代を書き残そう。 -
第47回
プログラミング+
DXとAI、withコロナ時代のディープラーニングを語る! -
第46回
プログラミング+
価値のある仕事を価値があると伝えるスキルをQuoraで磨く -
第45回
プログラミング+
「Quora」のキレッキレなQ&Aがつまった公式本刊行!! -
第44回
プログラミング+
ITの新常識、日本ディープラーニング協会の「G検定」が受験料半額で実施! - この連載の一覧へ