Kinecには、上下方法の首振りはありますが、横方向の首振りはありません。ユーザを認識後にユーザが左右の画面外に出ていったら認識が外れてしまいます。




F5キーを押すことでCOMポートをOpenします。
コマンド終端には、終端記号である「z」を負荷しました。(zに深い意味はありません。なんでもOKです。負荷しなくても\0で通常は認識されるのですが、昔、試したときにうまく動作しなかったため、いまだに終端記号を付けています。)
もしKinectが「ユーザを追尾して横方向の回転移動もできたならば・・・」、「ユーザが遠ざかっていくと追いかけてきたなら・・・」 ユーザを常に自動追尾してくるかわいいKinect。素敵ではないですか?
そこで登場するのが、タミヤの「楽しい工作シリーズ」です。
以前、「Wiiでラジコン」「Wiiラジコン その2」で紹介した、アレです。
これを用いれば、Kinectの横方向の回転も簡単にできますね。テレビの回転台をモータで回そうと思ったのですが、とりあえず第一弾としての「KinectTANK(キネクたんの紹介です。
●必要なもの
以下は、最低限必要なものです。- Arduinoをはじめようキット ×1個
- 楽しい工作シリーズ No.100 トラック&ホイールセット (70100) X 1個
- 楽しい工作シリーズ No.157 ユニバーサルプレート 2枚セット (70157) ×2個
- 楽しい工作シリーズ No.168 ダブルギヤボックス 左右独立4速タイプ (70168) ×1個
- モータドライバー TA7291P X2個
Amazonアソシエイトで小銭を稼ごうとは思っていませんが、Amazonさんで大抵のものは揃います。
これらのほか、USBケーブルや、ネジやネット、スペーサーや、電池ボックス(単三X4個)、工具が別途必要です。動きまわるのでUSBケーブルはKinectのケーブルと同じく長めのほうが良いでしょう。もちろんKinect本体も必要です。
●組立
今回は、電池ボックスを中段の
中段のユニバーサルプレートのとりつけ
横からみた図(古い写真なので、乾電池が2つタイプ)
Arduinoとブレットボードの取り付け
配線
ひたすら配線
上段のユニバーサルプレートの取り付けました。
Kinectの搭載 重心位置に注意しないと、少し動かしただけで倒れます。結束バンドで取り付けました。
●回路図
●ソースコード(Arduino)
void setup(){
//L Moter PinSET
pinMode(6,OUTPUT); //Pin6 OUTPUT
pinMode(7,OUTPUT); //Pin6 OUTPUT
//R Moter PinSET
pinMode(8,OUTPUT); //Pin6 OUTPUT
pinMode(9,OUTPUT); //Pin6 OUTPUT
Serial.begin(9600);
}
void loop(){
SerialRead();
}
void SerialRead() {
int i=0;
char read_c; //1文字読み込み
char read_datas[15]; //受信文字列
//データ受信
if (Serial.available()) { //受信データがある時
while (1) {
if (Serial.available()) {
read_c = Serial.read(); //1文字読み込み
read_datas[i] = read_c;
// 文字列の終わりは'z'で判断
if (read_c == 'z'){ break; }
i++;
}
}
read_datas[i] = '\0'; //受信データの最後にEOFを付加
///////////////////////////////////////////////////////
//データ解析
//////////////////////////////////////////////////////
//FF
if(read_datas[0]=='F' && read_datas[1]==',' && read_datas[5]=='\0'){
int val = RetunValue(read_datas); //文字を数値に変換
L_Moter_F(val);
R_Moter_F(val);
}
//Back
if(read_datas[0]=='B' && read_datas[1]==',' && read_datas[5]=='\0'){
int val = RetunValue(read_datas); //文字を数値に変換
L_Moter_B(val);
R_Moter_B(val);
}
//L
if(read_datas[0]=='L' && read_datas[1]==',' && read_datas[5]=='\0'){
int val = RetunValue(read_datas); //文字を数値に変換
L_Moter_B(val);
R_Moter_F(val);
}
//R
if(read_datas[0]=='R' && read_datas[1]==',' && read_datas[5]=='\0'){
int val = RetunValue(read_datas); //文字を数値に変換
L_Moter_F(val);
R_Moter_B(val);
}
//STOP
if(read_datas[0]=='S' && read_datas[1]=='T' &&
read_datas[2]=='O' && read_datas[3]=='P' && read_datas[4]=='\0'){
Moter_STOP();
}
}
}
//*************************************
// RetunValue
//*************************************
int RetunValue(char *data){
char tmp[5];
tmp[0] = data[2];
tmp[1] = data[3];
tmp[2] = data[4];
tmp[3] = '\0';
return atoi(tmp);
}
void Moter_STOP(){
digitalWrite(6,LOW);
digitalWrite(7,LOW);
digitalWrite(8,LOW);
digitalWrite(9,LOW);
}
void L_Moter_F(int Speed){
analogWrite(3,Speed); //SpeedSET
digitalWrite(6,HIGH);
digitalWrite(7,LOW);
}
void L_Moter_B(int Speed){
analogWrite(3,Speed); //SpeedSET
digitalWrite(6,LOW);
digitalWrite(7,HIGH);
}
void R_Moter_F(int Speed){
analogWrite(5,Speed); //SpeedSET
digitalWrite(8,LOW);
digitalWrite(9,HIGH);
}
void R_Moter_B(int Speed){
analogWrite(5,Speed); //SpeedSET
digitalWrite(8,HIGH);
digitalWrite(9,LOW);
}
●ソースコード(XNA)
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Research.Kinect.Nui; //Kinect Uniの読み込み using System.IO.Ports; //シリアル namespace KinectTANK { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Runtime nui; //Kinectのセンサクラス Texture2D texture_image = null; //実画像テクスチャ Color[] imageColor; //色情報の格納 SpriteFont font = null; //フォント System.IO.Ports.SerialPort Port_Arduino; //Arduinoデバイス String send_MSG=""; String send_MSG_B=""; #region コンストラクタ public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; //画面サイズを640 X 480 this.graphics.PreferredBackBufferWidth = 640; this.graphics.PreferredBackBufferHeight= 480; } #endregion #region Initialize protected override void Initialize() { #region Kinect初期化 nui = new Runtime(); //Kinectセンサクラスの初期化 try{ //奥行の取得、トラッキング、実画像 nui.Initialize( RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor); }catch (InvalidOperationException){ Console.WriteLine("Runtime initialization failed."); return; } try{ //ビデオストリームを開く nui.VideoStream.Open( ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color); //デプスストリームを開く nui.DepthStream.Open( ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex); }catch (InvalidOperationException){ Console.WriteLine("Failed to open stream. "); return; } //フレーム更新毎にnui_ColorFrameReadyを呼び出す nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_ColorFrameReady); #endregion #region Arduino初期化 this.Port_Arduino = new SerialPort(); this.Port_Arduino.BaudRate =9600; this.Port_Arduino.PortName ="COM70"; #endregion base.Initialize(); } #endregion #region LoadContent protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); //フォントの読み込み this.font = this.Content.Load<SpriteFont>("SpriteFont1"); this.imageColor = new Color[640 * 480]; } #endregion #region UnloadContent protected override void UnloadContent() { } #endregion #region Update protected override void Update(GameTime gameTime) { #region キーボード処理 KeyboardState keyState = Keyboard.GetState(); keyState = Keyboard.GetState(); //ESCキーが押されたら終了 if(keyState.IsKeyDown(Keys.Escape)){ this.Exit(); } //F5キーが押されたら if(keyState.IsKeyDown(Keys.F5)){ if(this.Port_Arduino.IsOpen!=true){ Console.Write("Open\n"); this.Port_Arduino.Open(); } } //F6キーが押されたら if(keyState.IsKeyDown(Keys.F6)){ if(this.Port_Arduino.IsOpen==true){ this.Port_Arduino.WriteLine("STOPz"); Console.Write("Close\n"); this.Port_Arduino.Close(); } } if(this.Port_Arduino.IsOpen==true){ //上キーが押されたら if(keyState.IsKeyDown(Keys.Up)){ this.send_MSG ="F,255z"; }else //下キーが押されたら if(keyState.IsKeyDown(Keys.Down)){ this.send_MSG ="B,255z"; }else //左キーが押されたら if(keyState.IsKeyDown(Keys.Left)){ this.send_MSG ="L,255z"; }else //右キーが押されたら if(keyState.IsKeyDown(Keys.Right)){ this.send_MSG ="R,255z"; }else{ //何も押されなかたら停止 this.send_MSG ="STOPz"; } #region シリアル送信処理 //同じ信号をなんども送るのは無意味なので //以前送った信号と異なる時のみ送信 if(this.send_MSG_B != this.send_MSG){ this.Port_Arduino.Write(this.send_MSG); Console.WriteLine(send_MSG); } #endregion this.send_MSG_B = this.send_MSG; } #endregion base.Update(gameTime); } #endregion #region Draw protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); #region spriteBatch this.spriteBatch.Begin(); //実画像Textureの描写 if(this.texture_image !=null){ this.spriteBatch.Draw(this.texture_image,new Vector2(0,0), null, Color.White,0.0f,Vector2.Zero,1.00f,SpriteEffects.None,0.0f); } #region ディスプレイ表示 this.spriteBatch.DrawString(this.font, "ESC :EXIT" , new Vector2(0,0),Color.White); this.spriteBatch.DrawString(this.font, "Arduino:"+this.Port_Arduino.PortName ,new Vector2(0,20),Color.White); this.spriteBatch.DrawString(this.font, " Open:"+this.Port_Arduino.IsOpen ,new Vector2(0,40),Color.White); #endregion this.spriteBatch.End(); #endregion base.Draw(gameTime); } #endregion #region 実画像処理 void nui_ColorFrameReady(object sender, ImageFrameReadyEventArgs e) { // 32-bit per pixel, RGBA image lock(this){ PlanarImage Image = e.ImageFrame.Image; this.texture_image = new Texture2D(graphics.GraphicsDevice,640,480);//テクスチャの作成 int no=0; //画像取得 for (int y = 0; y < Image.Height; ++y){ //y軸 for (int x = 0; x < Image.Width; ++x, no += 4){ //x軸 this.imageColor[ y* Image.Width +x ] = new Color( Image.Bits[no+2],Image.Bits[no+1],Image.Bits[no+0] ); } } this.texture_image.SetData(this.imageColor); //texture_imageにデータを書き込む } } #endregion } }
●実行結果
F6キーを押すころでCOMポートをCloseします。
COMポートとの接続している状態で、キーボードの矢印キーを操作することでKinectTANKが動きます。左右のキーを押すことで、戦車特有の超信地旋回も可能です。なにもキーが押されなければ止まります。
デジカメの電池が切れたので、携帯カメラでアップしました。後日差し替えます。
●解説
今回は、Kinectらしいことは一切行っていません、画面に画像を出して、キーボードで操作するラジコンを制作しただけです。
XNAからArduinoに対してシリアル通信を行っています。この通信方法を覚えておけば非常に汎用性が高いです。
今回は、文字情報で通信を行っています。XNAからArduinoに対して5つのコマンドを制作しました。
| コマンド | 動作 | 使用例 | 意味 |
| F | 前進 | F,255z | 0~255段階中、255のスピードで前進 |
| B | 後進 | F,255z | 0~255段階中、255のスピードで後進 |
| L | 左ターン | L,255z | 0~255段階中、255のスピードで左ターン前進 |
| R | 右ターン | R,255z | 0~255段階中、255のスピードで右ターン前進 |
| STOP | 停止 | STOP | 停止する |
次回は、検出した人物を追跡するTipsを行います。

