INDEX(各項目ごとの目次)

[HOME]  [Processing関係]  [Arduino関係]  [マテリアル関係]  [秋葉原工作マップ]

2008年用ですが、部分的に内容を更新しています(2010/06/14)。
また、[建築農業工作ゼミ2009-2010]とも連動していますので、そちらにも幾つかサンプルがあります。
:

検索キーワード「processing」に一致する投稿を関連性の高い順に表示しています。 日付順 すべての投稿を表示
検索キーワード「processing」に一致する投稿を関連性の高い順に表示しています。 日付順 すべての投稿を表示

7/30/2008

Processing サウンド4/スクラッチ

前回までSoniaライブラリを使って、音源の再生、逆再生ならびにテンポ(スピード)変換をしました。これらを組み合わせると、スクラッチ(ターンテーブルやCDJ)のようなことができるので、応用としてプログラミングしてみたいと思います。マウスを上下(Y座標)にドラッグすることで、スクラッチすることにします。画面下方へドラッグすれば順方向へ、画面上方へドラッグすれば逆方向へ曲が再生されるようにします。そのとき、ドラッグした量に比例して再生される速度も変化するようにします。スクラッチ中やスクラッチを解除したときに、ターンテーブルの回転数の立ち上がりを少し鈍くするために、テンポの変化にフィルターをかけることにします。
*音源データは予め用意して、スケッチフォルダ内のdataフォルダ内にいれておいて下さい(方法については、「Processing サウンド1/Sonia」を参照)。


//ライブラリを取り込む
import pitaru.sonia_v2_9.*;

//順再生と逆再生のオブジェクトを用意
Sample tuneFW;
Sample tuneRV;
//音源データの長さの変数
int dataLength;
//順再生の再生位置の変数
int currentFrameFW;
//逆再生の再生位置の変数
int currentFrameRV;
//テンポの変数
float tempo=0;
//フィルター付きテンポの変数
float tempoFiltered;

void setup() {
size(200,200);
//Soniaの使用開始
Sonia.start(this);
//音源の指定
tuneFW = new Sample("music.wav");
//音源の全フレーム数を調べておく
dataLength=tuneFW.getNumFrames();

//逆再生用のデータを作成
tuneRV= new Sample(dataLength);
float[] forward=new float[dataLength];
tuneFW.read(forward);
float[] backward=new float[dataLength];
backward=reverse(forward);
tuneRV.write(backward);

//念のため、順再生/逆再生のフレームレートを調べる
println(tuneFW.getRate()+":"+tuneRV.getRate());

//順再生する
tuneFW.play();
//テンポを1.0倍(標準)にしておく
tempo = 1.0;
}

void draw(){
background(0);
//テンポに少し反応を鈍くするフィルターをかける
tempoFiltered+=(tempo-tempoFiltered)/4;
//テンポ設定
tuneFW.setSpeed(tempoFiltered);
tuneRV.setSpeed(tempoFiltered);

//それぞれの再生位置を取得する
if(tuneFW.isPlaying()){//順再生の時
//順再生の再生位置を記憶させておく
currentFrameFW = tuneFW.getCurrentFrame();
//逆再生の再生位置を全フレーム数から
//順再生の再生位置を差し引いて計算しておく
currentFrameRV = dataLength-currentFrameFW;
}
else{//逆再生の時
//逆再生の再生位置を記憶させておく
currentFrameRV = tuneRV.getCurrentFrame();
//順再生の再生位置を全フレーム数から
//逆再生の再生位置を差し引いて計算しておく
currentFrameFW = dataLength-currentFrameRV;
}
}

//ドラッグしたとき(スクラッチ中)
void mouseDragged(){
//前回と今回のマウスY座標値の差を求める
int difY=mouseY-pmouseY;

if(difY>=0){//差がプラスなら(下向きにドラッグした時)
//再生位置から順再生させる
tuneFW.play(currentFrameFW,dataLength);
//逆再生停止
tuneRV.stop();
}
else{//差がマイナスなら(上向きにドラッグした時)
//再生位置から逆再生させる
tuneRV.play(currentFrameRV,dataLength);
//順再生停止
tuneFW.stop();
}
//差の絶対値を4で割った値をテンポにする
tempo=abs(difY)/4;
}

//マウスを放した時(スクラッチ解除時)
void mouseReleased(){
//再生位置からテンポを1.0倍にして順再生させる
tempo=1.0;
tuneFW.play(currentFrameFW,dataLength);
//逆再生停止
tuneRV.stop();
}

public void stop(){
Sonia.stop();
super.stop();
}

ここをクリックでサンプルのサイトへ移動します。
*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。


スクラッチによって曲が行ったり来たりするので、順再生と逆再生の二つの音源を別々に用意し、交互に切り替えて再生させています(逆再生の音源のつくりかたは「Processing サウンド2/逆再生」を参照)。まずsetup(){...}内の「dataLength=tuneFW.getNumFrames()」によって、曲の全フレーム数を調べておきます。全フレーム数から順再生された分のフレーム数を差し引けば、次に逆再生のされるときの再生箇所が導きだされます。そのため、draw(){...}内で毎回現在順再生か逆再生かをisPlaying()によって判別し、もし順再生なら「currentFrameFW = tuneFW.getCurrentFrame()」で現在の再生位置を調べておき、同時に次に逆再生されるときの再生位置を「currentFrameRV = dataLength-currentFrameFW」で計算して記憶させておきます。
念のために、setup(){...}内の「println(tuneFW.getRate()+":"+tuneRV.getRate())」で、順再生と逆再生のサンプルレートを調べておきます。同じ値ならいいのですが、もし異なる場合は、順再生と逆再生の再生速度が変わってしまうので、draw(){...}内のsetSpeed()で片方のスピード値を半分にするなどしてください。
スクラッチは前回と今回のマウスのY座標の差分をテンポに反映させて再生させています。大きくドラッグすれば、早回しで再生されることになります。ただし音が早回りしすぎるときがあるので、「tempo=abs(difY)/4」というように、値を4で割ってスケールダウンしています(4以外の数値を入れて調整してみてください)。ここで得られたテンポの値は、draw(){...}内の「tempoFiltered+=(tempo-tempoFiltered)/4」に代入され、テンポの変化が少し鈍く反映されるようにフィルターをかけています。ここでも4で割っていますが、もう少し反応を鈍くさせたい場合は4より大きい値で割ってください(少し反応を鈍くさせたほうが、スクラッチらしい音になるので、そうしています)。abs()は絶対値に変換する関数です。最終的にテンポに代入される値はプラスなので、差分がマイナスの場合にプラスになるように調整しています。
マウスを放したときは、スクラッチを止めた時なので、もともとの順再生の状態に戻るようにし、逆再生は停止させます。その時のテンポも標準の再生スピードになるように1.0に戻しておきます。

関連:
Processing サウンド3/テンポ」--音源再生のテンポ変換をする
Processing サウンド2/逆再生」--逆再生の音源をつくる
Processing サウンド1/Sonia」--音源の再生/停止/ポーズする

今回のプログラムのサンプルサイトへのリンク

*MacOSX(Intel)の場合、新たにJSynプラグインをインストールする必要があります。Windowsの場合も、上記サンプルサイトをオンラインで視聴するためにプラグインが必要となります。ここをクリックすると、インストール画面に移動します。移動先のページ上の「Click Here to Install JSyn Plugin」のボタンを押してインストールしてください。

 iTunes Music Store(Japan)

10/17/2008

Arduino+Xbee Shield/Processing+XBee Explorer USB

Arduinoにはワイヤレス通信するためのXbee Shieldがあります。今回はArduinoのXbee Shieldのサイトを参考に簡単な通信実験から始めたいと思います。Xbee Shiledを装着したArduino基板が2個必要になります。


(Xbee Shiledを装着したArduino基板)

注意しなければいけないことは、Arduino基板にXbee Shiledを装着したままプログラムをアップロードする際、Xbee Shiled上にある二つのジャンパピン(二つの間には「XBEE/USB」と表示されています)を「USB」側に差し替えなければいけないことです(3本のピンのうち、USB側のピンと中央のピンの2本のピンに差し込まれている状態になります)。
尚、アップロード後は「XBEE」側のピンと中央のピンの2本のピンに差し込まれている状態に戻してください。


「二つのArduino間での通信」
以下では、Xbee Shieldのサイトに従って、LEDの点滅実験を行います。一方のXbee Shiledを装着したArduino基板から、一秒おきに'H'か'L'の文字を送信し、受信したもう一方のXbee Shiledを装着したArduino基板の13番ピンに接続されたLEDが点滅する内容です。基本的には、通常のシリアル通信のプログラムと同じような内容になります。

送信側Arduinoのプログラム:
void setup(){
  //シリアル通信開始
  Serial.begin(9600);
}

void loop(){
  Serial.print('H');//「H」を送信(点灯)
  delay(1000);      //1秒待つ
  Serial.print('L');//「L」を送信(消灯)
  delay(1000);      //1秒待つ
}

送信側は一方的に'H'と'L'を1秒間隔で送信するだけです。受信側は、送信側からのデータが届いたら読み込みをし、データが「H」であれば13番ピンをHIGHで出力し、そうでなければLOWで出力するプログラムになります。

受信側Arduinoのプログラム:
int val;//受信データ用の変数を用意

void setup(){
  //シリアル通信開始
  Serial.begin(9600);
  //13ピンをデジタル出力に設定
  pinMode(13,OUTPUT);
}

void loop(){
  if(Serial.available()>0){ //データが来たら
    val=Serial.read();    //データを読み込み、valへ代入
  }
  if(val=='H'){           //valが「H」の場合
    digitalWrite(13,HIGH);//点灯
  }else{                  //valが「L」の場合
    digitalWrite(13,LOW); //消灯
  }
}

乾電池などの外部電源でArduinoを作動させれば、それぞれスタンドアロンで通信し合います。Decimilaなどの旧型のArduino基板の場合、外部電源を使うにはArduino基板についている「USB/EXT」のジャンパピンを「EXT」側に差し替える必要があります(プログラムをアップロードする際には、パソコンとUSB接続し、ジャンパピンも「USB」側に差し替えて下さい)。



「コンピュータに接続したXbeeと通信」:
コンピュータにXbeeを接続し、Arduinoとワイヤレス通信するには以下のようになります。
Sparkfunで販売されている「XBee Explorer USB」を使うと簡単にコンピュータとUSB接続でき、Processingからワイヤレスにシリアル通信が可能になります。


(左:「XBee Explorer USB」、右:Xbeeを装着した状態)

以下では、先ほどの受信用のXbee Shieldを装着したArduino基板に対して、Xbeeモジュールを装着した「XBee Explorer USB」を通して、Processingからワイヤレスでシリアル通信してみます。
まず、「XBee Explorer USB」のシリアルポートを調べてみます。

import processing.serial.*;
println(Serial.list());

「XBee Explorer USB」をコンピュータに接続し、上記の二行のプログラムをランさせれば、「XBee Explorer USB」のシリアルポートが出力されるはずです。


「Processingのプログラム」
受信側となるArduinoのプログラムは先ほどと同じものを使い、送信側となるProcessingだけのプログラムを以下に書きます。マウスボタンを押したら点灯、放したら消灯する内容とします。

