2014年12月13日土曜日

【Processing】平面二軸アームを動かす

こんにちは。前回の記事では、平面二軸アームの設計ということで、フリーソフトである3DCADのDesignSpark Mechanicalを使ってサーボモータ同士を繋ぐアーム部分の設計をしました。今回の記事では、その3DCADで作製した設計の通りにアクリル板を切り出して、アクリル曲げヒータで曲げ加工をし、サーボと合わせてロボットアームを作成し、ProcessingとArduinoを使って動かします。

 今回購入したのは厚さ3 mmの透明アクリル板910 mm × 600 mmです。近所のホームセンターで4800円でした。この板から今回使用するアームで使用する分(250 mm × 50 mm)を切り出します。切り出すためには、プラスチックカッターを使用します。プラスチックカッターによる加工は、安価であるというメリットがあります。しかし、切る部分に溝を彫り、ある程度のところまで彫れたら力を加えて割る必要があるため細かい加工が難しく、時間がかかるという欠点があります。

 
▲アクリルカッター

初めての加工ということもあり、細かい部分が割れてしまうというアクシデントがありましたが、組み立てには支障なかったのでこのまま使用します。(卓上糸鋸盤がほしい…。)
▲赤丸部分が欠けてしまっている

 切り出すことができたら、アクリル曲げヒータを使って設計どおりに曲げます。アクリル曲げヒータはアクリルに170℃付近まで熱を加える事で熱可塑性により加工を可能としています。アクリル曲げヒータは市販のものを購入すると結構なお値段(\7500~)になりますが、自分で材料を集めれば安く作ることもできます。僕はファブクラウドさんが販売しているアクリルヒータキットを購入しました。(送料込み3000円程度)

 曲げ加工まで済んだらサーボモータをくっつけて(横着して両面テープでとめた)ロボットアームの形にします。
▲横着ぼろっと

最後に、Processingで逆運動学により算出された各関節の角度をArduinoにシリアル通信で送信するプログラムを書いて動かします。

_kiitaniさん(@monitorgazer)が投稿した動画 -

 加工が適当すぎて、ボールペンの接触が不安定ですね。高専時代にもっと機械加工を実践しておくべきだったな〜と思います。


 うーん。アーティスティック…。

以下プログラムです。
Processing(データ送信側)
import controlP5.*;
import processing.serial.*;

ControlP5 cp5;
PGraphics pg;
Slider2D ui;
Serial myPort;

int[] cmd = {255,0,3,0,90,90};
boolean init = false;

float x1,y1,x2,y2,xe,ye;
float oldX2,oldY2,oldXe,oldYe;
float angle1;
float angle2;
float angle1_;
float angle2_;
float the1;
float the2;
final float l1 = 100;
final float l2 = 100;
final int r = 20;
int dir = 1;

void setup(){
  size(640,640);
  stroke(255,255,255);
  fill(255,255,255);
  //set to origin.
  x1 = 300;
  y1 = 350;
  pg = createGraphics(width,height,JAVA2D);
  cp5 = new ControlP5(this);
  
  ui = cp5.addSlider2D("Position")
    .setSize(200,200)
    .setArrayValue(new float[] {79, 1})
    .setMaxX(150)
    .setMaxY(132)
    .setMinX(-90)
    .setMinY(-50)
    .setPosition(30,30)
    ;
  String portName = Serial.list()[5];
  println(Serial.list());
  myPort = new Serial(this,portName,9600);
  init = true;
}

void draw()
{ 
  //myPort.write("123,456\0");
  background(0);
  angle1_ = acos((pow(l1,2)-pow(l2,2)+(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2)))/((2*l1)*sqrt(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2))));
  angle1 = atan2(ui.arrayValue()[1],ui.arrayValue()[0])-angle1_;
  angle2_ = acos((pow(l2,2)-pow(l1,2)+(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2)))/((2*l2)*sqrt(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2))));
  angle2 = angle1_ + angle2_;
  //print(angle1);print(":");println(angle2);
  float degangle1 = angle1 * 180/PI;
  float degangle2 = angle2 * 180/PI;
  angle1 = degangle1 * PI/180;
  angle2 = degangle2 * PI/180;
  x2 = x1+l1*cos(angle1);
  y2 = y1+l1*sin(angle1);
  xe = x2+l2*cos(angle1+angle2);
  ye = y2+l2*sin(angle1+angle2);
  if(degangle1 <= -90){
    xe = oldXe;
    ye = oldYe;
    x2 = oldX2;
    y2 = oldY2;
    degangle1 = -90;
  }
  if(degangle1 >= 90){
    xe = oldXe;
    ye = oldYe;
    x2 = oldX2;
    y2 = oldY2;
    degangle1 = 90;
  }
  if(angle2<=0){
    xe = oldXe;
    ye = oldYe;
    x2 = oldX2;
    y2 = oldY2;
    degangle2 = 0;
  }
  if(angle2>=180){
    xe = oldXe;
    ye = oldYe;
    x2 = oldX2;
    y2 = oldY2;
    degangle2 = 180;
  }
  ellipse(x1,y1,r,r);
  ellipse(x2,y2,r,r);
  fill(255,0,0);
  ellipse(xe,ye,r,r);
  fill(255);
  line(x1,y1,x2,y2);
  line(x2,y2,xe,ye);
  pg.beginDraw();
    pg.noStroke();
    pg.fill(0,100,100);
    //pg.ellipse(xe,ye,r,r);
  pg.endDraw();
  image(pg.get(0,0,width,height),0,0);
  
  //print(degangle1);print(":");println(degangle2);
  cmd[4] = (int)(90-degangle1);
  cmd[5] = (int)(180-degangle2);
  oldXe = xe;
  oldYe = ye;
  oldX2 = x2;
  oldY2 = y2;
  servoCmd();
}

void servoCmd() {
  if(!init) return;
  for(int i = 0; i < 6; i++) {
    myPort.write((byte)cmd[i]);
  }
}

void mouseDragged(){
  int x = (int)(mouseX-x1);
  int y = (int)(mouseY-y1);
  
  if( x <= -90) x = -90;
  if( x >= 150) x = 150;
  if( y <= -50) y = -50;
  if( y >= 132) y = 130;
  ui.arrayValue()[0] = x;
  ui.arrayValue()[1] = y; 
  print(ui.arrayValue()[0]);print(":");println(ui.arrayValue()[1]);
}

Arduino(データ受信側)
 #include <Servo.h> 
#include <string.h>
#include <stdlib.h>

Servo myservo1,myservo2,myservo3;
float deg1,deg2;
unsigned char incomingByte = 0;
unsigned char data[20];
int axis1 = 9;
int axis2 = 10;
int axis3 = 11;

int count = 0;
int paramLength = 0;
int paramCount = 0;
 
int pos = 0;    // variable to store the servo position 
 
void setup() 
{
  Serial.begin(9600); 
  myservo1.attach(axis1,800,2200);
  myservo2.attach(axis2,800,2200);
} 
 
 
void loop() 
{ 
  if(Serial.available() > 0){
      incomingByte = Serial.read();
      switch(count){
          case 0:
              paramLength = 0;
              paramCount = 0;
              if(incomingByte == 0xff){ //Header
                  data[count++] = incomingByte;
              }
              break;
          case 1:
              if(incomingByte == 0){ //ID
                  data[count++] = incomingByte;
              }
              break;
          case 2:    //LENGTH
              data[count++] = incomingByte;
              paramLength = incomingByte;
              break;
          default:
              if(paramLength > 0){
                      data[count++] = incomingByte;
                      paramCount++;
                      if(paramCount >= paramLength){
                          ServoCmdExec();
                          count = 0;
                      }
              }else{
                  count = 0;
              }
              break;
      }
  } 
}

void ServoCmdExec(){
    if((data[4] >= 0) && (data[4] <= 180) ) myservo1.write(data[4]);
    if((data[5] >= 0) && (data[5] <= 180) ) myservo2.write(data[5]);
}

複数のサーボをProcessingとArduinoのシリアル通信で制御するためには少し工夫が必要になります。

参考にしたサイト様

2014年12月6日土曜日

【DesignSpark Mechanical】平面二軸ロボットアームの設計

こんにちは。
 前回の記事では次はロボットを組み立てる。と書いていましたが、今日アクリル板を買ったら予想以上に高価で失敗したくないなと思い、3DCADソフトで設計してから実機を作成することにしました。。。

 使用したソフトはDesignSpark Mechanicalというフリーソフトです。Windowsでのみリリースされています。Mac版が欲しい…。
 
 高専に居た頃、SolidWorksを少しかじったことがあるので、わりとすんなり使うことができました。しかし細かい機能は全然わからない。。。

とりあえず、平面二軸アームということで、構成要素はサーボ二台と、アーム二本です。

サーボのモデルはこんな感じでつくりました。



これをアームと組み合わせて



こんな感じになります。

手先先端の穴にはペン等を保持できるように開けています。

次回こそは実機の制作の記事を書きたいと思います。

【Processing】平面二軸ロボットの逆運動学

こんにちは。
 前回の記事”【Processing】平面二軸ロボットの順運動学”では各関節の角度を指定して手先の位置を決める順運動学をProcessingを使って実装しました。今回の記事では手先位置を指定して各関節の角度を決定する逆運動学をProcessingを使って実装します。

 逆運動学を解くと以下のようになり、手先位置XE,YE及びリンク長L1,L2より、各関節角度θ1、θ2を決定することができます。実際には同じ手先位置を実現するロボットアームの姿勢が2つ以上存在することがあります。その場合は静力学、動力学を考慮して拘束条件を設定し、各関節の負担を小さくしたりする必要があります。


この式にもとづいて、Processingのプログラムを作成すると以下のようになります。

import controlP5.*;

ControlP5 cp5;
PGraphics pg;
Slider2D ui;

float x1,y1,x2,y2,xe,ye;
float angle1;
float angle2;
float angle1_;
float angle2_;
float the1;
float the2;
final float l1 = 100;
final float l2 = 80;
final int r = 20;
int dir = 1;

void setup(){
  size(640,640);
  stroke(255,255,255);
  fill(255,255,255);
  //set to origin.
  x1 = 300;
  y1 = 350;
  pg = createGraphics(width,height,JAVA2D);
  cp5 = new ControlP5(this);
  
  ui = cp5.addSlider2D("Position")
    .setPosition(30,40)
    .setSize(200,200)
    .setArrayValue(new float[] {15, 30})
    ;
}

