「Sphero Robotic Ball」 というラジコンのようなボールをご存知でしょうか?iPhoneやアンドロイドをコントローラとして、前後左右に自由に動かせて、更に様々な色に点滅させてと中々面白いおもちゃです。小さな子どもや犬猫と遊ぶとかなり楽しい代物です。完全に球形なので、よほど大きな衝撃を与えない限り壊れないという優れ物です。遊んでいる風景は公式ページにアップ されているので、是非御覧ください。
そんなSpheroですが、更にエンジニア心をくすぐる仕掛けがあります。Spheroとコントローラ(iPhone,Android)との通信はBluetoothで行なっているのですが、何とAPIは全て公開されています。そしてiPhone/AndroidのSDKも提供されているので、独自のプログラムで自由に動かすことが出来るのです。楽しいでしょう。
ということで、私も作ってみました。Siriを使って、音声で操作するアプリです。作っているっている時の脳内イメージは、鉄人28号みたいに「進め」や「止まれ」で命令して操作するロボットです。しかし、よく考えたら鉄人28号はリモコンで動いていましたね。
Sphero音声コントローラーの作り方
実装の殆どは、
kishikawakatsumi/VoiceNavigation に少し手を加えただけです。Siriの音声認識を使う為にUITextInputのstartDictation, stopDictation, cancelDictation メソッドを利用しています。ちなみに非公開のAPIを使っているので、アプリを作ってもAppleに申請することは出来ません。
viewDidLoadでNSNotificationCenterをひたすら行なっています。iOS6では、keyboardWillShowの挙動が変わっているっぽいので下の方で無理やり呼び出しています。(iOS5だと、ここで呼び出す必要はありません。)
- (void )viewDidLoad {
NSLog(@"viewDidLoad" );
[super viewDidLoad];
dictationView.layer.cornerRadius = 8.0f ;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dictationRecordingDidEnd:)
name:VNDictationRecordingDidEndNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dictationRecognitionSucceeded:)
name:VNDictationRecognitionSucceededNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dictationRecognitionFailed:)
name:VNDictationRecognitionFailedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[self keyboardWillShow:nil];
robotOnline = NO;
calibrateHandler = [[RUICalibrateGestureHandler alloc] initWithView:self.view];
}
音声認識の繰り返し。ループで一定期間間隔でスタートとストップで繰り返すようにしてあります。
- (void )startDictation {
NSLog(@"startDictation" );
[dictationController performSelector:@selector(startDictation)];
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onTimer:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self resetProgress];
micImageView.hidden = NO;
resultLabel.text = nil;
}
- (void )stopDictation {
NSLog(@"stopDictation" );
[dictationController performSelector:@selector(stopDictation)];
[displayLink invalidate];
displayLink = nil;
[self showWaitingServerProcessIndicator];
micImageView.hidden = YES;
}
- (void )cancelDictation {
NSLog(@"cancelDictation" );
[dictationController performSelector:@selector(cancelDictation)];
[displayLink invalidate];
displayLink = nil;
[self resetProgress];
micImageView.hidden = NO;
resultLabel.text = nil;
}
音声検出した後です。dictationRecognitionSucceededで解析の実体であるprocessDictationTextを呼び出しています。
- (void )dictationRecognitionSucceeded:(NSNotification *)notification {
NSLog(@"dictationRecognitionSucceeded" );
NSDictionary *userInfo = notification.userInfo;
NSArray *dictationResult = [userInfo objectForKey:VNDictationResultKey];
NSString *text = [self wholeTestWithDictationResult:dictationResult];
[self processDictationText:text];
[self hideWaitingServerProcessIndicator];
[self performSelector:@selector(startDictation) withObject:nil afterDelay:VNDictationRepeatInterval];
}
- (void )dictationRecognitionFailed:(NSNotification *)notification {
NSLog(@"dictationRecognitionFailed" );
resultLabel.text = @"-" ;
[self hideWaitingServerProcessIndicator];
[self performSelector:@selector(startDictation) withObject:nil afterDelay:VNDictationRepeatInterval];
}
音声認識はif文での分岐処理で行なっています。ここはゴリゴリ書いているだけなので、もう少しスマートに書くのが良いと思います。
- (void )processDictationText:(NSString *)text {
NSLog(@"processDictationText" );
resultLabel.text = text;
NSLog(@"text" );
if ([self hasString:text Search:@"とまれ" ] || [self hasString:text Search:@"止" ]) {
NSLog(@"止まれ" );
[RKRollCommand sendStop];
} else if ([self hasString:text Search:@"戻れ" ]) {
NSLog(@"戻る" );
[RKRollCommand sendCommandWithHeading:180.0 velocity:0.5 ];
} else if ([self hasString:text Search:@"進め" ]) {
NSLog(@"進む" );
[RKRollCommand sendCommandWithHeading:0.0 velocity:0.5 ];
} else if ([self hasString:text Search:@"お前の血は何色だ" ]) {
NSLog(@"お前の血は何色だ" );
[self changeColorRed:NULL ];
} else if ([self hasString:text Search:@"青" ]) {
NSLog(@"青" );
[self changeColorBlue:NULL ];
} else if ([self hasString:text Search:@"赤" ] || [self hasString:text Search:@"あか" ]) {
NSLog(@"赤" );
[self changeColorRed:NULL ];
} else if ([self hasString:text Search:@"緑" ]) {
NSLog(@"緑" );
[self changeColorGreen:NULL ];
}
}
なお、このアプリはSiriが使える機種限定です。2013年3月現在だと、iPhone 4S以降、新しいiPad以降、iPad miniとなっています。ソースは、GitHubで公開して あります。
デモ
動きが解るように、ボタンも用意してあります。そして、肝心の音声認識ですが、赤をどうしても馬鹿と認識してしまいます。これは私の滑舌の問題かと思いますw何回も赤といっても、
Siriに"ばか" と言い返されますw
傾向としては、Siriは単語系の認識率は悪いですね。会話等の文脈判断がある程度あるのだと思います。
VIDEO