//シリアル通信ライブラリを取り入れる
import processing.serial.*;
//ポートのインスタンス
Serial port;

void setup(){
  //「XBee Explorer USB」のシリアルポート設定
  port=new Serial(this,"/dev/tty.usbserial-A8003VXd",9600);
}
void draw(){
  //特になし  
}

void mousePressed(){//マウスボタンを押したら
  port.write('H');  //「H」を送信(点灯)
}

void mouseReleased(){//マウスボタンを放したら
  port.write('L');   //「L」を送信(消灯)
}

プログラムの内容はこれまでのシリアル通信と同じなので、ポートの設定、接続やジャンパピンの差し替えなどを間違わなければ特に問題はないと思います。



「Xbeeモジュールの設定/ATコマンド」
XbeeにATコマンドを送ることで、Xbeeモジュール自体の設定を確認したり変更することができます。ATコマンドをXbeeモジュールに送信するには、MacOSXなら「ZTerm」など、Windowsなら「ハイパーターミナル」や「Tera Term」などのターミナルアプリケーションで入力すると便利です(「ZTerm」と「ハイパーターミナル」の設定や使い方については、「Arduino-Processing BlueTooth通信+曲げセンサ」にも、説明があります)。

コンフィグレーションモードに入るには、
+++

プラスを3回入力し待機します(リターンキーは押さない)。そうすれば、
OK

という返事が返ってきます(ATコマンドモードでは、「+++」を押したあと1秒後に「OK」が返答され、さらに10秒以内に次のコマンドを送信しなければコマンドモードが自動的に終了してしまうので、返答がない場合は再度「+++」を押す必要があります)。さらに続けて、
ATID(リターンキーを押して送信)

と押せば、デフォルトの状態であれば、
3332

という数値が返ってきます。「3332」という数値は、そのモジュールのネットワークIDであり、このネットワークIDを変更すれば同じネットワークIDを共有しているXbeeモジュール間だけでの通信が可能になります(ひとつのモジュールだけでなく、相手になるモジュールも変える必要があります)。
設定されている通信速度を確認するには、
ATBD(リターンキーを押して送信)

と押せば、デフォルトであれば、
3

という数値が返ってきます。通信速度はそれぞれ
0:1200 bps
1:2400 bps
2:4800 bps
3:9600 bps
4:19200 bps
5:38400 bps
6:57600 bps
7:115200 bps

なので、デフォルトの「3」は9600 bpsということになります。
ATBD4(リターンキーを押して送信)

と打てば(「ATBD」の後に「4」を付け加える)、通信速度は「4」(19200)に変更されます(「OK」という返答がきます)。再度、
ATBD(リターンキーを押して送信)

と打てば、変更した内容を確認できます(この場合「4」という数値が返ってきます)。ただし、この場合電源が切れると設定内容は消えてしまいます。電源を切っても設定内容が戻らないようにするには、
ATWR(リターンキーを押して送信)

を変更後すぐに送信します(「OK」という返答がきます)。あるいは、
ATBD4,WR(リターンキーを押して送信)

という感じで、コマンドを複数合成して送信することもできます(「OK OK」という二つ分の返答がきます)。デフォルトの状態に戻すには、
ATRE(リターンキーを押して送信)

になるのですが、「WR」を付け加えていないので再度電源を入れたときには前回の状態に戻ってしまいます。
ATRE,WR(リターンキーを押して送信)

とすれば、電源を入れ直してもデフォルト状態は保持されます。
尚、コマンドモードから出るには、
ATCN(リターンキーを押して送信)

になります。
デフォルトでは、
ID:3332(ネットワークID)
CH:0x0C(チャンネル)
MY:0(そのモジュールのアドレス)
SH: (シリアルナンバー上位32ビット/モジュールごとに異なる)
SL: (シリアルナンバー下位32ビット/モジュールごとに異なる)
DH:0(送信先アドレス上位32ビット)
DL:0(送信先アドレス下位32ビット)
BD:3(通信速度:9600 bps)

に設定されており、ネットワーク、チャンネル、モジュールのアドレス、送信先アドレスがそれぞれ同じであるため、どのモジュール間でも通信可能です。逆に、ネットワークIDを変更してしまえば、他のモジュール群から干渉を受けずに通信し合うことも可能になります。あるいは、二つのモジュール間で送信先を互いに設定してしまえば、そのアドレスのモジュールだけとの通信が可能になります。そのためには、IDとCHは共有しておき、それぞれのモジュールアドレスを個別に設定しておきます。
ATMY1111,WR(リターンキーを押して送信)
OK OK(Xbeeからの返信)

とすれば、このモジュールの「MY」は「1111」に設定されたことになります(「WR」を付け加えたので、電源を落としても変更内容は記憶されます)。さらに送信先のアドレスを「2222」に設定するには、「DL」を「2222」に「DH」を「0」にします。
ATDL2222,DH0,WR(リターンキーを押して送信)
OK OK OK(Xbeeからの返信)

もう一方のほうも設定する必要があるので、「MY」を「2222」、「DL」を「1111」、「DH」を「0」にします。
ATMY2222,DL1111,DH0,WR(リターンキーを押して送信)
OK OK OK OK(Xbeeからの返信)

とします。こうすることで、「1111」のモジュールと「2222」のモジュールが互いに送信先を特定して通信し合うことができます。

Arduino Xbee Shieldサイトの説明によれば、「DH」を「0」、「DL」を「FFFF」に設定すれば、そのモジュールからの通信は、その他のすべてのモジュールによって受信可能になります。
また、送信先アドレス(上位ビットDHと下位ビットDL)が「FFFF」より大きい値(つまり「DH」が「0」以外の数値に設定したとき)、その「DH」と「DL」が相手モジュールの「SH」と「SL」に等しければ、相手モジュールのみに受信させることが可能になります。ただし、この場合も、ネットワークIDとチャンネルは同じでなければなりません。


「Ztermの設定」
以下は、MacOSXで「ZTerm」を使ったときの画面です。


ATコマンドを打つ画面。



メニューバー>Dial>Directory...をクリックすれば、上画面が現れます。「New」を押せば、新たな接続先を追加する画面(下画像)がでてきます。



「Service Name:」の欄に適当な名前を入れます。「Local Echo」にはチェックをいれておきます。チェックを外すと、自分の打った文字は画面に現れないので、チェックを入れておいた方がいいでしょう。Xbeeがデフォルト状態であれば、その他の項目は上画面のようになります。メニューバー>Setting>Connection...をクリックすることで、再度この画面が現れます。



メニューバー>Setting>Terminal...をクリックすれば、上画面が現れます。ここでは「Auto Line Feed」にチェックをいれておきます。チェックを外すと、ATコマンドが改行されなくなるので、チェックをいれておいたほうがいいでしょう。恐らく、MacOSXの改行コードは、「CR(キャリッジリターン:行頭に戻る)」だけなので、「LF(ラインフィード:次の行に移る)」も付け加えないと改行されなくなるからでしょう。

Zigbee開発ハンドブック (実践入門ネットワーク)
鄭 立
リックテレコム
売り上げランキング: 9866

DESIGNING ZIGBEE NETWORKS AND TRANSCEIVERS: The Complete Guide for Rf/Wireless Engineers
Shanin Farahani
Newnes
売り上げランキング: 21994

8/18/2008

Arduino デジタルコンパス/HMC6352

デジタルコンパス(方位センサ)によって、物体の方位を磁気的に調べることができます。Arduinoで制御可能あるいは入手しやすいデジタルコンパスとして幾つか以下に挙げておきます。