void draw()
{ 
  background(0);
  angle1_ = acos((pow(l1,2)-pow(l2,2)+(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2)))/((2*l1)*sqrt(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2))));
  angle1 = atan2(ui.arrayValue()[1],ui.arrayValue()[0])-angle1_;
  angle2_ = acos((pow(l2,2)-pow(l1,2)+(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2)))/((2*l2)*sqrt(pow(ui.arrayValue()[0],2)+pow(ui.arrayValue()[1],2))));
  angle2 = angle1_ + angle2_;
  the1 = angle1*PI/180;
  the2 = angle2*PI/180;
  print(angle1);print(":");println(angle2);
  x2 = x1+l1*cos(angle1);
  y2 = y1+l1*sin(angle1);
  xe = x2+l2*cos(angle1+angle2);
  ye = y2+l2*sin(angle1+angle2);
  ellipse(x1,y1,r,r);
  ellipse(x2,y2,r,r);
  fill(255,0,0);
  ellipse(xe,ye,r,r);
  fill(255);
  line(x1,y1,x2,y2);
  line(x2,y2,xe,ye);
  pg.beginDraw();
    pg.noStroke();
    pg.fill(0,100,100);
    pg.ellipse(xe,ye,r,r);
  pg.endDraw();
  image(pg.get(0,0,width,height),0,0);
}

逆運動学を使用すると、手先のX、Y座標を指定してアーム姿勢を決定するため、以下のように手先をX方向のみに移動させることやY方向のみに移動することができます。
_kiitaniさん(@monitorgazer)が投稿した動画 -
また、マウスパッドのようにXY座標を同時に入力するインターフェースを用いると、手先位置をXY軸で自由に動かすことができるので、絵を描いたりすることができます。

_kiitaniさん(@monitorgazer)が投稿した動画 -
次回はこのプログラムを基に、Arduinoとサーボモータを使用して、実際に平面二軸ロボットを作ります。

2014年12月4日木曜日

【Processing】平面二軸ロボットの順運動学

こんにちは。
 最近秋葉原でサーボモータを買いました。サーボで簡単にアームロボットを作りながら、ロボット制御工学の基礎を復習してゆきます。まずは、サーボを2つ使って平面二軸アームを作成し、ロボットアームを作ったお絵かきをしたいと思います。

 まずは基礎の基礎の順運動学を解いていきます。順運動学では各軸の角度を決めた時、エンドエフェクタがどの位置に来るかを計算するものです。平面二軸アームを用いると以下に示すようにXY平面上で(可動域の制限内で)自由に位置決めすることができます。このようにアームの各関節の角度が決まった時に、手先の位置を計算する方法を順運動学といいます。
※図の変更(2014/12/04 19:25)

 では、順運動学の計算を用いて平面二軸アームの可動域を可視化するプログラムを書いてみましょう。開発環境にはビジュアルプログラミングが簡単に行えるProcessingを用います。(今後Arduinoでアームを制御する際にも便利)以下にソースコードを示します。

import controlP5.*;
import java.util.ListIterator;

ControlP5 ui;
PGraphics pg;

float x1,y1,x2,y2,xe,ye;
float angle1 = 90;
float angle2;
float the1;
float the2;
final float l1 = 150;
final float l2 = 100;
final int r = 20;
int dir = 1;

void setup(){
  size(640,640);
  stroke(255,255,255);
  fill(255,255,255);
  ui = new ControlP5(this);
  
  ui.addSlider("angle1")
     .setPosition(10,10)
     .setSize(200,20)
     .setRange(-90,90)
     .setValue(-90);
  ui.addSlider("angle2")
     .setPosition(10,50)
     .setSize(200,20)
     .setRange(0,180)
     .setValue(90);   
  //set to origin.
  x1 = 300;
  y1 = 350;
  pg = createGraphics(width,height,JAVA2D);
  
}

void draw()
{
  //angle1++;
  //if(angle1>=90) angle1 = -90;
  //for(int i=0; i<=180; i++){
  // angle2=i; 
  background(0);
  the1 = angle1*PI/180;
  the2 = angle2*PI/180;
  ellipse(x1,y1,r,r);
  x2 = x1+l1*cos(the1);
  y2 = y1+l1*sin(the1);
  xe = x2+l2*cos(the2+the1);
  ye = y2+l2*sin(the2+the1);
  ellipse(x2,y2,r,r);
  ellipse(xe,ye,r,r);
  line(x1,y1,x2,y2);
  line(x2,y2,xe,ye);
  pg.beginDraw();
    pg.noStroke();
    pg.fill(0,100,100);
    pg.ellipse(xe,ye,r,r);
  pg.endDraw();
  //image(pg.get(0,0,width,height),0,0);
  //}
}

 左上に表示されるスライドバーは各関節の角度に対応しています。また、ソースコードのコメントを外すことで自動的に全ての可動域を描画することができます。以下は全ての可動域を可視化した時の動画です。アームの動作をDrawで描画しつつ、可動域を描画するためのTipsはPGraphicsを使用することです。pg.beginDrawからpg.endDraw()までに書かれた描画処理はDraw関数内のリフレッシュによっても消えない独立されたレイヤーに描画されるため、アームの動作とエンドエフェクタの軌跡を同時に示すことができます。


 これで平面アームの順運動学を解き、各関節の角度によってエンドエフェクタがどう動くかをシミュレーションすることが出来ました。順運動学は理解しやすい反面、自由度の増加や三次元空間への拡張を考えると複雑になりすぎて現実的ではありません。そこで用いられるのが逆運動学です。これは、エンドエフェクタの座標を決定したとき、各関節角度はどのようになるかを解くものです。次回は平面二軸アームの逆運動学に取り組みます。

2014年12月2日火曜日

C言語でArduinoとシリアル通信してアナログ電圧値をグラフ表示するWin32なC/C++のサンプルプログラムをつくる(1)

 こんにちは。タイトル長い。所用でとあるアナログデータをAD変換をしてPCに取り込み、グラフ表示するプログラムが必要になったので、”こんな感じでできます”サンプルとして、VisualStudioでWin32のプログラムを書いてゆきます。開発環境はWindows 7 32bit , Visual Studio 2012です。

 AD変換器として部屋に転がっていたArduinoUNOを使用しました。ArduinoUNOは10bitのADCを持っていますので、0-5Vのアナログ入力値を0-1023段階、4.9mV単位で量子化します。本当はもっと深いAD変換が必要だと思いますが、とりあえず簡易なデータロギング・表示のサンプルです。

 これまでC言語でGUIを持つプログラムはFormしか作ってこなかったので、かなり調べながらやりました。要素の配置を頭で想像しながらやるのはかなり大変でした。全ての処理を把握したいという要望があるみたいなので、Cで作れば文句ないと思います。

 簡単に処理の流れを書くと。。。
  1. ウィンドウ作成
  2. シリアルポート確立
  3. グラフグリッド描画
  4. グラフのリアルタイム描画用副スレッドの立ち上げ
  5. 副スレッドにてシリアルデータを読みながらプロットの描画
 と、こんな感じになっています。

プログラムを見ていく前にまず、プロジェクトの作成方法を確認します。

Win32GUIプログラムのプロジェクトの作成方法
 起動したらまず、新しいプロジェクトをクリックします。



 そうすると次のようなウィンドウが表示されますので、Visual C++ -> Win32 -> Win32プロジェクトの順で選択肢、適当な名前を入力してOKをクリックします。


すると、以下のようなウィザードが現れますが、特に変更するべきことはありませんので、完了をクリックしてプロジェクトの作成を完了します。


 プロジェクトの作成が完了しましたら、ビルドして実行をしてみます。ショートカットキーはF5です。できていれば何もない白いウィンドウが表示されるはずです。これが、Win32プログラムのスタートポイントです。


プログラム作成
 ではここからは作成したひな形に自分のプログラムを加えていきます。まずは、ウィンドウの大きさや名前を変更してみます。
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
   DWORD dwID;
   
   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。
   
   //Windowの大きさと名前の変更
   hWnd = CreateWindow(szWindowClass, L"GRAPHS", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, MW_W, MW_H, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
   //Serialポートの確立
   setupSerial();
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   //描画スレッドの作成
   CreateThread(NULL,0,threadfunc,(LPVOID)hWnd,0,&dwID);

   //ボタンの作成
   hButton1 = CreateWindow(L"BUTTON",L"計測開始",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
    600,10,100,30,hWnd,(HMENU)ID_BUTTON1,hInstance,NULL);
   
   return TRUE;
}

 CreateWindow関数の2番目の引数を変更することで、ウィンドウの名前を変更することができます。また、6番、7番目の変数はそれぞれ、ウィンドウの横幅、縦幅となっています。
 その他の部分では、CreateThread関数を使って、描画用の副スレッドを作成しています。また、setupSerial関数はシリアルポートを確立し、Arduinoとシリアル通信を繋ぐ自作関数です。
 