デジタルコンパスの種類:
・「RDCM-802」(秋月電子:¥3400
・「HMC1052L」(Sparkfun:$24.95ストロベリーリナックス:¥3480
・「HMC6352」(Sparkfun:$59.95ストロベリーリナックス:¥7350スイッチサイエンス:¥6980
・「HM55B(日立製)」(Parallax:$29.99日本マイクロボット教育社:¥4450
・「CMPS03」(RoboticsConnection:$55)

それぞれの性能など:
「RDCM-802」は、3ビットの信号で8方位の解像度しかないので、大体の向きを調べるくらいしかできません。
「HMC1052L」は、ArduinoのanalogRead()で入力すれば10ビット(1024段階)の解像度が得られます。
「HMC6352」は、I2C通信で0.0〜359.9度(0.1度ずつ)の分解能があります。
「HM55B」は、シリアル通信で11ビット(2048段階)の解像度が得られます。
「CMPS03」は、I2C通信で3600の分解能。

サンプルソースについて:
「HMC6352」については、WiringのExamplesのサイトにサンプルソース(Standbyモード)があり、I2C通信のライブラリを用いればArduinoでも使用可能になります。ArduinoのPlaygroundにもサンプルがあります。
「HM55B」については、ArduinoのPlaygroundのサイトにサンプルソースがあります。
「CMPS03」については、Arduino用のライブラリがあります。

HMC6352の使い方:
今回は、「HMC6352」デジタルコンパスモジュールの実験をしてみます。
「HMC6352」は、I2C通信で方位データを得るので、I2C通信のライブラリを用います。ライブラリを取り入れるには、メニューバーのSketch>Import Library>Wireを選択することで「#include <Wire.h>」の一文が自動的にプログラムを書く欄に挿入されます。ArduinoでのI2C通信は、SDA端子(データ用端子)はアナログ入力の4番ピン、SCK端子(クロック用端子)はアナログ入力の5番ピンに限定されます。「HMC6352」は、5V電源でも許容範囲ですが今回は3.3Vを使用することにします。接続方法は以下の様になります。



アドレスについて:
I2C通信では、マスターデバイスとスレーブデバイスという関係性があります。マスターデバイスには複数のスレーブデバイスが接続できるメリットがあり、複数のスレーブデバイスはマスターデバイスから制御されることになります。今回の場合は、Arduino基盤がマスターデバイス、HMC6325がスレーブデバイスになります。複数のスレーブデバイスが接続可能であるため、スレーブデバイス側にはどのデバイスであるかを識別するためのアドレスが必要となります。データシートによれば、HMC6325のデフォルトのアドレスは16進数の「0x42」であり、今回はこのアドレスを使うことにします。ただし、識別されるアドレスは8ビット中の上位7ビットであり、「0x42」を1ビット右にシフト「>>」した値になります(16進数の「0x42」は、二進数の「01000010」であり、1ビット右にシフトするということは、二進数の「01000010」の桁を右に一桁ずらすことになるので、「00100001」(=0x21)になるということです/ビットシフトに関しては「Arduino-Processing シリアル通信3」を参照)。

モード切替について:
HMC6325には、マスターデバイスから「A」を送信することで方位を計測開始しデータを読み取る「Standbyモード」と、一旦「A」を送信後データを読み取るごとに自動的に計測/出力する「Queryモード」、そして設定した周期(1Hz、5Hz、10Hz、20Hz、)で連続的に計測/出力した結果を読み取る「Continuousモード」があります。
今回は「Continuousモード(20Hz)」を使うことにします。この設定をするためには、RAM書き込み用コマンド「G」、書き込み先(レジスタ)の「0x74」、そして設定内容の8ビット「0111010」あるいは16進数の「0x72」を送信します。ちなみに、設定内容については、

0x50:Standbyモード(デフォルト)
0x51:Queryモード
0x52:Continuousモード(10Hz,Periodic Set/Reset=ON)
0x72:Continuousモード(20Hz,Periodic Set/Reset=ON)

となります(詳細はデータシートを参照して下さい)。
シリアルモニターを使って読み取り値を出力することにします。


//I2C通信ライブラリを取り込む
#include <Wire.h>

//デジタルコンパスモジュールのアドレス設定
int compassAddress = 0x42 >> 1; //=0x21
//読み込み値(角度)の変数を用意
int reading = 0;

void setup() {
//I2C通信開始
Wire.begin();
//角度表示のためのシリアル通信開始
//Serial.begin(9600);

//Continuous Modeに設定する
Wire.beginTransmission(compassAddress);
//RAM書き込み用コマンド
Wire.send('G');
//書き込み先指定
Wire.send(0x74);
//モード設定
Wire.send(0x72);
//通信終了
Wire.endTransmission();
//処理時間
delayMicroseconds(70);
}

void loop() {
//デバイスに2バイト分のデータを要求する
Wire.requestFrom(compassAddress, 2);
//要求したデータが2バイト分来たら
if(Wire.available()>1){
//1バイト分のデータの読み込み
reading = Wire.receive();
//読み込んだデータを8ビット左シフトしておく
reading = reading << 8;
//次の1バイト分のデータを読み込み
//一つ目のデータと合成(2バイト)
reading += Wire.receive();
//2バイト分のデータを10で割る
reading /= 10;
Serial.println(reading);
}
//処理のために少し待つ(20Hz)
delay(50);
}


まずは、Wire.begin()でI2C通信を開始します。「HMC6325」の初期設定では、「Standbyモード」になっているため、setup(){...}内で「Continuousモード」に切り替えます。設定内容を「HMC6325」のRAMに書き込むために「G」というコマンド、書き込み先となる専用レジスタの「0x74」、設定内容となる8ビットの「0x72」をWire.send()を使って送信します。
方位角度の読み込みは、Wire.requestFrom()でアドレスと何バイト分のデータかを指定して要求します。計測結果は0〜3599までの値(360度を10倍した値)を2バイト(16ビット)で返してきます。Wire.available()によって2バイト分読み込み可能なデータをカウントしたら、Wire.receive()によって、2バイトの値を2回に分けて読み込みます。一度に読み込むことができる値は1バイト(8ビット、0〜255)までなので、最初の1バイトを読み込んだら、その値を8ビット左にビットシフト(8桁左にビットシフトするということは、256倍することと等しくなります)、その値に次の1バイト分のデータを加算します。値は10倍されているので、最終的に10で割って0〜359度の角度として取り込みます(float型を使えば0.0〜359.9で出力します)。20Hzごとに計測する「Continuousモード」に設定したので、delay(50)を挿入してループの周波数も20Hzにしておきました。

測定結果をProcessingへシリアル通信するのであれば、2回に分けて読み取った値(2バイト分)をそのまま1バイトずつ2回送信(合計2バイト)し、Processing側で受け取ってから二つの値を合成すればいいでしょう。値を合成するには、一個目の値を左に8ビットシフト(または256倍)してから二個目の値を足せば0〜3599の値として得ることができるはずです。BYTEフォーマットなら、そのまま2回で送信します。文字列でデリミタ(区切り記号)を挿入して送信する場合は、2回目の送信時にSerial.println()で改行記号を用いて送信します(Processing側での受信については、「Arduino-Processing シリアル通信3(複数の値を送信する場合)」あるいは「Arduino-Processing シリアル通信5(文字列で送信する場合)」を参照して下さい。

BYTEフォーマットで送信する場合:

void loop() {
Wire.requestFrom(compassAddress, 2);
if(Wire.available()>1){
//2回に分けて読み取った値を
//BYTEフォーマットで2個送信する
Serial.print(Wire.receive(),BYTE);//1個目
Serial.print(Wire.receive(),BYTE);//2個目
}
delay(50);
}

あるいは、文字列として送信する場合:

void loop() {
Wire.requestFrom(compassAddress, 2);
if(Wire.available()>1){
//2回に分けて読み取った値を
//DECフォーマットで2個送信する
Serial.print(Wire.receive(),DEC);
Serial.print(",");//デリミタを挿入送信
Serial.println(Wire.receive(),DEC);//改行記号つき
}
delay(50);
}


また、「C」を送信することでキャリブレーションモードに入り、周囲の磁気の影響によるモジュール内素子のゆがみをなくし正常な状態に調整することが出来ます。モジュールを平らな場所に置いてから6秒から3分以内に、ニ周程度回転させ、「E」を送信することで終了します。20秒以上かけて2回転させれば、正確なキャリブレーションになります。

//キャリブレーション開始コマンド送信
Wire.beginTransmission(compassAddress);
Wire.send('C');
Wire.endTransmission();

//30秒ほど待つ(6秒〜3分まで)
//この間に数回モジュールを回転させる
delay(30000);

//キャリブレーション終了コマンド送信
Wire.beginTransmission(compassAddress);
Wire.send('E');
Wire.endTransmission();


デジタルコンパスをモータなどの磁気を発するものの近くに設置すると磁気的影響を受けるので、少し離れた場所に設置したほうがいいでしょう。

10/23/2008

Arduino-Processing BlueTooth通信+曲げセンサ

以前、ワイヤレス通信としてXbeeモジュールを用いましたが、もう一つワイヤレス通信として「BlueTooth」を用いる方法があります。今回は、SparkfunのBlueToothモジュール「BlueSmiRF Gold」を使います(国内ではストロベリーリナックスで販売)。Xbeeは3.3V電源を使用しますが、「BlueSmiRF Gold」は3.3V〜6.0Vまでの電源を使うことができるので、そのままArduinoにも接続しやすい仕様となっています(ただし、日本において、「BlueSmiRF Gold」は技術基準適合証明を受けていないため、実験や開発用のみで使用しなければならないようです)。Xbeeの場合は最低でも二つのモジュールを用意しなければ通信実験できないのですが、多くのノートパソコンにはBlueToothが搭載されているので、一つのBlueToothモジュールがあれば、とりあえずコンピュータから送受信することができます。


(上画像:「BlueSmiRF Gold」/from Sparkfun


今回は、Arduino基板に曲げセンサと「BlueSmiRF Gold」を接続し、コンピュータのBlueTooth機能を利用して、Processingとワイヤレスでシリアル通信してみます。曲げセンサについては、2本を背中合わせに貼り合わせて、まっすぐな状態から両方向に曲げて出力値が変化するように加工します。


「コンピュータとのBluetooth接続設定」
まず、「BlueSmiRF Gold」とコンピュータのBluetoothの接続設定を行います。
「BlueSmiRF Gold」には以下のように6本の端子があります。

CTS:送信要求端子(非接続またはRTS端子へ)
PWR:電源端子(Arduino 5V端子へ)
GND:グランド端子(Arduino GND端子へ)
TX :送信端子(Arduino RX:0番端子へ)
RX :受信端子(Arduino TX:1番端子へ)
RTS:受信準備完了端子(非接続またはCTS端子へ)

「BlueSmiRF Gold」のPWR端子とGND端子をArduinoの5V端子とGND端子に接続し、USBを通して電源供給します(あるいは、外部電源でもかまいません)。そうすれば、「BlueSmiRF Gold」の赤色のLEDが点滅し始めます。他の端子については、今のところ接続する必要はありません。

MacOSXの場合、
・メニューバー右端のBluetoothアイコンをクリック。
・「Bluetooth:入」を選択し機能をオンにする。
・さらに「Bluetoothデバイスを設定...」を選択する。
・「Bluetooth設定アシスタント」の画面が現れる。
・「続ける」を押していき、「任意のデバイス」を選択。
・デバイスが検索されたら「パスキーオプション」を押す。
・「このデバイスではパスキーを使用しない」を選択し「OK」を押す。
・「続ける」を押していき、最後に「終了」を押して完了。

Windowsの「Bluetooth設定」の場合、
アプリケーションを立ち上げて「新しい接続」を押します。「新しい接続の追加ウィザード」の画面で、「エクスプレスモード」を選び「次へ」を押し、「FireFry-D39A」というようなデバイス名で「BlueSmiRF Gold」を認識します。「次へ」を押すとポートが「COM40」あたりに設定されます。設定されれば以下のような画面になります。

「FireFry-D39A」を選択しダブルクリックすれば「接続」しますが、ここでは「接続」せず「ハイパーターミナル」上で「接続」することにします。

Bluetoothが内蔵されていないコンピュータの場合は、以下のようなBluetooth USBアダプタをコンピュータに接続する必要があります。




「Zterm/ハイパーターミナルの設定」
「BlueSmiRF Gold」の赤色のLED点滅が緑色のLED点灯に変われば通信接続されたことになります。
ただし「BlueSmiRF Gold」のデフォルトの通信速度(ボーレート)が115200bpsであるため、9600bpsに設定し直すことにします。そのためには、「BlueSmiRF Gold」の内部設定をATコマンドによって変更します。ATコマンドを使うには、MacOSXなら「ZTerm」、Windowsなら「ハイパーターミナル」などのターミナルアプリケーションを使って送信すると便利です。
「BlueSmiRF Gold」を電源に接続し、コンピュータのBluetooth接続設定をした後、「ZTerm」(MacOSX)あるいは「ハイパーターミナル」(Windows)を起動します。

MacOSXの場合:
ZTermのDial>Directry...を選択すると以下の画面が現れます。

「Dial Directory」画面上の「New」ボタンを押すと以下のような別画面が現れます。

「Service Name:」の欄に任意名を記入します。「Local Echo」にチェックを入れます。その他は上のような設定でも大丈夫です。「OK」ボタンを押します(「Dial Directory」画面上の中央上の「Connection」ボタンでこの画面に戻ることができます)。
「Settings>Terminal...」あるいは「Dial Directory」画面上の「Terminal」ボタンを押すと以下の画面が現れます。

上のような内容で設定し「OK」ボタンを押す。
次に、「Settings>Modem Preferences...」を選択すると以下の画面が現れます。

ここで、画面上段の「Serial Port:」がBluetoothのシリアルポート(「/dev/tty.FireFly-D39A-SPP-1」など)になっているか確認してください(なっていなければ選択する)。「OK」ボタンを押し、「Dial Directory」画面に戻ります。「Dial」ボタンを押して通信接続します。接続中は、「BlueSmiRF Gold」の緑色のLEDが点灯します。接続されない場合は、「BlueSmiRF Gold」の電源を入れなおすか、「Zterm」を再起動してみてください。

Windowsの場合:
ハイパーターミナルを立ち上げます。

この画面上で、「名前:」に接続名(任意名)を記入し、「OK」を押します(次回接続するときは、「キャンセル」を押し、メニューバーの「ファイル>開く」から保存した設定ファイルを選択してください)。


「接続方法:」の欄で、先ほどの「Bluetooth設定」で割り当てられたポート「COM40」を選択し「OK」を押します。


「ビット/秒:」を「9600」、「フロー制御」を「なし」、「適用」と「OK」を押します。
この設定ウィンドウが閉じると自動的に接続が開始されますが、メニューバーの「通信>切断」で通信を中断し、「ファイル>プロパティ」をクリックし、以下の画面を出します。


「ASCII設定」ボタンを押すと、さらに以下の画面が現れます。


上画像のようにチェックをいれます。「OK」を押して戻ります。
メニューバー「通信>電話」をクリックし、通信を開始します。
通信が開始されれば、「BlueSmiRF Gold」の緑のLEDが点灯します。
後は、ATコマンドを入力していきます。


「ATコマンド入力/通信速度の設定変更」
コンピュータ側のBluetoothと接続され、「BlueSmiRF Gold」の緑色のLEDが点灯すれば、以下のようにATコマンドを入力していきます。

$$$

ドルを三回(最後にリターンキーは押さない)を入力すると「CMD」という返事が表示され、ATコマンドモードに切り替わり、緑のLEDが点灯しつつ赤いLEDが点滅し始めます。
注意しなければいけないことは、「BlueSmiRF Gold」にはタイマー機能(デフォルト:60秒)があり、電源を入れてから60秒以内に「$$$」を入力する必要があります。時間切れになると、赤いLEDの点滅速度が遅くなります。「CMD」が表示されなかったり、赤いLEDが高速に点滅しないときは、一旦通信を切断し、「BlueSmiRF Gold」の電源を切って再接続した後、60秒以内に再度「$$$」を入力してみてください。あるいは、以下のように、このタイマー機能の時間を設定し直すことも可能です。

ST,100

を入力し「リターンキー」を押せば、タイマーを100秒に設定することができます(「ST,」の後に数値を入れることで制限時間を設定できます)。ただし、「ST,」の後に「0」を入力すると「遠隔設定不可」、「255」を入力すると「時間制限なし」になります。

通信速度(ボーレート)を9600に変更するには、

SU,96

を入力し「リターンキー」を押します(「SU,」の後にボーレートの最初の2桁を入力、9600の場合「96」、4800の場合「48」を入力)。「AOK」が表示されれば、変更されたことになります。変更内容を確認するには、

D

を入力し「リターンキー」を押します。そうすると各設定が以下のように表示されます。

***Settings***
BTA=00066600D39A
BTName=FireFly-D39A
Baudrt=9600
Parity=None
Mode =Slav
Authen=0
Encryp=0
PinCod=1234
Bonded=0
Rem=NONE SET

「Baudrt=9600」になっているので、通信速度(ボーレート)は9600に変更されています。ATコマンドモードを終了するには、

---

というように、ハイフンを3回入力し、「リターンキー」を押してください。そうすると「END」という返事がきます。
その他のコマンドについては、
http://www.sparkfun.com/datasheets/RF/RN_BlueportII-ref-guide.pdf
に書いてあります。「BlueSmiRF Gold」の基本設定は以上です。
尚、「ZTerm」やATコマンド入力については、「Arduino+Xbee Shield/Processing+XBee Explorer USB」においても説明があります。


(「ZTerm」のATコマンド入力画面)


「曲げセンサの接続方法」
次は、曲げセンサについてです。通常、曲げセンサはまっすぐな状態の時に抵抗値が低く、曲げると抵抗値が高くなる特性があります。反対方向に曲げてもそれ以上抵抗値は変化しないので、曲げる方向は一方向に限られてしまいます。2本の曲げセンサを背中合わせに貼り合わせて1本として使えば、まっすぐな状態からどちらの方向に曲げても抵抗値が変化するようになります。曲げセンサの通電性のある面を外側にし、両面テープなどで貼り合わせ、それぞれの端子の部分は互いにショートしないようにビニルテープや熱収縮チューブなどで絶縁しておきます。
ひとつの曲げセンサにプルアップ抵抗(10KΩ)を取付け、もうひとつの曲げセンサにはプルダウン抵抗(10KΩ)を取付けます(プルアップ/プルダウン抵抗については「センサについて」を参照)。そうすることで、一方へ曲げると読み取られる電圧は下がり、逆方向へ曲げると電圧が上がるようになります。それぞれ読み取った値をプログラム上で合成しProcessingへ送信します。Arduinoのアナログ入力analogRead()で読み込むと、一方の値が約200~500に変化し、もう一方が約500~800に変化するので、合成すると約700~1300前後で値が変化することになります。まっすぐな状態のときは、約1000前後になります。
ちなみに、「Bi-directional Flexible Bend Sensor」という両方向性の曲げセンサがあります。


「スタンドアロン/外部電源の接続方法」

今回はワイヤレス通信なのでコンピュータにUSB接続せずに、外部電源として乾電池006P(9V)を用いスタンドアロンで操作できるようにします。
抵抗を取り付けた曲げセンサを、以下のようにArduinoのアナログ端子の0番ピンと1番ピンに接続します。
Arduinoへプログラムをアップロードする際には、Bluetoothモジュールを外して行って下さい。同時に、Arduino基板上の「USB-EXT」のジャンパピンも「USB」側に差込んでアップロードしてください。
アップロード後は、Arduino基板とBluetoothモジュールを以下のように接続し、「USB-EXT」のジャンパピンを「EXT」側に差し込んでください。




「Arduinoのプログラム」

void setup(){
//シリアル通信開始
Serial.begin(9600);
}

void loop(){
//変数を用意し、二つの読取り値を合成して代入
int val=analogRead(0)+analogRead(1);

//合図用データが届いたら
if(Serial.available()>0){
//合成した値を文字列(改行コード付き)で送信
Serial.println(val,DEC);

//バッファを空にしておく
Serial.read();
}
}



Arduinoからは、値を文字列として送信します。文字列にすることで、大きな値をそのままProcessingへ送信することができます(文字列でシリアル通信する方法については「Arduino-Processing シリアル通信5」を参照して下さい)。


「Processingのプログラム」
Processingの方は、読み取った値に応じて矩形が左右に動く単純なプログラムにします。Arduinoからは改行コードを含んだ文字列データが届くのでbufferUntil()を用い、改行コードまでをひとまとまりのデータとしてreadStringUntil()で読み込み、不要な改行コードをtrim()で取り除き、int()で整数値化し、最終的にmap()でオフセットや最小値/最大値を設定してX座標に代入します。少しゆとりをみて、合成された読み取り値の最小値を500、最大値を1500、図形のX座標値となる最小値を0、最大値を画面幅の600に設定することにします。map()で得られる値はfloat(小数)になるので、X座標用の変数xもfloat型にしておきます。
プログラムを実行し、マウスクリックでシリアル通信を開始することにします。


//シリアル通信ライブラリを取り込む
import processing.serial.*;
//ポートのインスタンス
Serial port;
//X座標用の変数(小数)
float x;

void setup(){
//横長の画面に設定
size(600,200);
//シリアルポート設定(Bluetoothのポート)
port=new Serial(this,"/dev/tty.FireFly-D39A-SPP-1",9600);

//「10」(ラインフィード:改行コード)が来る度に
//serialEvent()を呼び出す
port.bufferUntil(10);
}

void draw(){
//背景
background(100);
//矩形描画
rect(x,100,20,20);
}

void serialEvent(Serial p){
//文字列の変数stringDataを用意し、
//「10」(ラインフィード:改行コード)が来るまで読み込む
String stringData=port.readStringUntil(10);

//文字列データが空ではないとき
if(stringData!=null){
//文字列データに含まれる改行記号を取り除き整数にする
int val=int(trim(stringData));
//値のオフセット、最小値、最大値を設定しxに代入
x=map(val,500,1500,0,600);
//合図用データ送信
port.write('A');
}
}

//マウスをクリックしたらシリアル通信開始
void mousePressed(){
//通信開始用データ送信
port.write('A');
}

10/04/2008

Processing QRコード/2次元コード

Processingのライブラリには、「QRCode」というものがあります。QRコードとは、以下のような2次元的なマトリクスを利用したコードです。バーコードよりは情報量が多く、様々な場面に用いられています。


このブログのURL情報が含まれたQRコード(上画像:qrcode.png)

Processingの「QRCode」ライブラリを用いることで、QRコードに含まれた情報を解読することができます。逆に、任意の情報のQRコードを生成するには以下のようなサイトで行うことができます。

http://qrcode.kaywa.com
http://qr.quel.jp

QRCode」ライブラリでは、上記サイトなどで直接生成したQRコード以外にも、Webカメラやデジタルカメラで撮影したQRコード(紙上に印刷したQRコード)を認識/解読することができます。尚、このライブラリを使うには「QRCode」ライブラリサイトからライブラリをダウンロード+インストールする必要があります。サイトの説明によれば、以下のようなコードでQRコードを解読することができます。予めQRコードの画像を用意して、スケッチフォルダ内dataフォルダに入れておいて下さい。
以下のサンプルは、上にあるQRコード(qrcode.png)を使って解読するコードです。無事QRコードが解読されれば、link()によって、自動的にこのブログのURLへジャンプするようになっています。


//ライブラリを取り入れる
import pqrcode.*;

//インスタンス名
Decoder decoder;

void setup() {
//オブジェクトの生成
decoder = new Decoder(this);
//イメージのロード
PImage img = loadImage("qrcode.png");
//イメージの解読
decoder.decodeImage(img);
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);
//テキストに書かれているURLへ移動
link(statusMsg, "_new");
}



QRCode」ライブラリのサイトには、Webカメラから撮影したQRコードを読み込んで解読するサンプルがあります。

http://www.shiffman.net/p5/pqrcode_files/Pqrcode_example.zip

このサンプルでは、「スペース」キーでカメラからのQRコードを画像として読み込み解読します。解読されたテキストは画面に文字として表示されます。「f」キーで、既に用意されているテスト用の画像を読み込んで解読します(「http://www.shiffman.net」と表示されるはずです)。「s」キーで、カメラセッティングの画面に切り替わります。
カメラでQRコードを撮影するときに、ピントがずれていると認識できないこともあるので、カメラを調整する必要があるかもしれません。

以下では、デスクトップ上あるいはその他の場所に保存してあるQRコードをファイルチューザーで選び、解読する実験をしてみます。ファイルチューザーについては、前回のブログ「Processing FileChooser/ファイル選択画面の表示」を参照して下さい。
マウスを押したら、ファイルチューザーのダイアログ画面が現れ、任意のQRコードを選択し、解読結果をProcessingのコンソールに表示します。

//JavaのSwingを取り込む
import javax.swing.*;

import pqrcode.*;

//解読用インスタンスを用意
Decoder decoder;

//画像インスタンスを用意
PImage pimage;

//選択ファイル名を用意し
//ファイル名を空にしておく
String getFile = null;

void setup(){
//とりあえず表示画面を400角に設定
size(400,400);
//解読用オブジェクトの生成
decoder = new Decoder(this);
//背景(黒)
background(0);
}

void draw(){
//選択ファイル名が空でないとき
if(getFile != null){
//ファイルを取り込む
fileLoader();
}
}

//マウスを押したら
void mousePressed(){
//選択ファイル取得処理
getFile = getFileName();
}

//ファイルを取り込むファンクション
void fileLoader(){
//選択ファイル名のドット以降の文字列を取得
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
//その文字列を小文字にする
ext.toLowerCase();
//文字列末尾がjpg,png,gif,tgaのいずれかであれば
if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){
//選択ファイル名のイメージを取り込む
pimage = loadImage(getFile);
//背景(黒)
background(0);
//イメージ表示
image(pimage, 0, 0, pimage.width, pimage.height);
//イメージの解読
decoder.decodeImage(pimage);
}
//選択ファイルパスを空に戻す
getFile = null;
}

//ファイル選択画面、選択ファイル名取得の処理
String getFileName(){
//処理タイミングの設定
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//ファイル選択画面表示
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
//「開く」ボタンが押された場合
if (returnVal == JFileChooser.APPROVE_OPTION) {
//選択ファイル取得
File file = fc.getSelectedFile();
//選択ファイルのパス取得
getFile = file.getPath();
}
}
//上記以外の場合
catch (Exception e) {
//エラー出力
e.printStackTrace();
}
}
}
);
//選択ファイルパス取得
return getFile;
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);
}



以下は、授業内で実験した内容です。
数値の情報(100など)を含んだQRコードを生成し、プログラムによって解読された文字列としての数値を整数型の数値に変換します。その数値をArduinoへシリアル通信で送信し、analogWrite()でLEDの輝度やモータの速度あるいはサーボの回転角度などに反映させる内容です。いくつかのQRコードを作成し、ファイルチューザーで選択したQRコードを入れ替わりで送ります。

//シリアル通信ライブラリを取り込む
import processing.serial.*;
//シリアル通信インスタンス
Serial port;

//JavaのSwingを取り込む
import javax.swing.*;
import pqrcode.*;
//解読用インスタンスを用意
Decoder decoder;
//画像インスタンスを用意
PImage pimage;
String getFile = null;

void setup(){
//とりあえず表示画面を400角に設定
size(400,400);
//解読用オブジェクトの生成
decoder = new Decoder(this);
background(0);

//シリアルポートの設定
port = new Serial(this, "/dev/tty.usbserial-A4001Kjl", 9600);
}

void draw(){
//選択ファイル名が空でないとき
if(getFile != null){
//ファイルを取り込む
fileLoader();
}
}

void mousePressed(){
getFile = getFileName();
}

void fileLoader(){
//選択ファイル名のドット以降の文字列を取得
String ext = getFile.substring(getFile.lastIndexOf('.') + 1);
//その文字列を小文字にする
ext.toLowerCase();
//文字列末尾がjpg,png,gif,tgaのいずれかであれば
if(ext.equals("jpg") || ext.equals("png") || ext.equals("gif") || ext.equals("tga")){
//選択ファイル名のイメージを取り込む
pimage = loadImage(getFile);
//背景(黒)
background(0);
//イメージ表示
image(pimage, 0, 0, pimage.width, pimage.height);
//イメージの解読
decoder.decodeImage(pimage);
}
//選択ファイルパスを空に戻す
getFile = null;
}