では、setupSerial関数を見てみます。
void setupSerial(){
   //シリアル通信用ポートの確立 ("COM8")
   serialPort = CreateFile(L"COM8",GENERIC_WRITE|GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
   if(serialPort == INVALID_HANDLE_VALUE)
   {
    OutputDebugString(L"PORT COULD NOT OPEN");
   }
   bool Ret = SetupComm(serialPort,1024,1024);
   if(!Ret){
    OutputDebugString(L"SET UP FAILED");
    CloseHandle(serialPort);
   }
   Ret = PurgeComm(serialPort,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
   if(!Ret){
    OutputDebugString(L"CLEAR FAILED");
    CloseHandle(serialPort);
   }
   //基本通信条件の設定
   DCB dcb;
   GetCommState(serialPort,&dcb);
   dcb.DCBlength = sizeof(DCB);
   dcb.BaudRate = 9600;
   dcb.fBinary = FALSE;
   dcb.ByteSize = 8;
   dcb.fParity =NOPARITY;
   dcb.StopBits = ONESTOPBIT;

   Ret = SetCommState(serialPort,&dcb);

   if(!Ret){
    OutputDebugString(L"SetCommState FAILED");
    CloseHandle(serialPort);
   }
}

この関数は以前に書いたC言語でArduinoとシリアル通信してLEDを制御するの部分とほとんど同じです。

では次に、ウィンドウにグラフのグリッドを描画してみます。
//
//  関数: CreateGrids(HDC)
// 目的: グラフのグリッド描画を行います。
//
int CreateGrids(HDC hdc)
{
 HGDIOBJ hUserPen,hDefPen;

 //方眼の線色の設定
 hUserPen = CreatePen(PS_SOLID,1,RGB(200,200,200));
 hDefPen = SelectObject(hdc,hUserPen);

 //方眼の作成
 //x軸の描画
 for(int i=0; i<= (GW_y2-GW_y1)/grid_interval_x + (GW_x2-GW_y2)/grid_interval_x; i++)
 {
  WCHAR szBuffer[100];
  int current_x_posi = GW_x1+grid_interval_x*i;
  MoveToEx(hdc,current_x_posi,GW_y1,NULL);
  LineTo(hdc,current_x_posi,GW_y2);
  _stprintf_s(szBuffer,L"%d",grid_interval_x*i);
  TextOut(hdc,current_x_posi,GW_y2,szBuffer,wcslen(szBuffer));
 }
 //y軸の描画
 for(int i=0; i<=(GW_y2-GW_y1)/grid_interval_y; i++)
 {
  WCHAR szBuffer[100];
  int current_y_posi = GW_y2-grid_interval_y*i;
  MoveToEx(hdc,GW_x1,current_y_posi,NULL);
  LineTo(hdc,GW_x2,current_y_posi);
  _stprintf_s(szBuffer,L"%dV",i);
  TextOut(hdc,GW_x1-20,current_y_posi,szBuffer,wcslen(szBuffer)); 
   
 }
 
 return 0;
}

 このようにして方眼を作成します。線を引くためにはまず始点をMoveToEx関数で指定して、そこからLineTo関数を使って線を描画します。また、軸に数値を表示するためにはまず、数値を文字列に変換するために、_stprintf_s関数を用いてからTextOut関数で描画します。

次に副スレッドでArduinoからデータを取得して、リアルタイムにプロットを描画していきます。
DWORD WINAPI threadfunc(LPVOID vdParam)
{
 HDC hdc;
 unsigned int iCount = 0;
 char data[255]="0"; 
 double ch1Value = 0.0;
 double ch2Value = 0.0;
 char* ch1Data = {0};
 char* ch2Data = {0};
 char* ctx = {0};
 HGDIOBJ hUserPen,hUserBrush,hDefPen,fDefBrush,hNullPen,hNullBrush;

 while(1)
 {
  hdc = GetDC((HWND)vdParam);
  //横軸のポイント数のカウントアップ
  iCount+=1;
  
  //現在取得した点(LineToの終点)
  nPointch1x =  GW_x1+iCount;
  nPointch1y = GW_y2-ch1Value*100;
  nPointch2x =  GW_x1+iCount;
  nPointch2y = GW_y2-ch2Value*100;
  //ch1の線色を指定して描画
  hUserPen = CreatePen(PS_SOLID,1,RGB(255,0,0));
  hDefPen = SelectObject(hdc,hUserPen);
  MoveToEx(hdc,nOldPointch1x,nOldPointch1y,NULL);
  LineTo(hdc,nPointch2x,nPointch1y);
  SelectObject(hdc,hDefPen);
  //ch2の線色を指定して描画
  hUserPen = CreatePen(PS_SOLID,1,RGB(0,0,255));
  hDefPen = SelectObject(hdc,hUserPen);
  MoveToEx(hdc,nOldPointch1x,nOldPointch2y,NULL);
  LineTo(hdc,nPointch2x,nPointch2y);
  SelectObject(hdc,hDefPen);

  //計測点のマーカの描画、marker_sizeを0にすると表示しない。
  if(marker_size != 0)
  {
   Rectangle(hdc,nPointch1x+marker_size,GW_y2-ch1Value*100-marker_size,nPointch1x-marker_size,GW_y2-ch1Value*100+marker_size);
   Rectangle(hdc,nPointch2x+marker_size,GW_y2-ch2Value*100-marker_size,nPointch2x-marker_size,GW_y2-ch2Value*100+marker_size);
  }
  
  //現在取得した点を次のLineTo始点にする。
  nOldPointch1x = nPointch1x;
  nOldPointch1y = nPointch1y;
  nOldPointch2x = nPointch2x;
  nOldPointch2y = nPointch2y;

  DWORD dwRead;
  DWORD dwSendSize;
  BYTE sendflag = 1;
  DWORD dwErrorMask;
  COMSTAT comStat;
  DWORD dwCount;
  TCHAR tdata[255];

  ClearCommError(serialPort,&dwErrorMask,&comStat);
  dwCount = comStat.cbInQue;
  WriteFile(serialPort,&sendflag,sizeof(sendflag),&dwSendSize,NULL);
  bool Ret = ReadFile(serialPort,&data,dwCount,&dwRead,NULL);
  if(!Ret){
   OutputDebugString(L"thread READ FAILED\n");
   CloseHandle(serialPort);
  }
  //シリアルデータが文字列として送信されてくるので区切る
  ch1Data = strtok_s(data,",\r\n",&ctx);
  ch2Data = strtok_s(null_ptr,",\r\n",&ctx);
  //ch2DataのNULLによるエラー防止処理
  if(ch2Data==NULL)ch2Data="9.9";
  //10bitデータを0~5Vに変換する。刻みはおよそ4.9mVとなる。
  ch1Value = map(atoi(ch1Data),0.0,1023.0,0.0,5.0);
  ch2Value = map(atoi(ch2Data),0.0,1023.0,0.0,5.0);
  //取得したデータを電圧に変換して表示
  _stprintf_s(tdata,L"ch1:%1.5lf     ch2:%1.5lf",ch1Value,ch2Value);
  TextOut(hdc,20,20,tdata,wcslen(tdata));
  //サンプリング間隔(ミリ秒)※要改善点※
  Sleep(1000);
 }
}

 副スレッドでデータを取得する部分は無限ループを一定間隔で回すことでリアルタイム描画を実現します。2ch分のデータを取得するのでArduinoからのデータは文字列にて送信し、C言語のプログラム側でカンマで分割処理することで2ch分のデータを同時に取得します。また、Arduinoからデータを送ってもらうタイミングを同期するためにC言語側から適当なデータをArduinoに送信し、そのタイミングでArduinoからデータを送信してもらうようにしています。

Arduino側のプログラムは
//logger 2ch
void setup() {
  // initialize the serial communication:
  Serial.begin(9600);
}

void loop() {
  // send the value of analog input:
  if(Serial.available()>0){
    Serial.print(analogRead(A0));
    Serial.print(",");
    Serial.println(analogRead(A1));
    Serial.read();
  }
}
以上です。

動作している様子は以下のようになっています。

_kiitaniさん(@monitorgazer)が投稿した動画 -

今回のプログラムのVisualStudioのプロジェクトファイルは以下からダウンロード可能です。

https://app.box.com/s/bw4gvmaqd7nlihvjaouj

改善が必要な部分
 現在のプログラムでは、軸の拡大縮小・移動が実装されていません。また、表示可能な部分が限られていますので、グラフ領域の端まできたら表示部位を切り替えてプロットしていくようなアルゴリズムを考える必要があります。また、データを保存する機能なども必要だろうと思います。これから改善してゆきます。

2014年11月29日土曜日

Maker Faire Tokyo 2014 へ行ってIntel の Edison 開発ボードをいただいた。

こんにちは。
 11/24に国際展示場で開催されたMaker Faire Tokyo 2014にもっちーと行ってきました。地の利を活かして、IntelのEdison開発ボードが配布されると話題となっていた各日先着36名のワークショップに参加してEdison頂いちゃいました。ちなみに6時に家でて国際展示場に7時半につきました。

▲ポール・ニコルソンさんすみません。みんなの肖像権を優先してしまいました。

 Edison値段的になかなか手を出せない感じなのですごく嬉しかったです。SDカードサイズにWifiまで入ってるとかすごいです。ここぞというときに使いたいと思います。ワークショップではかる~く開発環境の設定をして、専用のソフトを使ってポチポチやる感じでした。ガリガリ勉強しましょう!って感じではなかったです。MFT自体のレポはどこかだれかがいっぱい写真とってますのでそっちのほうが楽しいと思います。
 僕のまわりかたがヘタだったのか、以前行った(去年のMFTは行けなかった)大岡山のが楽しかったかな〜って印象をもちました!以上です!

 
 追伸
ヤフオクで中古のオシロスコープ買っちゃいました!楽しいです!でかい!うるさい!かわいい!
_kiitaniさん(@monitorgazer)が投稿した動画 -

2014年11月16日日曜日

NATURE OF CODE買った

Amazonを眺めていたら面白そうだったので買いました。英語版だったらWebで全部見ることができます。

 内容的には副題の通り、Processingで始める自然現象のシミュレーションとなっているんだけれども、書いてある内容がそれほどProcessingに特化しているわけではないので他の言語でも十分活用できると思います。

 今日届いて眺めた感じ、めっちゃ良かったので趣味のプログラミングで面白いことをしたい人にはおすすめです。

 Chapter0、”はじめに”を読んだのでその内容を軽くまとめます。

 ”はじめに”では最も有名で簡単な運動シミュレーションとしてランダムウォークを取り上げています。”はじめに”を完了する頃には乱数を扱う技術が向上しました。パーリンノイズすごい。

 順序的には
  1. Processingでオブジェクト指向のプログラミングを行う準備と解説
  2. 擬似乱数を使用した2次元のランダムウォークの実装
  3. 偏りを持つ乱数の実装
  4. ガウス分布に従う乱数の実装
  5. パーリンノイズに従う乱数の実装
となっています。

以下、”はじめに”を読んで書いたもの
_kiitaniさん(@monitorgazer)が投稿した動画 -


_kiitaniさん(@monitorgazer)が投稿した動画 -


_kiitaniさん(@monitorgazer)が投稿した動画 -

 最後の雲のような、霧のようなエフェクトは20行ぐらいで実装しています。パーリンノイズが優秀すぎです。なぜこれまで知らなかったんだろう…。

 パーリンノイズはケン・パーリンが1980年代のはじめに映画Tronの第一作の製作に携わり、より自然なCGエフェクトを生成するために考案した自然な秩序を持った一連の擬似乱数を生成するアルゴリズムだそうです。後にアカデミー科学技術賞を受賞したお仕事です。

 この後、ベクトル、力、振動、粒子系、物理ライブラリ、自律エージェント、セル・オートマトン、フラクタル、遺伝と進化、ニューラルネットワークについてコーディングしながら学んでいくと相当表現の幅が拡がるのではないかなと思います。”はじめに”の終わりで釘を刺されたようにパーリンノイズに頼りがちなってしまう罠に気をつけ、使える道具をもっともっと増やそうと思います!

2014年11月9日日曜日

無印良品のダブルリングモバイルノートがめっちゃいい

 こんにちは。この記事は雑記です。

 手帳の季節だそうです。私はこれまで手帳に挑戦しては敗れる。という経験を繰り返してきました。巷では”手帳が人生を変える!”などのキャッチコピーが溢れてまして、「はあそれならば」と手帳を購入してきました。”手帳を使いこなしている人”というのはなんか大人っぽい感じがして憧れます。しかし、私は使いこなすことができませんでした。なぜ破れてきたか。その理由をリストアップしてみました。

  1. Google Calendarを使ってるので手帳とGoogle Cal.の両方に予定を書くのが面倒
  2. ”これは手帳”というフォーマットに対するプレッシャーにより書けなくなる
  3. 大きさの問題で持ち歩くのが面倒
見事にダメパターンに嵌っているような気がします。

 これまで試した手帳としては
  • ほぼ日手帳(文庫本サイズ、一日1ページ。どんどん白紙のページが溜まっていった。あのスペースに書ききれる書きたいことが見つからなかった。)
  • クリエイターズダイアリー(蛇腹折りで一年を総覧できる。そもそも予定に〆きり日が多く、いつからいつまでといった期間的予定がなく総覧の恩恵がうけられなかった。)
  • フラットダイアリーA4(ノート状カレンダー。Google Cal.を使えばよかった。)
があります。全く使わなくなりました。すごくもったいない!!

 つまりは…つまりは…私の生活スタイルでは手帳はあわない!(ばばーん)Google Cal.で十分!しかしです、メモを書き留めたりするためにはまだまだ紙に勝るものはないと思っています。なのでノートは必要です。今はロディアのA5方眼リングノート使っていますが、方眼の線の色が濃すぎて使いにくいかなと思います。
▲ちょっと線の色がこい。

 そこで、タイトルのノートを購入しました。もともとは蔵前のカキモリさんでオリジナルノートを作成するつもりでしたが、B5、B6サイズしかなく、実際に触ってみると表紙もちょっと薄くてガサツな私が持つには上品だったので今回はパスしました。(値段もネックでした。平山くんごめん。)

 仕様(商品シールより)は
商品名:上質紙滑らかなかき心地ダブルリングモバイルノート 
A5、70枚、6mm横罫、ゴム留め付、税込500円、となっています。

 では以下フォトレビューです。
▲Dymoのエンボステープでナンバリングした。表紙の裏にはポケット付き!

▲特筆すべきは表紙の厚さ硬さ。厚みは一円玉と同じくらいある。

▲中表紙もしっかりしています。横罫は一般的なものに近い。

 安くていいです。ロディアより300円安い。これまでA5サイズを使ってきたのでサイズ的に違和感がないのが嬉しいです。また、表紙がしっかりしていて頼れる感じがします。横罫しかラインナップがない点が少し残念ですが、まあ問題ないでしょう。
 スケジュールはGoogle Cal.を使い、趣味のプログラミングのアイデアや記録、日常の諸々はノートに書いていくというスタイルでしばらくやってみようと思います。

2014年11月1日土曜日

【openframeworks】顔認識・追従で目を盗む【ofxFaceTracker】

 こんにちは。今回は前回のむりやり笑わせるプログラムよりも簡単なプログラムを作ります。具体的には、ofxFaceTrackerの機能を利用して顔を認識し、追従し、笑い男マークを上書きするプログラムです。攻殻機動隊用語でこれを”目を盗む”と言います。
俺の目を盗みやがったな

 
 実装の方法としては、顔の位置をofxFaceTrackerで取得し、その位置に合わせて笑い男マークを描画するだけです。簡単ですね。

 では以下プログラムです。

testApp.h
#pragma once

#include "ofMain.h"
#include "ofxCv.h"
using namespace ofxCv;
using namespace cv;

#include "ofxFaceTrackerThreaded.h"

#define INPUT_MOVIE_WSIZE   640
#define INPUT_MOVIE_HSIZE   480


class testApp : public ofBaseApp {
public:
 void setup();
        void exit();
 void update();
 void draw();
 void keyPressed(int key);
 
 ofVideoGrabber cam;
 ofxFaceTrackerThreaded tracker;
 ofVec2f position;
 float scale;
 ofVec3f orientation;
 ofMatrix4x4 rotationMatrix;
 
 Mat translation, rotation;
        ofImage ringImg,faceImg;
        cv::Mat ringMat,faceMat;
 ofMatrix4x4 pose;
 
        bool isFirstRead = true;
    
 ofEasyCam easyCam;
};


testApp.cpp
#include "testApp.h"

using namespace ofxCv;
using namespace FACETRACKER;

void testApp::setup() {
 ofSetVerticalSync(true);
 ofSetDrawBitmapMode(OF_BITMAPMODE_MODEL_BILLBOARD);
 
 cam.initGrabber(INPUT_MOVIE_WSIZE, INPUT_MOVIE_HSIZE);
 tracker.setup();
    //笑い男マークの文字のリングパーツをロード
    ringImg.loadImage("img/laughingman_ring.png");
    ringMat = toCv(ringImg);
    //笑い男マークの顔パーツをロード
    faceImg.loadImage("img/laughingman_face.png");
    faceMat = toCv(faceImg);
    
}

void testApp::exit() {
    tracker.waitForThread();
}

void testApp::update() {
 cam.update();
 
    if(cam.isFrameNew()) {
  tracker.update(toCv(cam));
  position = tracker.getPosition();
  scale = tracker.getScale();
  orientation = tracker.getOrientation();
  rotationMatrix = tracker.getRotationMatrix();
 }
}

void testApp::draw() {
 ofSetHexColor(0xffffff);
 cam.draw(0, 0);
    
 ofDrawBitmapString(ofToString((int) ofGetFrameRate()), 10, 20);
 
 if(tracker.getFound()) {
  ofSetLineWidth(1);
        //顔が見つかったら、画像の大きさをちょうどいい大きさに(改善必要)
        if(isFirstRead){
            cv::resize(ringMat, ringMat, cv::Size(ringImg.width/2,ringImg.height/2));
            cv::resize(faceMat, faceMat, cv::Size(faceImg.width/2,faceImg.height/2));
            isFirstRead = false;
        }
        
        ringImg.setFromPixels(ringMat.data, ringMat.cols,ringMat.rows, OF_IMAGE_COLOR_ALPHA);
        faceImg.setFromPixels(faceMat.data, faceMat.cols,faceMat.rows, OF_IMAGE_COLOR_ALPHA);
        //リングパーツと顔パーツの描画位置を決定
        int faceCenterX = (int)tracker.getImagePoint(29).x - ringImg.width/2;
        int faceCenterY = (int)tracker.getImagePoint(29).y - ringImg.height/2;
        int ringCenterX = (int)tracker.getImagePoint(29).x;
        int ringCenterY = (int)tracker.getImagePoint(29).y;
        ofPushMatrix();
            ofTranslate(ringCenterX, ringCenterY); //座標中心をピボット回転の中心へ移動
            ofPushMatrix();
                ofRotate(ofGetFrameNum() * -1, 0, 0, 1);//ピボット回転の中心に移動した座標軸を回転
                ofPushMatrix();
                    ringImg.draw(-ringImg.width/2, -ringImg.height/2);//オフセットを初期化し、描画
                ofPopMatrix();
            ofPopMatrix();
        ofPopMatrix();
        faceImg.draw(faceCenterX,faceCenterY);
    }
}

void testApp::keyPressed(int key) {
 if(key == 'r') {
  tracker.reset();
 }
}

 少し複雑な処理をしているのはリング部分を回転させるために座標軸の移動をしている点です。回転中心と画像の中心がずれてしまうと、その場でぐるぐる回るという動作をしなくなってしまうので注意する必要があります。また、写り込んだ顔の大きさに合わせて笑い男マークの縮尺を変更する処理を入れていないので、改善する必要があります。

以下はプログラムを動作をさせている際に撮影した動画です。

2014年10月29日水曜日

【openframeworks】画像処理でどんな顔も無理やり笑顔にしてしまう【ofxFaceTracker】

すこし間が開いてしまいましたが、前回の続きです。

 今回はofxFaceTrackerを使用し、フェイストラッキングの結果得られた顔の各パーツの位置情報を利用してどんな顔も無理やり笑顔にするプログラムを作ります。

基本戦略としてはお札の顔を笑わすあれと同じです。
これ
 考える必要があるのはこの「”山折り・谷折り”を画像処理でどのように表現したらよいか。」ということになります。今回は、ROI(関心領域)を利用することで実装することにします。
 ここで、問題なのはOpenCVではROIの範囲指定が各角が直角の四角形でしかできないということです。そこで、以下のようにROIを1pxづつ細切れにして少しづつずらしながら表示画像に貼り付けていくことで実現します。
 細切れのROI指定し、それを移動させていくことで山形の画像処理範囲をしてすることが可能となります。

では以下ソースコードです。
ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxCv.h"
using namespace ofxCv;
using namespace cv;

#include "ofxFaceTrackerThreaded.h"

#define INPUT_MOVIE_WSIZE   640
#define INPUT_MOVIE_HSIZE   480


class testApp : public ofBaseApp {
public:
 void setup();
    void exit();
 void update();
 void draw();
 void keyPressed(int key);
 
 ofVideoGrabber cam;
 ofxFaceTrackerThreaded tracker;
 ofVec2f position;
 float scale;
 ofVec3f orientation;
 ofMatrix4x4 rotationMatrix;
 
 Mat translation, rotation;
    ofImage leftEyeImg,rightEyeImg;
    cv::Mat processImgMat;
 ofMatrix4x4 pose;
 
 ofEasyCam easyCam;
};

ofApp.c

#include "testApp.h"

using namespace ofxCv;
using namespace FACETRACKER;

void testApp::setup() {
    ofSetVerticalSync(true);
    ofSetDrawBitmapMode(OF_BITMAPMODE_MODEL_BILLBOARD);

    cam.initGrabber(INPUT_MOVIE_WSIZE, INPUT_MOVIE_HSIZE);
    processImgMat = toCv(cam);
    tracker.setup();
}

void testApp::exit() {
    tracker.waitForThread();
}

void testApp::update() {
    cam.update();

    if(cam.isFrameNew()) {
        tracker.update(toCv(cam));
        position = tracker.getPosition();
        scale = tracker.getScale();
        orientation = tracker.getOrientation();
        rotationMatrix = tracker.getRotationMatrix();
    }
}

void testApp::draw() {
    ofSetHexColor(0xffffff);
    cam.draw(0, 0);
    //フレームレートを表示
    ofDrawBitmapString(ofToString((int) ofGetFrameRate()), 10, 20);

    if(tracker.getFound()) {
        ofSetLineWidth(1);
        int x,y; //ROIの範囲指定用
        int dHeight = 1;  //ROIの横幅
        int dWidth = 1; //ROIの縦幅
        int leftXOffset = -10;
        int yOffset = 5;
        unsigned char * pixels = cam.getPixels(); //Webカメラの画像ピクセル取得
        //OpenCVの変数型にofImageから変換
        cv::Mat camMat(cv::Size(INPUT_MOVIE_WSIZE,INPUT_MOVIE_HSIZE),CV_8UC3,pixels); 
        //左目
        int leftXStart = (int)tracker.getImagePoint(36).x - 20;
        int leftXEnd = (int)tracker.getImagePoint(40).x;
        int leftYStart = (int)tracker.getImagePoint(36).y;
        int leftYEnd = (int)tracker.getImagePoint(40).y;
        int leftXCenter = (int)tracker.getImagePoint(37).x;
        //目尻から目頭までの距離
        int leftEyeDistance = (int)sqrt(powerof2(leftXEnd-leftXStart)+powerof2(leftYEnd-leftYStart));
        //y軸方向にどれだけ移動させるか
        int leftYMax = (int)(leftEyeDistance/2)*dHeight+yOffset;
        for(int i=0;i<leftEyeDistance+abs(leftXOffset);i+=dWidth){
            //目尻から目の中央まで
            if(i+leftXStart <= leftXCenter){
                x = (int)(leftXCenter-((leftEyeDistance/2)+(i*dWidth)));
                y = (leftYMax-(i*dHeight))+yOffset;
                //ROI領域の設定
                cv::Rect cropROI = cv::Rect(x,y,dWidth,INPUT_MOVIE_HSIZE-y);
                camMat = camMat(cropROI).clone();
                //leftEyeImgに切り出した画像を読み込み
                leftEyeImg.setFromPixels(camMat.data, camMat.cols,camMat.rows, OF_IMAGE_COLOR);
                //表示画像へと貼り付け
                leftEyeImg.draw(x,0);
                camMat.release();
                camMat=toCv(cam);
            }
            //目の中央から目頭まで
            if (i+abs(leftXOffset)/2 > leftEyeDistance/2){
                x = (int)(leftXStart+30+((i*dWidth)));
                y = (leftYMax-(i*dHeight))+yOffset;
                cv::Rect cropROI = cv::Rect(x,y,dWidth,INPUT_MOVIE_HSIZE-y);
                camMat = camMat(cropROI).clone();
                leftEyeImg.setFromPixels(camMat.data, camMat.cols,camMat.rows, OF_IMAGE_COLOR);
                leftEyeImg.draw(x,0);
                camMat.release();
                camMat=toCv(cam);
            }
        }
        //右目
        int rightXOffset = 10;
        int rightXStart = (int)tracker.getImagePoint(45).x - 20;
        int rightXEnd = (int)tracker.getImagePoint(47).x;
        int rightYStart = (int)tracker.getImagePoint(45).y;
        int rightYEnd = (int)tracker.getImagePoint(47).y;
        int rightXCenter = (int)tracker.getImagePoint(44).x;

        int rightEyeDistance = (int)sqrt(powerof2(rightXEnd-rightXStart)+powerof2(rightYEnd-rightYStart));
        int rightYMax = (int)(rightEyeDistance/2)*dHeight+yOffset;
        for(int i=0;i<rightEyeDistance+abs(rightXOffset);i+=dWidth){
            if(i+rightXStart <= rightXCenter){
                x = (int)(rightXCenter-((rightEyeDistance/2)+(i*dWidth)));
                y = (rightYMax-(i*dHeight))+yOffset;
                cv::Rect cropROI = cv::Rect(x,y,dWidth,INPUT_MOVIE_HSIZE-y);
                camMat = camMat(cropROI).clone();
                rightEyeImg.setFromPixels(camMat.data, camMat.cols,camMat.rows, OF_IMAGE_COLOR);
                rightEyeImg.draw(x,0);
                camMat.release();
                camMat=toCv(cam);
            }
            if (i+abs(rightXOffset)/2 > rightEyeDistance/2){
                x = (int)(rightXStart+10+((i*dWidth)));
                y = (rightYMax-(i*dHeight))+yOffset;
                cv::Rect cropROI = cv::Rect(x,y,dWidth,INPUT_MOVIE_HSIZE-y);
                camMat = camMat(cropROI).clone();
                rightEyeImg.setFromPixels(camMat.data, camMat.cols,camMat.rows, OF_IMAGE_COLOR);
                rightEyeImg.draw(x,0);
                camMat.release();
                camMat=toCv(cam);
            }
        }
    }
}

void testApp::keyPressed(int key) {
    if(key == 'r') {
        tracker.reset();
    }
}

 マジックナンバーがところどころ散見されますが、自分の顔に合わせて微調整した結果です。
 このプログラムを実行すると以下のように処理されます。

@monitorgazerが投稿した写真 -
顔のピッチ角(首をかしげるような動き)への対応に関してはもう少し勉強したいと思います。

次回はもっと簡単な笑い男プログラムについて書きたいと思います。

2014年10月24日金曜日

【openframeworks】ofxFaceTrackerで遊んでみた。サンプル解説

 スマートホームがなんちゃら…のシリーズの更新が止まってしまっていますが…。
最近openframeworksをはじめまして、その習作としてofxFaceTrackerを利用したプログラムの作成を行ってみました。実行環境はOS X 10.9 Marvericks , MacBook Pro early 2011 13inchです。

この記事では
  1. ofxFaceTrackerのサンプルプログラムのレビュー
  2. Webカメラから取得した画像の一部に切り取り、貼り付けを行い、笑顔いっぱいデモの実装
  3. 複数の画像パーツとその並進、回転を利用した笑い男デモの実装
の3点の内容をお送りいたします。

1. ofxFaceTrackerのサンプルプログラムのレビュー  

 レビューといってもそれぞれがどんな動きをするプログラムなのか確認した程度になりますが…ofxFaceTrackerのサンプルプログラムは以下のようなものがあります。
  • example-advanced
  • example-align-eyes
  • example-calibration
  • example-blink
  • example-cutout
  • example-expression
  • example-extraction
  • example-non-realtime
  • example-threaded
全てに共通することなのですが、それぞれのbinディレクトリにdataディレクトリを作成し、modelディレクトリをコピーする必要があります。元のmodelディレクトリのパスはofxFaceTracker->libs->FaceTracker->modelみたいな感じです。あとはそれぞれのサンプルごとに必要なディレクトリやファイルが変わってくるので注意が必要です。

 example-non-realtimeはdataディレクトリ内のvideo.movを読み込んでフェイストラッキングを施すというサンプルに成っています。video.movは自分で用意する必要があります。Webカメラ等を使用してリアルタイムに取り込みを行う必要がないので、超高画質な動画ファイルや特殊なカメラで撮影した動画ファイルに対してフェイストラッキングを行いたいようなときには役立ちそうです。

 example-align-eyesは静止画像を読み込んで、顔を認識し、両目を繋ぐ直線と画像の縦軸が直交するように画像を回転させるプログラムになっています。静止画像に対する処理を行いたいときに参考になりそうです。dataフォルダの中にrawフォルダを作成し、その中に画像ファイルを入れることでプログラムで画像を読み込ませます。

 example-expressionは表情認識を行うプログラムです。例えば笑った状態のフェイストラッキングの結果をプログラムに学習させておいて、後からプログラムに「この顔は笑顔である、この顔は笑顔でない」などの評価させることができます。

 example-calibration, example-cutoutはi386 Architecture(32bitアーキテクチャ?)では実行できませんでした。自分の環境で実行できないものはしょうがないのでパスします。

example-advancedはファイストラッキングを行い、顔パーツや輪郭の特徴点を三次元座標上で表示するプログラムです。実行してみた感じでは、顔を探す部分の処理がとても重く、顔を探している間は2fpsほどしかでません。顔を見つけた後は60fpsで動作していました。以下は実行している時に撮影した動画です。
example-blinkは瞬きを認識するプログラムです。瞬きを使って他のプログラムをコントロールすることを目指しているプログラムの用で、ofxOSCのインクルードが必要です。XcodeのBuild SettingsでHeader Search Pathを書き足す必要があり、少し厄介です。
僕が引っかかった点を紹介します。

 画像にあるようにBuild Settingには簡易表示であるBasicと全設定項目を表示するAllがあるのですが、BasicになっているとHeader Search Pathsが表示されず、設定することができないので、気を付けましょう。ここに引っかかるのは僕だけかもしれませんが。

 以下は実行している時に撮影した動画です。example-advancedと同じように顔を見つけるまでは処理に時間がかかりますが、見つけた後は比較的滑らかです。瞬きに合わせて左上のグラフ表示の四角の中が白くフラッシュするのがわかるかと思います。まばたき上表をOSCを介して他のプログラムに伝えることができるようです。
 
 example-extractionは何をするプログラムなのかよくわかりませんでした…。勉強不足ですみません。実際の顔の画像を3Dテクスチャ上に貼り付けているような感じを受けますが…よくわかりませんでした。一応実行中に撮影した動画があります。
 
example-threadedはthreadを使用して、顔を見つけるまでの画像描画の遅延を解消しています。advancedとプログラムの表示結果は同じなのですが、advancedで見受けられた顔を発見するまでに”2fpsしか出ない”というような症状がありませんでした。実行している際の動画が以下になります。

以上でofxFaceTrackerの簡単なレビューを終わります。
2. Webカメラから取得した画像の一部に切り取り、貼り付けを行い、笑顔いっぱいデモの実装
3.複数の画像パーツとその並進、回転を利用した笑い男デモの実装
についてはレビューの記述だけでこの記事がいっぱいになってしまったので次回以降に回します。

2014年9月4日木曜日

Programmable XbeeとRaspberry Piを連携してスマートホームをDIY(1)

前回までの記事とはタイトルが変わりまして、Programmable XbeeとRaspberry Piの連携を中心に書いていきたいと思います。

基本的にはこれまで組んできたシステムと同じです。
目指すシステムの概略図を示します。

このシステムを作るために使ったものは
  1. Programmable Xbee Pro ZB 2台 (http://www.switch-science.com/catalog/606/)
  2. XBeeピッチ変換基板とソケットのセット(http://www.switch-science.com/catalog/100/)
  3. FT232RL USBシリアル変換モジュール(http://akizukidenshi.com/catalog/g/gK-01977/)
  4. XBeeエクスプローラUSBドングル(http://www.switch-science.com/catalog/1790/)
  5. 適当な大きさのブレッドボード
  6. Raspberry Pi modelB
  7. SSRキット(http://akizukidenshi.com/catalog/g/gK-00203/)
  8. 適当な電球ソケットつきACケーブルと電球
でした。

次回はProgrammable Xbeeの開発環境の構築です。

2014年9月2日火曜日

Socket.IOとNode.js、Node-serialport、それにArduinoを加えてスマホから家電のスイッチを制御する(4)

時間が開いてしまいましたが、前回までの記事ではMacbookのローカル環境においてNode.js等を使って家庭用AC100V電源を操作するところまで出来ました。今回の記事ではそれをRaspberry Piで動かします。

まず、RaspberryPiにNode.jsを動かすことができる環境を構築します。
これまでの記事…
を参考にiptableの設定を行っている場合、3000番ポートをNode用に開ける必要があります。

※SSHできるようにしておくと、開発が捗ります。

-A INPUT -p tcp --dport 3000 -j ACCEPT

次にnodeをインストールします。
参考:http://thinkit.co.jp/story/2013/08/22/4206
を参考にするといいと思います。

そして、Macbookに構築したものと同じものをRaspberry Piにも入れていきます。
参考:http://monitorgazer.blogspot.jp/search/label/Node.js

あとはMacbookで動かしていたindex.jsの部分を少し変更するのみです。

ar app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var SerialPort = require('serialport').SerialPort;
var serial = new SerialPort('/dev/tty.usbmodemfd121',{
    baudrate:57600
});
 
app.get('/',function(req,res){
    res.sendfile('index.html');
});
 
serial.on('open',function(){
    console.log('open');
});
 
serial.on('data',function(data){
    io.emit('recvmsg',data.toString());
});
 
io.on('connection',function(socket){
    socket.on('message',function(msg){
        serial.write(msg,function(err,results){
        });
    });
});
 
http.listen(3000,function(){
    console.log('listen 3000 port');
});

これを
ar app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var SerialPort = require('serialport').SerialPort;
var serial = new SerialPort('/dev/ttyACM0',{
    baudrate:57600
});
 
app.get('/',function(req,res){
    res.sendfile('index.html');
});
 
serial.on('open',function(){
    console.log('open');
});
 
serial.on('data',function(data){
    io.emit('recvmsg',data.toString());
});
 
io.on('connection',function(socket){
    socket.on('message',function(msg){
        serial.write(msg,function(err,results){
        });
    });
});
 
http.listen(3000,function(){
    console.log('listen 3000 port');
});
とするだけです。
変更部分は一箇所で、
var serial = new SerialPort('/dev/ttyACM0',{
    baudrate:57600
});
のシリアルポート名のみです。

シリアルポート名を調べる方法は、
ArduinoをUSBポートに繋ぐ前と後に
ls /dev
と入力し、増えたものがArduinoがつながっているシリアルポート名です。

これで、Rapsberry PiでAC100V電源のON・OFFの制御ができるようになりました。ここでこのNodeアプリを常に動かす(デーモン化する)ためにforeverを利用します。
参考:node.js node.jsスクリプトをforeverでデーモン化する
参考サイトのようにすれば特に問題が起きることはないと思います。

これでRaspberry Piにnode.jsの実行環境を構築し、ブラウザからAC100V電源のON・OFFを行うシステムをコンパクトにまとめる事が出来ました。ですが、今のシステムでは制御したい家電のそばにArduinoを含むRaspberry Piシステム一式を設置する必要があり、邪魔です。そこでRaspberry Piを母艦サーバと位置づけ、Programmable Xbeeというfreescaleマイコン入り無線モジュールを使用してもっと便利で使いやすいシステムに改良していきます。Programmable Xbeeを使うことでArduinoを取り除くことができ、コスト的にも優れたシステムを構築することが可能です。

2014年7月23日水曜日

Socket.IOとNode.js、Node-serialport、それにArduinoを加えてスマホから家電のスイッチを制御する(3)

こんにちは。前回までの記事ではNode.js、Socket.IO、Expressを使用してWebアプリケーションの開発方法を学んできました。ここからは、ブラウザを通して実世界とのインタラクションをデザインしていきます。具体的にはサーバPCにArduinoを接続することでさまざまなセンサやアクチュエータを動かしてブラウザでの操作を実世界に作用させていきます。Node.jsでSerial通信を行うためにNode-Serialportというパッケージを用います。今回の記事ではNode-SerialPortのインストールを行い、Arduinoとシリアル通信を行って、

  1. Arduinoからの情報をうけとる。
  2. Arduinoへ命令を出す。
という2つの動作を同時に実現したいと思います。

まずは、Node-SerialPortのインストールです。

これまでにやってきた方法と同じようにnpmを使用してインストールを行います。そのコマンドは、
npm install --save serialport
となっています。

続いては、Arduinoのプログラムを書きます。

ここで書くプログラムは非常に単純なシリアル通信のプログラムとなっています。内容としては、擬似乱数を生成しシリアル通信でindex.jsに送信、index.jsはクライアントとなるindex.htmlに受信した数値を送信し表示します。また、同時にindex.htmlに用意されたButton要素がクリックされたらindex.jsにイベントを送信し、index.jsからシリアル通信を利用してArduinoに命令を送信(今回は'1'を送信)し、命令を受信したArduinoでDigitalPinのHIGHとLOWを切り替えます。こうするとブラウザを経由してLチカ(LEDをチカチカさせること)ができるようになります。今回はLEDをソリッドステート・リレーに置き換えて、AC100V電源のON/OFFを切り替えます。

以下ソース
char incomingByte;
boolean sw = false;

void setup(){
    Serial.begin(57600);
    pinMode(4,OUTPUT);
}

void loop(){
        int rnd = random(1,100);
        delay(500);
        Serial.println(rnd);
        if(Serial.available() > 0){
            incomingByte = Serial.read();
            if(incomingByte == '1'){
                sw = !sw;
                if(sw == true){
                    digitalWrite(4,HIGH);
                }else{
                    digitalWrite(4,LOW);
                }
            }
        }
}
先ほど説明したように擬似乱数を生成してシリアル通信で送信し、命令があったらDigitalPinのHIGH/LOWを切り替えるというシンプルなプログラムになっています。DigitalPinが4番ピンを指定している理由は特にありません。何番でも大丈夫です。Arduinoの4番ピンにLEDのアノード(足が長い方)、GNDにカソードを刺します。電流制限抵抗とかはとりあえず気にしません。

続いてはChatアプリを基にシリアル通信を行うアプリを作製します。

でははじめにソースを示します。

index.js
ar app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var SerialPort = require('serialport').SerialPort;
var serial = new SerialPort('/dev/tty.usbmodemfd121',{
    baudrate:57600
});

app.get('/',function(req,res){
    res.sendfile('index.html');
});

serial.on('open',function(){
    console.log('open');
});

serial.on('data',function(data){
    io.emit('recvmsg',data.toString());
});

io.on('connection',function(socket){
    socket.on('message',function(msg){
        serial.write(msg,function(err,results){
        });
    });
});

http.listen(3000,function(){
    console.log('listen 3000 port');
});
新しく出てきたNode-SerialPortの部分は、
//ここと
var SerialPort = require('serialport').SerialPort;
var serial = new SerialPort('/dev/tty.usbmodemfd121',{
    baudrate:57600
});
//ここの部分です
serial.on('open',function(){
    console.log('open');
});

serial.on('data',function(data){
    io.emit('recvmsg',data.toString());
});

io.on('connection',function(socket){
    socket.on('sendmsg',function(msg){
        serial.write(msg,function(err,results){
        });
    });
});
上のほうではパッケージを使う宣言を行い、serialを作製しています。一番目の'/dev/tty.usbmodemfd121'という部分は、シリアルポートを特定する部分です。Macなのでこういった感じになっていますが、Windowsでは'COM3'などのようになるかもしれません。また、Raspberry Piに移植する際にもこの部分の変更が必要になります。
下の方では、Arduinoからのデータを受信し、index.htmlにrecvmsgというイベント名で文字列データとして送信する部分io.emit('recvmsg',data.toString());と、htmlからsendmsgイベントを受け取り、Arduinoに命令(msg)を送る部分serial.write(msg,function(err,results){});から成っています。とてもシンプルです。

index.html
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8" />
  <title>arduino control</title>
  <style>
   article, aside, dialog, figure, footer, header,
   hgroup, menu, nav, section { display: block; }
  </style>
 </head>
 <body>
            <h1>hello</h1>
            <button>SW1</button>
            <script src="/socket.io/socket.io.js"></script>
            <script src="http://code.jquery.com/jquery-1.11.1.js"></script>
            <script>
                var socket = io();
                $('button').click(function(){
                    socket.emit('sendmsg','1');
                });
                socket.on('recvmsg',function(data){
                    $('h1').text(data);
                });
            </script>
 </body>
</html>
このHTMLは、h1要素とbutton要素の2つの要素のみで構成されています。先ほどのindex.jsでArduinoから受け取ったデータをrecvmsgイベントを通してh1要素に表示し、button要素がクリックされたら、sendmsgイベントを通してindex.jsに'1'を送信します。この'1'は最終的にindex.jsからArduinoに送信されます。

図に書くと
となります。これで、Arduinoから情報を受信しつつ、Arduinoに命令を送ることができる双方向のWebアプリケーションが完成しました。秋月電子のソリッドステート・リレーと組み合わせた例が以下になります。
この電球は家庭用AC100V電源につながっています。そのため扇風機など他の家電を動かすこともできます。もちろんソリッドステート・リレーだけでなく、サーボモータや赤外線LEDなどなどArduinoで動かせるものはなんでも使うことができますし、ブラウザに表示する情報も温度湿度や照度、果ては空気清浄度までArduinoにつけるセンサやアイデアによっては無限大です。カメラがiPhoneしかない関係上、ブラウザはMacBookのものを使っていますが、もちろんiPhoneから操作することも可能です。

さて、次回の記事ではこのシステムをRapsberry Piに移植し、自動起動化することでよりコンパクトで便利なシステムにまとめていきます。

2014年7月21日月曜日

Socket.IOとNode.js、Node-serialport、それにArduinoを加えてスマホから家電のスイッチを制御する(2.5)

こんにちは。前回の記事ではsocket.ioの公式チュートリアルであるchatアプリを作成しました。今回はnode-serialportを追加してブラウザからArduinoを操作するところまでを行いたいと思います。前回の記事でソースについて解説することを忘れていたので、軽く説明をしてから本題に入ります。

*意外と解説が長くなったのでArduinoを操作する部分は次回の記事で書くことにしました。

まず、index.jsの解説を行います。

ソースは、
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
 
app.get('/', function(req,res){
    res.sendfile('index.html');
});
io.on('connection',function(socket){
    socket.on('chat message', function(msg){
        io.emit('chat message',msg);
    });
});
http.listen(3000,function(){
    console.log('listenning on localhost:3000');
});
となっていました。上から順番に説明してきます。
まず、
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
の部分では使用するパッケージの読み込みをしています。おまじないと思っても使うことはできます。
次に、
app.get('/', function(req,res){
    res.sendfile('index.html');
});
の部分では同ディレクトリのindex.htmlを読み込みます。
また、
io.on('connection',function(socket){
    socket.on('chat message', function(msg){
        io.emit('chat message',msg);
    });
});
の部分ではwebサイト(localhost:3000)に接続したことを検知した際の処理を記述しています。ここでは接続状態であるときにindex.html側のスクリプトから'chat message'を受け取ったら、その内容(msg)をindex.htmlに対して返す。というような内容になっています。

続いてindex.htmlについて

index.htmlのhtmlの要素としては2つしかありません。まずひとつはチャットの表示部分であるList要素、それから発言BOXとしてのForm要素です。
index.htmlのソースを見てみましょう。
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
<script src="/socket.io/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
    <input id="m" autocomplete="off"/><button id="btn">Send</button>
</form>
<script>
var socket = io();
$('form').submit(function(){
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
        });
socket.on('chat message',function(msg){
        $('#messages').append($('<li>').text(msg));
        });
</script>
</body>
</html>
まず重要なのが、
<script src="/socket.io/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
の部分です。外部ファイルとしてsocketio.jsとjquery.min.jsを読み込んでいます。jqueryはhtmlの要素を楽に操作するために使用しています。
次に重要なのは
<script>
var socket = io();
$('form').submit(function(){
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
        });
socket.on('chat message',function(msg){
        $('#messages').append($('<li>').text(msg));
        });
</script>
の部分です。前半ではform要素の中でsumbitアクションが起きた時の動作を決めています。この書き方はjqueryのものなのですが、#が着くとidを指定、何もつかない場合はその要素を全て指定という解釈をしています。必要になったらググります。後半の部分では、index.jsからのchat messageを検知して、msgの内容をlist要素に追加するというようになっています。

理解するために図を書くと…
という感じになっています。
意外と長くなってしまったので今回の記事は2.5回として、Arduinoとの通信は次回の記事にしたいと思います。

※追記2014/07/2123:19
画像の紫の部分がindex.htmlとなっていますが正しくはindex.jsです。

Socket.IOとNode.js、Node-serialport、それにArduinoを加えてスマホから家電のスイッチを制御する(2)

こんにちわ。前回の記事ではNode.jsをインストールするところまでをおこないました。この記事ではnpm(node package manager)を利用してSocket.IOをインストールしてチュートリアルであるchatアプリを作製します。

Socket.IOのインストール

Socket.IOをインストールするためにはnpmを用います。Macbookの環境ではNode.jsを入れた際に同時にnpmもインストールされていました。ない場合は個別にインストールが必要です。コマンドは、
npm install --save socket.io
です。これでNode.jsのパッケージをインストールする方法がわかるようになりました。

chatアプリの作製

それではSocket.IOの公式チュートリアルに則ってchatアプリを作っていきます。これを通してSocket.IOの使い方を学びます。公式チュートリアルはhttp://socket.io/get-started/chat/でみることができます。それによると、まずpackage.jsonを作製する必要があります。新しくチャット用のディレクトリchatを作製して移動します。
mkdir chat && cd chat

package.jsonの作製
 これはプロジェクトについて記述するマニフェストファイルです。これをchatディレクトに入れておきます。中身には名前やバージョンを記述します。チュートリアルなので適当に記述しておきます。
{
  "name": "socket-chat-example",
  "version": "0.0.1",
  "description": "it is first socket.io sample",
  "dependencies": {}
}
のように書いておきましょう。dependenciesの部分には追加しているpackageの情報を記述しますが、まだなにも入れていないので空になっています。

expressとsocket.ioのインストール
 package.jsonを記述したら使用するパッケージをインストールしていきます。まずexpressをインストールします。expressはnodeのwebフレームワーク。だそうです。詳しいことはよくわかっていませんが、ものすごい機能があるイメージです。続けてSocket.IOもインストールします。
npm install --save express
npm install --save socket.io
saveオプションをつける意味としては、package.jsonのdependenciesに自動的にパッケージ名とバージョンを記述してくれるということがあります。chatディレクトリをみると、node_modulesというディレクトリができているのがわかると思います。これでexpressとsocket.ioのインストールが完了です。

chatアプリの記述
 ここまでで環境が整いましたのでchatアプリを記述していきます。作成するファイルはindex.jsとindex.htmlになります。まずソースを載せます。

index.js

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req,res){
    res.sendfile('index.html');
});
io.on('connection',function(socket){
    socket.on('chat message', function(msg){
        io.emit('chat message',msg);
    });
});
http.listen(3000,function(){
    console.log('listenning on localhost:3000');
});

index.html

<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
<script src="/socket.io/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
    <input id="m" autocomplete="off"/><button id="btn">Send</button>
</form>
<script>
var socket = io();
$('form').submit(function(){
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
        });
socket.on('chat message',function(msg){
        $('#messages').append($('<li>').text(msg));
        });
</script>
</body>
</html>

これでチャットアプリが完成しました。これを実行するためには
node index.js
とやれば良いです。
動作の確認のためには複数のブラウザ(例えばノートパソコンとスマホ)からmacbookのipアドレス+3000ポートにアクセスします。例としては
http://192.168.1.2:3000/
のように入力すれば良いです。ipアドレスはifconfigを使用して調べます。
すると以下のようなチャットアプリが表示/動作します。
やったね!nodeのアプリを終了する際にはCtrl+cを押せば大丈夫です。

次回はchatアプリで学んだ方法を活かしてブラウザのボタンを押すとArduinoにシリアル通信を行い、Arduinoからの情報をシリアル通信で受信してブラウザに表示するアプリを作成します。

2014年7月20日日曜日

Socket.IOとNode.js、Node-serialport、それにArduinoを加えてスマホから家電のスイッチを制御する(1)

こんにちわ。このシリーズではNode.jsとExpress、Socket.IO、Node-serialportを使ってブラウザからArduinoからのシリアル通信を受け取ったり、ブラウザからArduinoへ制御コマンドをシリアル通信で送信してArduinoをコントールしたりします。最後にはRaspberry Piに移植して書いたプログラムをデーモン化することで、常にプログラムを動かし続け、ブラウザからAC電源のONOFFを制御するシステムを構築します。
 この記事では、nodebrewを利用したNode.jsのインストールについて記述します。

こんなのを作ることができます。

Node.jsのインストール
 Homebrewというパッケージ管理ソフトを使用してNodebrewをインストールします。なおapt-getやMacportsでもいいはずなので、そこは読み替えてください。Nodebrewはnodeのバージョン管理ソフトです。これをつかってNodeをインストールします。
 Homebrewでのインストールコマンドは、
(sudo) brew update
(sudo) brew install nodebrew
となっています。
パスの設定が必要になるので、.bashrcや.zshrcに
export "PATH=$HOME/.nodebrew/current/bin:$PATH"
と書き足しておきます。どこでも大丈夫だと思います。先頭でも、末尾でも。
インストールが無事終了していれば、
nodebrew help
と端末に入力するとnodebrewの使い方が表示されます。
nodebrew 0.7.4

Usage:
    nodebrew help                         Show this message
    nodebrew install             Download and install a  (compile from source)
    nodebrew install-binary      Download and install a  (binary file)
    nodebrew uninstall           Uninstall a version
    nodebrew use                 Use 
    nodebrew list                         List installed versions
    nodebrew ls                           Alias for `list`
    nodebrew ls-remote                    List remote versions
    nodebrew ls-all                       List remote and installed versions
    nodebrew alias          Set alias to version
    nodebrew unalias                 Remove alias
    nodebrew clean  | all        Remove source file
    nodebrew selfupdate                   Update nodebrew
    nodebrew migrate-package     Install global NPM packages contained in  to current version
    nodebrew exec  --   Execute  specified 

Example:
    nodebrew install v0.10.22     Install a specific version number
    nodebrew use v0.10.22         Use a specific version number
では、nodebrewをつかってnodeをインストールします。helpを参照するとインストールに使うコマンドは
nodebrew install 
or
nodebrew install-binary 
となっていることがわかります。僕は楽をしたかったのでinstall-binaryを選択しました。また、versionはなんとなくv0.10.26を選択。なので、インストールコマンドは
nodebrew install-binary v0.10.26
となります。次にどのバージョンを使用するかnodebrewに伝えます。
nodebrew use v0.10.26
そして、確認のために
nodebrew list
とすると使用しているNoad.jsのバージョンが表示されます。
nodebrew list
v0.10.26

current: v0.10.26
念のために、nodeコマンドを使用してnodeが使用できるようになったか確認します。
kiitani> node -v
v0.10.26
良さそうです。

これでNode.jsのインストールが完了です。
次の記事ではSocket.IOをインストールして公式チュートリアルであるchatアプリを作製することでSocket.IOの使い方を勉強します。


2014年7月6日日曜日

Raspberry Piを買って初期設定をする



すっごい久しぶりに書きます。
大学院生になってしまいました。

今日は近くの秋葉原でRaspberry Piを買ってきたので、セットアップを行いました。
忘備録を兼ねて簡単にまとめておきます。

  1. 購入すべきものと購入場所
  2. OSのインストール
  3. 初期設定
 ローカルホストとしてのPCはOSX 10.9.2 marvericksで端末にはiTerm2を使っています。VNCクライアントはFinderの機能を使います。Windowsの方はTeraTerm等でSSHを行うことができます。

1.購入すべきものと購入場所
 まずはRaspberry Piです。これがなくては始まりませんね?TypeBを買いましょう。秋葉原で現地調達するとなると、僕が確認したのはマルツと千石電商本館2Fです。マルツでは4700円+税、千石では4720円だったような気がします。今回は千石で買いました。それにしても千石電商本館2Fは色々センサやらモータも売っていてすごい楽しい。ちなみにRSオンラインから購入すれば3900円+送料です。今すぐ欲しい!休日にRaspiで遊びたいんだ!って人以外はネットで買ったほうが安いです。

 次に周辺機器です。
  • Raspberry Piの電源としてUSB(microB)充電器5V1Aのもの。
  • ストレージとしてClass10/8GB以上のSDカード。


  • ディスプレイ出力するためのHDMIケーブル。
  • 無線LANドングル もしくは LANケーブル。


  • あとは、持っていなければUSBキーボード。
これらは全てヨドバシカメラで購入。今からするとネットで買えばもっと予算が抑えられた気がします。注意点としては、Raspberry PiはSDカードとの相性があるので、http://elinux.org/RPi_SD_cardsを見て確認するといいかもしれません。

▲高専時代に買ったのと違って箱を開けたらボードでケースっぽいのはなかった。


2.OSのインストール
 Rapsberry PIのOSはさまざまなものがあり、NOOBSというシステムをつかってOS複数入れて切り替えて使うこともできますが今回は標準的なRASPBIANをインストールします。OSはhttp://www.raspberrypi.org/downloads/からダウンロードすることができます。僕はターミナルのddコマンドをつかってSDカードにOSのイメージを書き込みました。

 具体的な方法はhttp://www.myu.ac.jp/~xkozima/lab/raspTutorial1.htmlでとても詳しく記述されています。巨人の肩の上に乗っちゃいましょう。最近では、専用のソフトを使用するOSのインストール方法もあるようです。http://elinux.org/RPi_Easy_SD_Card_Setupで詳しく確認できます。また、それも不安だという人は、OS入りのSDカードを購入することもできます。割高ですが。

3.初期設定
 さまざまなサイトで初期設定について書かれているので、今回は自分がやったことをリストアップします。一度目に起動した時に出てくるあの青い設定画面はsudo raspi-configで再び出すことができます。

  • Expand File System
これを実行しないと、パーティションが小さいままでSDカードの容量がもったいないです。

  • Change User Password
セキュリティのためにデフォルトから変えましょう。ちなみにデフォルトはraspberryとなっています。

  • Internationalization Options
言語設定、地域に合わせた時刻設定、キーボードの種類の変更を行います。この時、言語設定でja_JP.UTF8を選択しておきましょう。この時、チェックを入れるためにはスペースキーを押します。またja_JP.UTF8を優先させることで、デスクトップを表示したとき日本語表示されます。時刻はAsiaからTokyoを選びます。

  • rootのパスワードの変更
デフォルトではrootにパスワードがかかっていません。コマンドは、

sudo passwd root

です。

  • 無線LANの設定
無線LANの設定はhttp://openrtm.org/openrtm/ja/content/raspberrypi_initial_settingを参考にすると良いです。


  • iptablesの設定
ファイアウォールを設定します。http://wordpress.zenmai.org/2013/09/29/raspberry-pi-9-2/の方の設定を参考に設定しています。このくらいを行っておけば最低限セキュリティを考えているといえるのでは無いでしょうか。Raspberry Piを踏み台にして、家のパソコンがいたずらされたらい屋ですからね…。



 えー。最後はオススメのサイトを紹介しまくるという投げやりな感じになってしまいましたが、次回はRaspberry Piに直接キーボードやモニターを刺さずに遠隔操作できるSSHやVCNについて書きたいと考えています。とりあえずググればできるよってことで!

2014年1月28日火曜日

ArduinoをWindowsにつないでGmailの未読メールの数を表示する (2)

前回記事
の続き.前回の記事ではlibcurlを使って,未読メール一覧を取得してchar型配列に格納するところまでやった.今回の記事では,文字列処理を行って未読メール数を取得するところまでをみていく.

以下にソースを示す.

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

#include <curl/curl.h>
#include <string.h>

typedef struct
{
    char* memory;
    size_t size;
} Memory;

size_t write_memory_callback(void* ptr, size_t size, size_t nmemb, void* data)
{
    if (size * nmemb == 0){
        return 0;
 }
    size_t realsize = size * nmemb;
    Memory* mem = (Memory*)data;
    
    mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory) {
        memcpy(&(mem->memory[mem->size]), ptr, realsize);
        mem->size += realsize;
        mem->memory[mem->size] = 0;
    }

    return realsize;
}

int main(void)
{
    CURL *curl;
    CURLcode res;
    char* start;
    char* end;
    //未読数が囲まれているタグを定義
    char start_str[] = "<fullcount>";
    char end_str[] = "</fullcount>";
    //タグ部分の切り出し
    char unread[100];
    Memory* mem = (Memory* )malloc(sizeof(Memory*));
    int startposition,endposition;
 int unreadNum = 0;
    
 while(1){
  mem->size = 0;
  mem->memory = NULL;
  curl = curl_easy_init();
 
  if(curl)
  {
       curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
       //ユーザ名・パスワード設定
       curl_easy_setopt(curl, CURLOPT_USERPWD, "username:password");
       curl_easy_setopt(curl, CURLOPT_URL,"https://mail.google.com/mail/feed/atom");
       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
       curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)mem);

       res = curl_easy_perform(curl);

       //エラーチェック 
       if(res != CURLE_OK){
          fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    }
       //クリーンアップ
       curl_easy_cleanup(curl);
  }

  start = strstr(mem->memory,start_str); 
  end = strstr(mem->memory,end_str);
  startposition = (int)(start-mem->memory);
  endposition = (int)(end-mem->memory);
  strncpy_s(unread, mem->memory+startposition,endposition-startposition);
  
  //数字の抜き出し
  int arrayIndex = 0;
  char unreadNumStr[10]={0};
  for(int i=0; i<strlen(unread); i++){
   if(isalnum((int)unread[i]) != 0){
    if(isalpha((int)unread[i]) == 0){
     unreadNumStr[arrayIndex] = unread[i];
     arrayIndex++;
    }
   }
  }

  unreadNum = atoi(unreadNumStr);
  printf("%d\n",unreadNum);
 }
    return 0;
}

簡単に説明すると,

1. strstr関数を使って,<fullcount>,</fullcount>を探し,そこから切り出しを始める.
 例えば,処理前の文字列が<fullcount>1</fullcount>asdfaklsufkashdf.....
だとすると,
startは<fullcount>1</fullcount>asdfaklsufkashdf.....
となり,
endは</fullcount>asdfaklsufkashdf.....
となる.この操作によって,欲しい文字列<fullcount>1</fullcount>の始まりと終わりのポインタがわかる.

2. 何文字目から何文字目までを切り出すのかを明確にする

3. strncpy_s関数を使って文字列を切り出す.
コピーのはじめと終わりの位置を指定してコピーすることで新しい文字列に目的の文字列を切り出します.

4. isalnum , isalpha関数を使って数字部分のみを取り出す.

5. atoi関数を使って文字列を数値に変換する.

という流れになっている.

2014年1月23日木曜日

ArduinoをWindowsにつないでGmailの未読メールの数を表示する (1)


Arduino Yún (http://arduino.cc/en/Main/ArduinoBoardYun)という,Wifi対応のLinuxを搭載したArduinoがある.そして,このArduino Yúnを使った作例に「Gmailに届くメールの数を表示」がある.この作例を普通のAruidnoを使って再現した.


なにをしたか
 以前書いた,
を組み合わせて,Gmailの未読メール数をSPI LED Moduleに表示した.どこに新しい(自分にとっての)技術勉強の要素があったかというと,C言語からGmailの未読メール数を取得することだった.実現にはlibcurlを使っている.

 ためしに,https://mail.google.com/mail/feed/atom にブラウザからアクセスすると,未読メールの一覧が表示される.このページをC言語で読み込み,文字列処理をすることによって未読メール数を取得する.といっても処理自体はかんたんで,未読メール数は<fullcount> </fullcount>というタグに囲まれているので,その部分を抜き出してやればよかった.


VisualStudio C++ 2012 でlibcurlを使う
この通りにやればいい.簡単に説明すると,
  1. http://curl.haxx.se/latest.cgi?curl=win32-ssl-devel-msvc からZipファイルをダウンロードし,好きなところに解凍する
  2. VisualStudioでプロジェクトを開き,解凍したlibcurlのIncludeディレクトリ,Libraryディレクトリを設定する.
  3. リンカー > 入力 > 追加の依存ファイルにcurllib.libを追記
  4. curllib.dll, libeay32.dll, openldap.dll, and ssleay32.dllをプロジェクトの適切なディレクトリに入れる

HTMLを取得して表示するプログラムは以下のようになる.

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
    CURL *curl;
    CURLcode res;
    
    curl = curl_easy_init();
    if(curl)
    {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.co.jp/");
        res = curl_easy_perform(curl);
        /* Check for errors */ 
        if(res != CURLE_OK)
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
               curl_easy_strerror(res));
        /* always cleanup */ 
        curl_easy_cleanup(curl);
    }
 system("PAUSE");
    return 0;
}

このようにcurl_easy_performを使うと,printf等を使わなくても標準出力に表示されるが,結果を利用してなにかしたいときには,char型配列に保存しなければいけない.その部分は,
を参考にさせていただきました.結果,無事char型配列に保存することが出来ました.

今日はここまで.
次回は文字列処理をして未読メール数を取得するところまでを書きます.

2014年1月18日土曜日

秋月電子のSPI(3線式)LED Module 8 Digital を使う

こんにちは.
今日は研究室に転がっていた秋月電子のSPI(3線式)LED Module 8 Digital を使ってみました.
商品URL:http://akizukidenshi.com/catalog/g/gM-06681/

 使い方とサンプルプログラムがhttp://www.dfrobot.com/wiki/index.php?title=SPI_LED_Module_(SKU:DFR0090)#Pinout_Diagram に示されています.どうやらbyte配列を送信することで表示が可能となるようです.
 サンプルプログラムではシリアル通信を使うことが前提となっています.ですが時計を作ったり,Arduinoに付いているセンサの値を表示する時には数値データをそのまま表示したいですね.ですので今回は,8桁(負数は7桁)までの数値をこのモジュールで表示するサンプルプログラムを書きました.

以下ソースコード

//Pin connected to latch pin (ST_CP) of 74HC595
const int latchPin = 8;
//Pin connected to clock pin (SH_CP) of 74HC595
const int clockPin = 3;
//Pin connected to Data in (DS) of 74HC595
const int dataPin = 9;
//7SegLEDの数
const int ledArray = 8;

byte byteData[ledArray] = {0};
char strData[ledArray] = {0};

//表示させたい数字ledArrayの桁数以内
//8桁の場合-9999999から99999999
long number = 20140119;

void setup() {
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
}

void loop() {
 sprintf(strData, "%ld", number);
 //char型文字をbyte配列に置き換え、右揃えで表示できる形に整形
 setDataFormat(strData, byteData);
 
 digitalWrite(latchPin, LOW);
 for(int i=ledArray-1; i>=0; i--){
     shiftOut(dataPin, clockPin, MSBFIRST, byteData[i]);
 }
 digitalWrite(latchPin, HIGH);
 
}

void setDataFormat(char* strData,byte *byteData){
  int numofNull=0;
  // char型数字をbyteコードに置き換えはじめ
  for(int i=0; i<ledArray; i++){
    switch(strData[i]){
      case '\0':
        byteData[i]=0xff;
        numofNull++;
        break;
      case '0': 
        byteData[i]=0xc0;
        break;
      case '1':
        byteData[i]=0xf9;
        break;
      case '2':
        byteData[i]=0xa4;
        break;
      case '3':
        byteData[i]=0xb0;
        break;
      case '4':
        byteData[i]=0x99;
        break;
      case '5':
        byteData[i]=0x92;
        break;
      case '6':
        byteData[i]=0x82;
        break;
      case '7':
        byteData[i]=0xf8;
        break;
      case '8':
        byteData[i]=0x80;
        break;
      case '9':
        byteData[i]=0x90;
        break;
      case '-':
        byteData[i]=0xbf;
        break;
    }
  }
  // char型数字をbyteコードに置き換えおわり
  //配列の入れ替えはじめ
  for(int i=0; i<numofNull; i++){
    byte tmp[ledArray];
    int j;
    for(int i =0; i<ledArray; i++){
      tmp[i]=byteData[i];
    }
    for(int i =0; i<ledArray; i++){
      if(i==0){
        j = ledArray-1;
      }else{
        j = i-1;
      }
      byteData[i] = tmp[j];
    }
  }
  //配列の入れ替えおわり
}


今回のポイント
  1. 送信するbyte配列:byteDataを読み込ませる順番
  2. 表示の右揃え
  3. マイナスを表示
送信するbyte配列:byteDataを読み込ませる順番
 このモジュールでは読み込んだbyteデータを右から順番に並べていきます.例えば,20140119を配列の0番目から7番目まで読み込ませた場合,91104102と表示されます.これを解消するには配列の7番目から0番目に向かって読み込ませていけば大丈夫です.つまり,配列を右から読んで右から詰めるということです.整理整頓!(?)

表示の右揃え
 このプログラムでは数値データをsprintfという関数をつかって文字型数字配列に変換しています.ここでもしも,桁数が7桁の数値1234567を変換すると'1','2','3','4','5','6','7','\0'というようになります.これを表示すると
というようになります.数字データなのに右端が開くのは違和感があります.そこで,送信前に配列を入れ替えてすべての'\0'を左端に持ってくることで表示を右揃えにします.
具体的には82行目から94行目の部分です.まず,配列の複製を行います.そして入れ替えを行います.入れ替えでは,入れ替えれる側(元の配列)の先頭に\0を入れたいので,0番目の要素の時だけ,複製された配列の最後の要素つまり,7番目の要素を代入します.残りは一つづつ右にずらしていきます.これを\0の数だけ繰り返すとどんなに桁数が減っても右揃えして表示できるようになります.

マイナスを表示
 数字以外のアルファベットやハイフン、ピリオドなどを表示したくなった時どうしたらよいかです.サンプルプログラムでは文字型数字からバイトコードへの変換を40行目から79行目で行っています.処理は単純なswitch-case文です.このモジュールの場合,LEDの各セグメントと8ビットの関係は次の様になっています.例えば,ハイフンを表示したいときには10111111となります.
次に,得た10111111を16進数にします.自分で計算してもいいのですが,めんどくさい人は,http://www.binary-code.org/ のサイトを使いましょう.

主なポイントは以上です.
もっとよく知りたい方はこのモジュールを実現しているシフトレジスタ74HC595について調べたり,小数表示を実装してみたりするといいと思います.