//ファイル選択画面、選択ファイル名取得の処理
String getFileName(){
//処理タイミングの設定
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//ファイル選択画面表示
JFileChooser fc = new JFileChooser();
int returnVal = fc.showOpenDialog(null);
//「開く」ボタンが押された場合
if (returnVal == JFileChooser.APPROVE_OPTION) {
//選択ファイル取得
File file = fc.getSelectedFile();
//選択ファイルのパス取得
getFile = file.getPath();
}
}
//上記以外の場合
catch (Exception e) {
//エラー出力
e.printStackTrace();
}
}
}
);
//選択ファイルパス取得
return getFile;
}

void decoderEvent(Decoder decoder) {
//解読結果をテキストとして取り出す
String statusMsg = decoder.getDecodedString();
//テキストを画面に出力
println(statusMsg);

//解読した文字列を整数値に変換
int val=int(statusMsg);
//シリアル通信で送信
port.write(val);
}



Arduino側は、11番ピン(PWMピン)にLEDを接続し、輝度が変化する内容であれば以下のようになります。


int val;

void setup(){
//シリアル通信開始
Serial.begin(9600);
}

void loop(){
//受信データがひとつ届いたら
if(Serial.available()>0){
//受信データ読み込み
val=Serial.read();
}
//11番ピンをアナログ出力する
analogWrite(11,val);
}



QRコードで、「100,20,35,180」のように、幾つかの数値をコンマ(デリミタ)で区切り連続した数値の文字列を作成し、解読した結果として送信すれば、複数の数値データをArduinoへ送信することもできます。
この場合、Processingのプログラムの最後の部分にあるvoid decoderEvent(){...}の部分を以下のようにします。

void decoderEvent(Decoder decoder) {
String statusMsg = decoder.getDecodedString();
int[] data=int(split(statusMsg,',');
for(int i=0;i<data.length;i++){
port.write(val);
}
}

解読される文字列は

statusMsg = "100,20,35,180"

なので、
この文字列statusMsgをsplit()に代入し「,」コンマを区切り記号として複数の文字列を含んだ配列に変換します。同時にその文字列群をint()で括ることで、文字列データを整数値データに変換し、

data = {100,20,35,180}

という整数値の配列になります。data.lengthによって配列dataに何個のデータが含まれているか確認し(この場合4個、data[0]からdata[3]まで)、for()文でデータ数の分だけport.write()で繰り返しシリアル通信で送信します(4回分送信)。Arduino側で、順番にこれらの数値を受け取ることで、LEDの輝度調整のプログラムであれば、連続した値をもとに変化する輝度調整が可能になります。

応用的な使い方として、LEDの点灯やサーボの回転角度などの連続する動きのデータをQRコードで幾つか作成しておき、Webカメラを通して解読させる度に異なる動作をさせたり、マトリクスLEDなどに文字や模様として表示させたりできます。

9/14/2008

Processing 緊急モーションセンサー(Mac)

Appleの2005年以降のPowerBook/iBook/MacBook(ノート型)には、緊急モーションセンサーが内蔵されています。緊急モーションセンサーは、ハードディスクを保護するために、あやまってコンピュータを落としたときの衝撃を感知します。
Processingには、この緊急モーションセンサーから値を得る「Sudden Motion Sensorライブラリ」があります。緊急モーションセンサーでは、以前扱った「加速度センサ」のように、コンピュータ本体を傾けたり衝撃を与えたりすることによって変化する値(XYZ軸に対する3つの値)が得られます。



Sudden Motion Sensorライブラリのサイトに従えば、3つの値は以下の方法で読み込むことが可能になります。尚、ライブラリをダウンロード+インストールする必要があります。

//ライブラリを取り込む
import sms.*;

void setup() {
//画面サイズをとりあえず200角
size(200,200);
}

void draw() {
//3つの値を読み込み、配列に代入
int[] vals = Unimotion.getSMSArray();
//3つの値を出力
println(vals[0] + " " + vals[1] + " " + vals[2]);
}

上記プログラムによって出力された値は以下のようになりました(機種や状況によって多少誤差が含まれるかもしれません)。

水平時 x: 0, y: -3, z:56
左90度 x: 52, y: -3, z: 6
右90度 x:-51, y: -3, z: 5
前90度 x: 1, y: 49, z: 6
後90度 x: 1, y:-54, z: 5

xの振り幅:103(-51から52までの±51.5)
yの振り幅:103(-54から49までの±51.5)

になります。それぞれの振り幅からxとyの中点(水平時の値)を新たに求めると、

xの中点:(-51+52)/2=0.5
yの中点:(-54+49)/2=-2.5

になります。これら中点の値をオフセット値として用いることにします。つまり、計測された値からオフセット値を差し引いて角度の計算をすることになります。以前の「Arduino 加速度センサ」のときと同様に、出力値からそれぞれの角度を求める式を用意します。それぞれの角度をradX、radY、オフセット値をoffsetX、offsetY、読み取り値をx、yとすると、

//atan2()で求める場合
radX=atan2((x-offsetX),sqrt(51.5*51.5-(x-offsetX)*(x-offsetX)))
radY=atan2((y-offsetY),sqrt(51.5*51.5-(y-offsetY)*(y-offsetY)))

//またはacos()、asin()で求める場合
radX=asin((x-offsetX)/51.5)
radY=acos((y-offsetY)/51.5)

になります。式中の51.5は加速度1G(重力)の時の値です(機種によっては256くらいのときもあります)。

以上の式を使って、コンピュータ本体を傾けることでProcessing画面上の3Dモデルを動かしてみます。
3Dモデルに関しては、前回の記事「Processing 3Dモデル/OBJ Loader」のものを使うことにします。コンピュータを傾けた方向に、画面内の3Dモデルも同様に傾く内容とします。
スケッチフォルダ内にdataフォルダを作成し、3Dモデルのデータを入れておいて下さい(3Dデータは、ここからダウンロードできます/.objファイルと.mtlファイルの二つが必要です)。


(コンピュータを傾けた時の3Dモデル/前回ブログの3Dモデルを使用)


//ライブラリのインポート
import sms.*;
import saito.objloader.*;

//モデルのオブジェクトを用意
OBJModel model;

void setup() {
//3D画面サイズ設定
size(400,400,P3D);
//モデルのオブジェクトを生成
model=new OBJModel(this);
//3Dデータ読み込み
model.load("macbook.obj");
//ワイヤーフレームなし
noStroke();
}

void draw() {
//3つの値を読み込み、配列に代入
int[] vals = Unimotion.getSMSArray();
//println(vals[0] + " " + vals[1] + " " + vals[2]);

//背景描画
background(50);
//直線光の設定
directionalLight(200, 200, 200, -1, 1, -1);
//環境光の設定
ambientLight(200, 200, 200);

//3Dモデルの位置座標設定
translate(width/2,height*2/3,0);

//オフセット値
float offsetX=-2.5;
float offsetY=0.5;
//角度の計算:atan2()で求める場合
float radX=-atan2(vals[1]-offsetX,sqrt(51.5*51.5-(vals[1]-offsetX)*(vals[1]-offsetX)));
float radY=-atan2(vals[0]-offsetY,sqrt(51.5*51.5-(vals[0]-offsetY)*(vals[0]-offsetY)));
//またはacos()、asin()で求める場合
//float radX=asin((vals[1]-offsetX)/51.5);
//float radY=acos((vals[0]-offsetY)/51.5);

//回転角度
rotateX(radX+PI/2);
rotateY(radY);

//三角形分割で面を生成する
model.drawMode(TRIANGLES);
//スケール(200倍)
scale(200);
//モデル描画
model.draw();
}

画面内の3D座標は、
左:-X
右:+X
上:-Y
下:+Y
後:-Z
前:+Z
という関係になります。
緊急モーションセンサーのX軸は、Processing上のY軸に対応しているので、vals[0]の値を3DモデルのY軸回転角度へ代入し、val[1]の値は3DモデルのX軸回転角度へ代入します。表示上90度X軸に対してずれていたので、rotateX(radX+PI/2)というようにradXにPI/2(90度)足しておきました。
directionalLight()は太陽光のような直線光であり、括弧内の数値については、最初の3つがRGBで光の色を指定、最後の3つが(0,0,0)の原点を基準に光の向きを設定することになります。
ambientLight()は環境光であり、光の向きはなく、空間全体を明るくしたり暗くしたりし(あるいは色を変える)、RGBの3つの数値で指定します。


もう一つのサンプルとして、コンピュータ自体をコントローラとして傾けて、水平面上のボールを転がすプログラムをしてみます。今回は、setup(){...}内で、一度緊急モーションセンサーから値を読み込み、それらをオフセット値として使うことにします。こうすることで、コンピュータを平らな場所においてプログラムを開始したときの状態(オフセット値)を記憶させておくことができます。ellipse()で擬似的な影を地面に落とすことで立体感がでるようにします。


(コンピュータの傾きに合わせてボールが転がる)


//ライブラリのインポート
import sms.*;

//ボールの座標用変数
float xPos,yPos;

//オフセット用変数
float xOffset;
float yOffset;

void setup() {
//3D画面設定
size(400,400,P3D);
//ワイヤーフレームなし
noStroke();
//水平状態の読み込み
int[] vals = Unimotion.getSMSArray();
//読み込み値をオフセット値に設定する
xOffset=vals[0];
yOffset=vals[1];
}

void draw() {
//モーションセンサーからの読み込み
int[] vals = Unimotion.getSMSArray();
//背景色
background(220);
//画面上半分の塗色
fill(50);
//画面上半分の矩形
rect(0,0,width,height/2);

//ボールの速度の計算
float xSpeed=vals[0]-xOffset;
float ySpeed=vals[1]-yOffset;
//ボール移動量の計算
xPos+=-xSpeed;
yPos+=ySpeed;
//ボール位置の設定
translate(width/2+xPos,height*4/5,-200+yPos);

//影の塗色
fill(50);
//影の座標と大きさ
ellipse(-25,30,80,80/5);

//直線光の設定
directionalLight(255, 255, 255, -1, 1, 0);
//ボールの塗色
fill(255,100,50);
//ボール描画
sphere(30);
}

緊急モーションセンサーの読み取り値からオフセット値を差し引いた値を、そのままボールのスピードに反映させています。読み取られたY方向の値は、画面内のZ座標(前後の軸)に対応するので、yPosをtranslate()のZ軸に代入してあります。実際に動かしてみて、向きが逆であったり、座標軸がきちんと対応していない場合は、値にマイナスを掛けたり、代入先を入れ替えたりして調整してみて下さい。
ボールの位置設定のためのtranslate()は、まずX座標をwidth/2で左右中央、Y座標をheight*4/5で画面上下4/5の位置、Z座標を-200奥とした座標を基準とし、その基準の座標に変化する値となるxPosとyPosを追加して、最終的な位置を決定しています。
疑似の影として用いたellipse()は2D用の図形でありX座標とY座標しか設定できませんが、translate()以後に挿入してあるので、translate()のZ軸の値に合わせて、前後に動きつつ大きさも変化します(2D図形は、3D空間上ではZ座標値が0の位置に配置されており、translate()以後に書いた2D図形は、Z軸の値を変化させれば、見た目の大きさや位置も影響を受けて変化します)。同様に、directionalLight()に対しても、2D図形をdirectionalLight()以前に書けば、光の影響を受けませんが、directionalLight()以後に2D図形を書くと、光の影響を受けて図形自体に陰影がつきます。そのため、directionalLight()は、プログラムの冒頭の方に書かずに、ボール描画の直前(2D図形描画以降)に書いておきます。

9/11/2008

Processing 3Dモデル/OBJ Loader

Processingのライブラリには、3Dモデリングソフトで制作した3Dモデルを読み込むOBJ Loaderライブラリがあります。OBJ Loaderでは、拡張子が「.obj」の3Dモデルを扱うことができます。3Dモデリングソフトがあれば、制作した3Dモデルを「.obj」フォーマットで書き出して、3DデータをOBJ Loaderで読み込みます。読み込みに使用する3Dデータは、スケッチフォルダ内に入れておきます。
また、モデリングが面倒であれば、インターネットからフリーの3Dデータを検索しダウンロードして利用する方法もあります。この場合「.obj」フォーマットのデータでなければならないのですが、それ以外のフォーマットであっても、一旦3Dモデリングソフトで読み込んで「.obj」フォーマットにして書き出せば利用可能です。最近はフリーの3Dモデリングソフトも多く存在するので、ダウンロード/インストールしてすぐに使うことができるはずです(ブログページ右側にもフリーのモデリングソフトのリストがあります)。

参考3Dモデリングソフト(フリーウェア):
Blender (Win,Mac)
Maya 2010体験版(30日)(Win,Mac)
Rhino (Mac用ベータ版/要登録)
Metasequoia/LE (Win)
DoGA (Win)
CB Model Pro (Win,Mac)
SketchUp (Win,Mac)
SketchyPhysics (Win):SketchUp物理演算プラグイン
trueSpace7 (Win)
CoCreate (Win/要登録)

今回はインターネット上から無料の3Dモデルのデータをダウンロードし、Processing上に表示してみたいと思います。Turbo Squidという3Dモデルのデータライブラリのサイトから無料の3Dモデルを探し出し、以下の3Dモデルを利用してみたいと思います(データをダウンロードするにはTurbo Squidに登録する必要があります)。

http://www.turbosquid.com/3d-models/max-apple-macbook/391534

このモデルは拡張子が「.3DS」なので3ds Max用のデータです。フリーウェアのBlenderという3Dモデリングソフトで、この「.3DS」フォーマットのデータを読み込み(File>Import>.3ds)、「.obj」フォーマットで書き出して(File>Export.obj)利用してみたいと思います(「.obj」に変換したファイルのリンクはこの記事の最後にあります)。

はじめてのBlender (I・O BOOKS)
山崎 聡
工学社
売り上げランキング: 6131


そのまま読み込むと、各パーツの位置が少しずれていたので、修正して「.obj」フォーマットで書き出すことにしました。「.obj」フォーマットを選択して書き出すと、「~.obj」と「~.mtl」という二つのファイルが出来上がります。「~.obj」はポリゴンの座標についてのデータであり、「~.mtl」は色や材質、テクスチャなどのデータが含まれています。以下のプログラムで、3Dデータを読み込んでみます(「~.obj」ファイルと「~.mtl」ファイルは、スケッチフォルダ内に入れておいてください)。

//ライブラリのインポート
import saito.objloader.*;

//モデルのオブジェクトを用意
OBJModel model;

void setup() {
//3D用の画面設定
size(400,400,P3D);
//モデルのオブジェクトを生成
model=new OBJModel(this);
//objファイルの読み込み
model.load("macbook.obj");
//ワイヤーフレームなし
noStroke();
}

void draw(){
//背景描画
background(50);
//直線光の設定
directionalLight(200, 200, 200, -1, 1, -1);
//環境光の設定
ambientLight(200, 200, 200);

//3Dモデルの位置設定
translate(width/2,height/2,0);
//スケール設定(200倍)
scale(200);

//マウス入力で回転させる
rotateX(PI*mouseY/height);

//三角形分割で面を生成する
model.drawMode(TRIANGLES);
//3Dモデルの描画
model.draw();
}


Blenderでは、細かな出力設定はせずに、そのまま「.3DS」データを読み込み(File>Import>.3ds)、「.obj」フォーマットで書き出す(File>Export>.obj)ことにします(書き出す際には、予め画面上で3DモデルをSelectしておく必要があります)。表示されるスケールが小さすぎたので、scale()で200倍の大きさに変換しています。そのまま表示させると、3Dモデルの色が真っ黒のままだったので、多少の修正が必要になります。



色などの内容を確かめるために「.mtl」ファイルをテキストエディタなどで開くと、以下のような内容が記述されていることが分かります。

# Blender3D MTL File: macbook.blend
# Material Count: 2
newmtl Screen
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.084706 0.084706 0.084706
Ks 0.449020 0.449020 0.449020
Ni 1.000000
d 1.000000
illum 2

newmtl Plastic
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.800000 0.800000 0.800000
Ks 0.449020 0.449020 0.449020
Ni 1.000000
d 1.000000
illum 2

このデータには、ScreenとPlasticという名前の二つの材質についての数値が含まれており、以下のような内訳になります。
# コメント
newmtl 材料名
Ns 輝度
Ka 環境色
Kd 拡散色
Ks 反射色
Ni 光の屈折率
d  アルファ値
illum 0:照明なし、1:反射ハイライトなし、2:Ksの値で反射ハイライトあり

恐らくKaの値がそれぞれ0.000000, 0.000000, 0.000000なので真っ黒な状態になったのでしょう。それぞれの値を以下のように変えます。

ScreenのKaの値を
Ka 0.1 0.01 0.01

PlasticのKaの値を
Ka 0.7 0.7 0.7

上記三つの値はRGB(赤,緑,青)に対応しています。
それぞれの値を変更すると、ScreenのKaは少し赤みがかった黒、PlasticのKaは白に近いグレーになります。
先ほどのプログラムで再度描画させれば、以下のようにかたちが認識できるようになるはずです。


マウス上下で3Dモデルも上下に回転します。
見えない場合はこちらへ


Processingでは、box()sphere()vertex()などの基本的な3D描画のコマンドはありますが、複雑な3Dモデルを制作する際には、モデリングソフトを利用して取り込んだ方がいいでしょう。あるいは、3Dモデルのデータもインターネット上に数多く存在しているので、検索すれば相応しいものが見つかるかもしれません。
尚、上記プログラムで使用したmacbook.objとmacbook.mtlのファイルは、ここからダウンロードできます。


3Dキャラクタアニメーション Blender(DVD付)
トニー・マレン
アスキー
売り上げランキング: 118200

4/07/2009

書籍:Programming Interactivity


Programming Interactivity: Rough Cuts Version
A Designer's Guide to Processing, Arduino, and openFrameworks
http://oreilly.com/catalog/9780596800581/index.html


4/19/2008

授業概要

 建築発明工作ゼミにおいては、プログラミング、エレクトロニクス、メカニクス、素材加工という今日用いられている様々な技術を横断的に習得します。設定した目的やアイデアに沿って制作していきますが、技術習得中における技術的発見から偶発的なアイデアが生成されることも同時に期待します。はじめに予定していたアイデアとは異なるこのような技術的発見が、最初の発明的行為となります。この場合のアイデアとは、技術的アイデア(コンピュータの演算レベルにおけるアイデア/電気的特性におけるアイデア/素材加工法におけるアイデアなど)として、偶発的ながら即座に現象化可能なものとなるはずです。この偶発的かつ技術的アイデアが、次に行われるであろう行為の問題提起や表現の橋渡しとなるでしょう。初期アイデアと偶発的技術的アイデアの組合わせ/試行/実験を繰り返し、一つの場面を建築という社会的環境/生活的空間につくりあげていきます。
 それぞれの技術は専門的であり通常分業化されていますが、ひとつの技術に固執する習得法ではなく、複数の技術を連関させながら同時習得することを目指し、個人の視野や能力を拡張させることも同時に目的とします。
 制作されるものは、ものごとをスタティックに定義づける役目を果たすというよりも、常に変化し続ける時間/状況/人々に対応する流動的なものとなります。そのために個人レベルで様々な技術を用いツール/ディバイス/ガジェットを段階的に制作するところから始めます。そして、それらを応用拡張し、実際の空間や生活につながる状況を導きだします。

「使用機材/道具類」
プログラミング:
 *コンピュータ(Windows/Macintoshどちらでも可)
  各自ノートパソコンを持参することが望ましい。
 *開発環境(プログラミングするためのフリーのソフトウェア/インターネットからダウンロード可)
  Processing (http://processing.org)
  Arduino (http://arduino.cc)
 *参考書籍
  「Built with Processing (改訂版)」(ビー・エヌ・エヌ新社)
  「Making Things Talk(英語版)」(Oreilly & Associates Inc)

電子工作:
 *マイクロコントローラ/Arduino Diecimila基盤
  メカロボショップ (http://www.mecharoboshop.com) ¥4361
  Sparkfun (http://www.sparkfun.com) $34.95
 *半田ごて、テスター、ブレッドボード、ジャンパワイヤ、ラジオペンチなど
  秋月電子 (http://akizukidenshi.com
  千石電商 (http://www.sengoku.co.jp)
 *電子部品/センサー類:(必要に応じて各自購入)
  上記あるいは下記ショップにて購入
  マルツパーツ館 (http://www.marutsu.co.jp)
  ツクモロボット王国 (http://www.rakuten.co.jp/tsukumo/index.html)
  ストロベリーリナックス (http://strawberry-linux.com)
  Sparkfun (http://www.sparkfun.com)
  Parallax (http://microbot-ed.com/j_titlepage.html)

素材加工:木、金属、プラスチックなどの加工/メカニカルな部品製作
 *ノコギリ、金属用ノコギリ、ヤスリ、ペンチ、ドリルなど(日曜大工用具一式)。
 (学内機材:ボール盤、小型旋盤、小型フライス盤、丸ノコ、模型用卓上丸ノコ、ジグソー、ドライバドリル)

毎回の授業では、空間を前提としたテーマに沿って表現を試行します。
その都度、表現に用いられる技術的なトピックについて平行して演習します。

はじめの段階から、上記すべての機材や道具をそろえる必要はありません。
必要なものは、授業内あるいはこのブログにおいて連絡します。

6/02/2008

出力方法について

出力方法には、LEDのように発光表示する視覚的なもの、ものを動かす実際の力やエネルギーをつくりだすアクチュエータ、その他、熱や音を生成するものなどあります。特にアクチュエータの代表となるモータは、ギヤや機構の組合わせによって、様々な動き方を可能にしますが、電気以外に機械に対する知識や技術も必要になってきます。また、Arduino基盤と接続するには、モータドライバICFETトランジスタ、別電源が必要となります。
視覚的な表示方法であれば、Processingのプログラムを液晶プロジェクタで直接投影することも考えられます。
音に関しては、直接鳴らすブザーやスピーカがありますが、Processingの音のライブラリ(minimEssSonia)を用いることもできます。その場合は、既に用意してある音源データを再生したり、プログラム上で波形をつくって音を出力することもできます。
以下に、様々な出力方法のリストを挙げておきます。幾つかは、その他の部品を取付けないと作動しないものや、それに応じたプログラムが必要になるものも含まれていますが、今後授業で取り扱っていきたいと思います。

表示(発光)関係
 一般電球/蛍光灯
 LED(単色、3色)
 7セグLED
 ドットマトリックスLED
 液晶ディスプレイ
 小型有機ELディスプレイ
 冷陰極管
 レーザーモジュール
 EL発光シート

アクチュエータ関係
 DCモータ
 ギヤードモータ
 (平行ギヤ、ベベルギヤ、ウォームギヤ、ラック、遊星ギヤ)
 ステッピングモータ
 サーボモータ
 振動モータ

 電磁石
 ソレノイド

 気体ポンプ
 液体ポンプ
 電磁弁
 エアシリンダー

 バイオメタル人工筋肉
 通電性高分子人工筋肉

熱関係
 フィルムヒーター
 ニクロム線(発熱)
 ペルチェ素子(冷却)

音関係
 圧電スピーカ/ブザー
 コーンスピーカ
 Processingライブラリの利用(minimEssSoniaなど)
 関連:「スピーカ/音出力について

6/05/2008

Processing 手描きの線の表現




Processingにおいて、rect()ellipse()のような幾何学図形ではなく、マウスを使って「手描き」の表現をする場合のサンプルです。マウスボタン(クリック)を押しながらドラッグすると、手描きのような線を引くことができ、「e」のキーを押せば画面は真っ白に戻るという内容です。

以下の画面中央(白い矩形内)に描くことができます。









Processingのプログラム:

void setup(){
//画面サイズ幅400、高さ300
size(400,300);
//滑らかな描画(アンチエイリアス)
smooth();
//背景色を255(白)に設定
background(255);
}

void draw(){
//ここには何も書かなくてよい
}

//マウスドラッグ中のプログラム
void mouseDragged(){
//線を描画
line(pmouseX,pmouseY,mouseX,mouseY);
}

//キーが押された時のプログラム
void keyPressed(){
//もしキーが「e」なら
if(key=='e'){
//背景色を白で塗り直す
background(255);
}
}


上のプログラムでは、mouseDragged()keyPressed()の時にしか描画しないので、draw(){...}内には特に書く項目はありません。線を引くには、line(x1,y1,x2,y2)というように四つの値が入り、開始点(x1,y1)から終点(x2,y2)まで線を引きます。line()内のpmouseXは、一つ前(1ループ前)のマウスのX座標値であり、mouseX()は現在のX座標値です。つまり、一つ前のマウス座標値から現在のマウス座標値まで線を引くということになります。ドラッグ中は、その内容が繰り返され連続した線になります。すべてを白紙に戻すには、キーを押したときに、background()を用いて画面全体を塗り直します。draw(){..}内にbackground()を書いておくと、描画されている線も同時に上塗りされて消えてしまうので、setup(){...}の中で、最初に一度設定しておき、線だけが重ね描きされるようにします。

Arduinoにつないだセンサから読み取った値を、シリアル通信を通してline()内に入れれば、センサによって自由な線を描くことができます。応用的な使い方を考えてみて下さい。

6/06/2008

Processing ドラッグしながら図形を描く


上はこのプログラムをキャプチャした画像です。
下の白いキャンバス内にドラッグして矩形を描くことができます。eキーで白紙に戻ります。







Processingでは、予めプログラム上で図形の座標やサイズを決めてから描画させますが、
一般の描画ソフト(イラストレータなど)のように、ドラッグしながら図形の大きさを決めて描くサンプルを以下に書きます。今回は、描く図形をrect()にし、マウスボタンを押した地点から描画を開始し、ドラッグ中はrect()のサイズが変化し、マウスボタンを放した時に描画完了とします(いわゆる描画ソフトでの一般的な描き方)。重ね描きできるようにするためには、毎回background()で画面を塗りつぶさず、今回はloadPixels()updatePixels()を使うことにします。重ね描きが分かりやすいように、noFill()で図形の塗り無しで、外形線だけの描画とします。「e」のキーを押せば、白紙に戻るようにします。

Processingのプログラム:

//図形描画開始点座標の変数
int x,y;

void setup(){
size(400,300);
//最初に背景色を白にしておく
background(255);
//始点と終点座標による描画に設定
rectMode(CORNERS);
//塗り無し
noFill();
}

void draw(){
//ここには何も書かない
}

//マウスボタンが押されたら
void mousePressed(){
//マウス座標を変数に入れておく
x=mouseX;
y=mouseY;
//画面内容を記憶しておく
loadPixels();
}

//ドラッグしたら
void mouseDragged(){
//画面内容を更新する
updatePixels();
//x,yを開始点、マウス座標を終点として描画
rect(x,y,mouseX,mouseY);
}

//キーが押されたら
void keyPressed(){
//「e」キーなら
if(key=='e'){
//白で塗りつぶす
background(255);
}
}

初期設定のsetup()では、rectMode(CORNERS)にして、開始点と終点の二点間の座標による定義にしておきます。マウスボタンを押した時に、マウス座標値mouseXmouseYrect()の描画の開始点としてxとyの変数に記憶させ、その時の画面内容をloadPixels()を使って記憶しておきます(ドラッグ中の処理のための下準備)。そしてドラッグ中には、loadPixels()の時の画面内容をupdatePixels()によって表示させつつ、xとyを開始点としてrect()を描き始め、終点はドラッグ中のmouseXとmouseYの座標を使って常に変化するようにします。loadPixels()によって、画面内容を記憶させておかなかったり、updatePixels()によってその画面内容を表示させなければ、ドラッグ中のrect()の線は重なり合うように描画されます(試しに、updatePixels()を消してランさせればどうなるか分かります/下画像)。


updatePixels()を使わない場合の画面。
このようにドラッグ中の変化する線も消されずに描画されます。敢えてこの方法を使うということも考えられます。

4/20/2008

Processingの基本操作 その1

Processingのアプリケーションを開くと以下のような画面が現れます。


画面上部メニューバーのHelp>Referenceを選ぶと、プログラミングで使用される言語や関数がカテゴリーごとに列挙されているページが現れます(ProcessingのサイトにもReferenceのページがあります)。すべての言語を覚えるのは大変なので、必要に応じて辞書のように参照しながら、プログラムを書いていきます。それぞれの言語(関数)をクリックすれば、使用法のサンプルページが出てきます。
例えば、Shapeのカテゴリーの2D Primitivesの中のrect()という関数は、長方形を描くためのものです。

rect(10,30,40,20);

と書けば、X座標が10で、Y座標が30の場所に、幅40で高さ20(単位:ピクセル)の長方形を描くということになります。
カッコの中の4つの数値はそれぞれ順番にX座標、Y座標、幅、高さに対応しています。
四つの数値の間のカンマや最後のセミコロンを忘れないように書いてください。
大文字、小文字(半角英字)も間違わないように書いてください。
ちなみに、画面上の(0,0)座標は左上の角となります。左から右へX座標はプラスになっていきますが、注意しなければいけないことは、Y座標に関しては、上から下へ向けてプラスになるので、いわゆる数学で扱われる座標の向きとは異なります。
とりあえず、rect(10,30,40,20);をプログラムを書く欄に記入して、左側にあるRunボタンもしくはメニューバーのSketch>Runをクリックすれば、プログラムが開始され、その結果が別画面で現れます。その画面を閉じれば、そのプログラムは終了します。

以下のようにもう一行追加すれば、

size(200,300);
rect(10,30,40,20);

表示画面の大きさを幅200、高さ300ピクセルと指定して先ほどの長方形を描くことができます。
size()のカッコの中の二つの数値は画面の幅と高さに対応しています。もし、画面サイズを指定せず何も書かなければ、自動的に初期設定のサイズ(100x100ピクセル)が適用されることになります。

さらに、もう一行追加して、

size(200,300);
background(100);
rect(10,30,40,20);

と書けば、表示画面の背景色を指定して描画されます。
background()のカッコの中には、0から255までの数値が入ります。0は黒、255は白、その間の数値は段階ごとのグレーになります。background(255,100,30);のように3つの数値をカンマで区切って入れれば、それぞれの数値はRed,Green,Blueに対応しRGB色を指定することができます。例えば、赤ならbackground(255,0,0);で黄色ならbackground(255,255,0);です。
このような細かな設定については、冒頭で述べた「画面上部メニューバーのHelp>Reference」のページへ行き、それぞれの言語の使用法から調べることができます。

5/12/2008

Processing マウス入力1

前回までは、ループ処理を使って図形を動かしていました。ただ、このままでは一方的に動画を表示していることに過ぎないので、録画されたコンテンツを再生していることと、何ら違いがありません。今回は、ユーザ側からのマウス入力のプログラムを加えることで、一方的に表示されている内容を、相互作用的な内容へと変化させます。
例として、前回の入れ子で動く正方形のプログラム(「Processingの描画サンプル」)にマウス入力に反応するプログラムを書き足します。ここでは、mousePressed()というマウスのボタン(左クリック)が押された時に発動するプログラムを付け足してみます。
そのためには、

void mousePressed(){
//ここにボタンが押されたときに発動するプログラムを書く
}

というプログラムを前回のプログラムの最後に付け足します。

例えば、クリックしたら二つの正方形の位置がそれぞれ0に戻るというプログラムであれば、

int a=10;
int b=a*12;
int c=b*3;
int ax; //小さい正方形(黒)のX座標の変数
int bx; //大きい正方形(赤)のX座標の変数

void setup(){
size(360,120);
noStroke();
}

void draw(){
background(0);
fill(250,20,20);
rect(bx,height/2-b/2,b,b);
bx=bx+1;
if(bx>c){
bx=-b;
}
fill(0);
rect(ax+bx,height/2-a/2,a,a);
ax++;
if(ax>b){
ax=-a;
}
}

void mousePressed(){
ax=0;
bx=0;
}

となります(axは前回用いたプログラムの小さな黒い正方形のx座標のための変数、bxは大きな赤い正方形のx座標のための変数)。draw()の中の上から4行目の「bx=bx+1;」を「bx+=1;」あるいは「bx++;」と書くこともできます。同様にさらに6行下の「ax++;」は「ax=ax+1;」あるいは「ax+=1;」と同じことです。以前の「Processingの基本操作 その3」でも触れましたが、「++」はインクリメントと呼ばれ、変数に+1を自己代入して+1ずつ増加させていくことです。
mousePressed()のほかにmouseReleased()mouseDragged()mouseMoved()があります。
mousePressed()は、マウスボタンを押した瞬間に発動します。
mouseRelease()は、マウスボタンを押して放される瞬間に発動します。
mouseDragged()は、クリックし、そのままドラッグしている間発動し続けます。
mouseMoved()は、マウスが動いている間(クリックしなくても)発動し続けます。
それぞれの挙動の違いを各自確かめてみてください。

10/12/2008

Processing FileChooser2

以前「Processing FileChooser/ファイル選択画面の表示」でJava Swingを用いましたが、Processing 146以降からselectInput()によって、ファイル選択画面を通して任意の場所にあるファイルを読み込むことが簡単にできるようになりました。
以下は、「f」キーを押すとselectInput()で、ファイル選択画面を表示し、コンピュータの任意の場所にある画像を表示するサンプルです。表示された画像は、ドラッグすることで位置を変えられるようにしてあります。

//画像用インスタンス用意
PImage img;

//現在選択中のファイルパスの変数
String currentPath=null;
//画像配置座標の変数
int x,y;
//画像配置座標とクリック座標の差分の変数
int dx,dy;

void setup(){
  //画面サイズ設定
  size(600,400);
}

void draw(){
  //背景描画(黒)
  background(0);
  //現在選択中のファイルパスが空ではないとき
  if(currentPath!=null){
    //画像描画
    image(img,x,y);
  }
}

//クリックしたら
void mousePressed(){
  //画像配置座標とクリック座標の差分を求めておく
  dx=x-mouseX;
  dy=y-mouseY;
}

//ドラッグ中
void mouseDragged(){
  //マウス座標に差分座標を加えた値を画像配置座標とする
  x=mouseX+dx;
  y=mouseY+dy;
}

//キーを押した場合
void keyPressed(){
  //「f」キーなら
  if(key=='f'){
    //ファイル選択画面を表示し選択したファイルパス取得
    String loadPath = selectInput();
    //ファイルパスが空の場合
    if (loadPath == null) {
      //「ファイルが選ばれてない」メッセージを出力
      println("No file was selected...");
      //ファイルパスを前回のファイルパスにする
      loadPath=currentPath;
    } 
    else {//ファイルパスが選択された場合
      //ファイルパスのドット以降の文字列を取得(拡張子名を取得)
      String ext = loadPath.substring(loadPath.indexOf('.') + 1);
      //拡張子が「jpg」または「png」なら
      if(ext.equals("jpg") || ext.equals("png")){
        //選択ファイルパスの画像を取り込み
        img = loadImage(loadPath);
        //現在選択中のファイルパスを更新
        currentPath=loadPath;
        //現在選択中のファイルパスを出力
        println(currentPath);
      }else{//拡張子が「jpg」または「png」ではないとき
        //「画像ファイルではない」と出力
        println("Not image file.");
      }      
    }
  }
}


ファイル選択画面上で選択したファイルのパスを取得したら、substring()によってパスの文字列末尾に含まれる拡張子を調べます。その際、indexOf()を使うことで、パスの文字列に含まれる「.」を手掛かりに、パスの文字列末尾の拡張子を抜き出します(indexOf()は、括弧内に入れた文字が文字列中の何番目にあるかを教えてくれます)。
今回は、拡張子が「jpg」か「png」であれば、loadImage()で画像を取り込み、次回のためにファイルパスを記憶させておきます。それ以外の拡張子の場合は、"Not image file."だけを出力します。

selectInput()以外に、フォルダを選択するためのselectFolder()や、保存先を指定するselectOutput()も加えられています。

----------------------------------
以下は頂いたコメントに対するサンプルです。
import java.awt.*;

void setup() {
  size(200,200);
}

void draw(){  
}

void fileSelected(File selection) {
  if (selection == null) {
    println("Window was closed or the user hit cancel.");
  } else {
    println("User selected " + selection.getAbsolutePath());
  }
}

void mousePressed(){
  FileDialog fd = new FileDialog(new Frame(),"Choose a file");
  fd.setDirectory("/Users/username/Desktop");//ここのディレクトリを任意に変えてください。
  fd.setVisible(true);
  if(fd.getFile()==null){
    println("Canceled");
  }else{
    println("FILE NAME:"+fd.getFile());
    println("DIRECTORY:"+fd.getDirectory());
  }
}

5/13/2008

Processing マウス入力3

クリックやドラッグ以外に、マウス(カーソル)を図形の上に重ねた時に、色や形が変わるロールオーバーという機能があります。Processingでは、ロールオーバーの関数は用意されていないので、自前でプログラムする必要があります。原理的には、マウス(カーソル)の座標mouseXmouseYが、その図形上にあるかどうかをif文を用いて判定するプログラムになります。
例として、画面上の座標(200,100)に配置された一辺50pixelの正方形(赤)にマウス(カーソル)が重なった ら黄色に変化し、マウスボタンを押したら緑に、放したらもとの赤に戻るというプログラムを書きます。

void setup(){
size(400,200);//画面サイズ
fill(255,0,0);//正方形の初期色を赤に設定
}
void draw(){
background(100);
rect(200,100,50,50);

//マウスが正方形内にある場合
if(mouseX>=200 && mouseX<=250 && mouseY>=100 && mouseY<=150){
if(mousePressed){//クリックした場合:緑
fill(0,255,0);
}else{//それ以外(クリックしない場合):黄
fill(255,255,0);
}
}else{ //それ以外(マウスが正方形外にある場合):赤
fill(255,0,0);
}
}

マウスの座標であるmouseXmouseYに対して、if文によって正方形内にマウスがあるかないかを判定しています。X座標に関しては正方形の左端(200)から右端(250)の座標、Y座標に関しては上端(100)から下端(150)の座標の範囲にマウスがある場合ということを、複数の条件としてif文に設定しています。&&は、複数の条件「Aという条件かつBという条件」という意味です。マウスの座標が200以上かつ250以下なので、200<=mouseX<=250と書きたいところですが、mouseX>=200 && mouseX<=250というように分けて書かないとエラーがでます。ちなみに「Aという条件またはBという条件」の場合は、||を用います。

このプログラムでは条件式が少し複雑ですが、まずマウスが正方形内にあるかないかという条件を設定します。もし正方形外ならそのままfill(255,0,0)で赤、もし正方形内であれば、ロールオーバーかクリックかという二つの条件に分かれて、mousePressedされれば、fill(0,255,0)で緑、そうでなければ、ロールオーバーのままfill(255,255,0)で黄色になるという仕組みです。
if(mousePressed){};というようにmousePressedは、if文の()内に入れて使うことはできますが、mouseReleasedはmouseReleased()として使うのであって、if文の()内に直接入れることはできません。

4/21/2008

次回の授業は5月10日です。

4/26は休講です。5/3は祝日ですので、次回の授業は5/10となります。
次回から、「One inch / One foot / One yard」というテーマに沿って「Wooden Stick」の制作を開始します。
「Wooden Stick」は、大きさや長さをあらわす基準ツール(独自の定規のようなもの)であり、今回用いられる様々な技術を試行するためのモチーフ/ベースにもなります。詳細については次回の授業で説明しますので、木の棒を各自持参して下さい。

Processingを用いたプログラミングのエクササイズは、次回も行います。Processingのサイト内学習サンプルや、このブログ内の説明を参考にプログラミングによる描画を試してみて下さい。「One inch / One foot / One yard」がテーマですので、
「1 inchという長さを描画する」
「1 footという長さを描画する」
「1 yardという長さを描画する」
というタイトルで幾つかのサンプルをプログラミングしてみて下さい。
インチ、フット(フィート)、ヤードという単位は、どのように人間の身体や行動と関係しているのかということを考慮しながら、表現の工夫を試みて下さい。

ノートパソコンを持っている人は、できるだけご持参ください。

5/03/2008

Processingの描画サンプル

以下は、「One inch /One foot / One yard」のテーマをもとにProcessingで作成したプログラムの例です。



このプログラムでは、画面の横幅を1yard、大きな正方形(赤)を1footの正方形、小さな正方形(黒)を1inchの正方形とみなしています。入れ子になった正方形が左から右へ動くというサンプルです。
「One inch /One foot / One yard」は、単位やスケールに関わるテーマです。このようなシンプルな表現で構わないので、次回の授業までに作成してきてください。
ちなみに、このプログラムでは、noStroke()という関数を使っています。noStroke()は、rect()などの「図形の外形線を描かない」というコマンドです。それ以外は、このブログで説明してある方法を用いてプログラムできると思います。

以下がプログラムソースです。

int a=10;
int b=a*12;
int c=b*3;
int ax;
int bx;

void setup(){
size(360,120);
noStroke();
}
void draw(){
background(0);
fill(250,20,20);
rect(bx,height/2-b/2,b,b);
bx=bx+1;
if(bx>c){
bx=-b;
}
fill(0);
rect(ax+bx,height/2-a/2,a,a);
ax++;
if(ax>b){
ax=-a;
}
}

9/23/2008

次回(9/27)授業について

9/27の授業では、以下のものを持参して下さい。

・パソコン
・Webカメラ(パソコン内蔵カメラでも可)
・Arduino基盤
・LED

Webカメラをパソコンに接続し、Processingのプログラムによって身体計測の実験を行います。
Webカメラの使い方については、「Processing Video(Webカメラ)」などを参考にして下さい。

尚、上記機材等を持参できない場合は、学校の備品を使うことになります(ただし、数が限られています)。


[目次:Processing関係]  [HOMEへ戻る]  [目次:Arduino関係]