openFrameworks x Arduino #001

openFrameworks x Arduino #001 from Takepepe on Vimeo.

そろそろこのアバターの格好で記事投稿するのが辛くなり始める今日この頃。
皆様いかがお過ごしでしょうか。

今回はopenFrameworksをArduinoでコントロールするネタです。(Arduinoはほぼオマケですが…)
動画ではひらひら揺らめく面と、再生している音楽が同期しています。
赤外線センサーに手を近づけて、面の揺らめきと音楽のスピード・音量をコントロールします。

ArduinoはINとOUTを備えたマイコンボードの統合開発環境です。
一般的なPCやガジェットには無いセンサーをコンテンツに取り込むだけでなく、
LED、モーターや家電のコントロールも出来る素敵インターフェースです。

Arduino

Arduino http://www.arduino.cc/

Arduinoはボードにプログラムを書き込み、電源を供給すればスタンドアロンでも動かす事ができます。
Processing・openFrameworksでも簡単に相互通信できるAPIが用意されています。
とても面白いプラットフォームなのでいろんな記事を調べてみてください。

いつもの様に制作過程の流れで説明していきます。

  1. Arduinoボードの設定
  2. openFrameworksとArduinoをつなぐ
  3. ofxSimpleGuiTooを追加
  4. 3D空間にofCircleを描く
  5. PerlinNoiseでゆらゆら
  6. 音楽を再生
  7. Arduinoの値を摘要

1.Arduinoボードの設定

今回のインスタレーションでは赤外線測距モジュール(GP2Y0A21)を使っています。
参考というか、出版されている書籍に載っているものをそのままですw
「5章 レシピ2:距離を測りたい」

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ
http://www.oreilly.co.jp/books/9784873114538/

まずは下記回路図の様にArduinoを組み立てます。

Arduino

赤外線センサーから値が取れていることを確認します。
以下のスケッチでは距離がcm単位でSerialに出力されます。

OA001.ino



// アナログピン0番
const int sensorPin = 0;

// 閾値
const int threshold = 80;

void setup(){

	// 接続速度の設定
	Serial.begin(9600);

}

void loop(){

	int value = analogRead(sensorPin);
	if(value > threshold){
		// cm単位に変換
		float range = (6787 / (value -3)) -4;
		Serial.println(range+"cm");
	}

}

2.openFrameworksとArduinoをつなぐ

openFrameworksで値を取得する行程に入りますが、Arduinoでは先程のスケッチは使用しません。
ArduinoボードにはStandardFirmataを書き込んでおきます。
StandardFirmataは、色んなプラットフォームとArduinoを繋ぐものです。
StandardFirmataのインストール方法とArduinoへの書き込み方法については割愛します。

openFrameworksのコードに移ります。

OA001.h

class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		ofArduino ard;
		float ardValue;
		float distance;
		void setupArduino(const int & version);
		void analogPinChanged(const int & pinNum);

}

OA001.cpp

// Arduinoとの接続速度
#define SPEED 57600

//--------------------------------------------------------------
void OA001::setup(){

	// Arduinoに接続
	ard.connect("/dev/cu.usbmodemfd321", SPEED);
	ofAddListener(ard.EInitialized, this, &OA001::setupArduino);

}

//--------------------------------------------------------------
void OA001::setupArduino(const int & version) {
    
	// イベントリスナ削除
	ofRemoveListener(ard.EInitialized, this, &OA001::setupArduino);
	
	// アナログピン0番からのレポートを取得
	ard.sendAnalogPinReporting(0, ARD_ANALOG);
	ofAddListener(ard.EAnalogPinChanged, this, &OA001::analogPinChanged);
	
	//cout << ard.getFirmwareName() << endl;
	//cout << "firmata v" << ard.getMajorFirmwareVersion() << "." << ard.getMinorFirmwareVersion() << endl;

}

//--------------------------------------------------------------
void OA001::analogPinChanged(const int & pinNum) {

	int value = ard.getAnalog(pinNum);
	cout << value << endl;

}

//--------------------------------------------------------------
void OA001::update(){

	// Arduinoを更新
	ard.update();

}

コンソールにセンサー値が出力されたら接続成功です。
このセンサー値は近くに何も無い場合、150以下で推移、最も手を近づけた時600強の値が出ます。
純粋なセンサー値とは別に、この回で使用しているローパスフィルターをかけながら
distanceに変換後の値を入れていきます。

OA001.cpp

void OA001::analogPinChanged(const int & pinNum) {

	// そのままのセンサー値を格納
	ardValue = ard.getAnalog(pinNum);

	// センサー値を調整して格納
	if(ardValue < 100){
		distance = 100;
	}else if(ardValue > 700){
		distance = 700;
	}else{
		// ローパスフィルターを摘要
		distance = distance*0.9+ardValue*0.1;
	}

}

ここまででArduinoについて分からない場合は田所先生の講義資料を見て勉強してみてください。
本当に素晴らしい講義資料ばかりを公開していただいています。感謝!

yoppa.org 「第9回:openFrameworksとArduinoを連携する」
http://yoppa.org/ma2_11/3383.html

3.ofxSimpleGuiTooを追加

開発を進めるにあたり、アドオンのofxSimpleGuiTooを使います。
とても便利で、作品を作るにあたり開発効率が良くなるので早期に取り入れます。
先程取得したセンサー値も、簡単に視覚化することが出来ます。
筆者はmemoさんのバージョンを使っています。

ofxSimpleGuiToo
https://github.com/memo/ofxSimpleGuiToo

OA001.h

class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		ofxSimpleGuiToo gui;
		void setupGUI();

}

OA001.cpp


void OA001::setup(){

	// ofxSimpleGuiTooをセットアップ
	setupGUI();

}

//--------------------------------------------------------------
void OA001::setupGUI() {

	gui.setup();
    
	// GUIの見た目を設定
	gui.config->textColor = 0xFFFFFF;
	gui.config->buttonHeight = 20;
	gui.config->sliderHeight = 10;
	gui.config->titleHeight = 20;
	gui.config->fullActiveColor = 0x00aec3;
    
	// Arduinoのセンサー値
	gui.addTitle("ARDUINO");
	gui.addSlider("ardValue", ardValue, 0.0f, 700.0f);
	gui.addSlider("distance", distance, 0.0f, 700.0f);
	
	// 前回終了した時の設定で起動
	gui.loadFromXML();
    
	// 最初から表示させる
	gui.show();
    
}

//--------------------------------------------------------------
void OA001::draw(){

	// ofxSimpleGuiTooを描画
	gui.draw();

}

4.3D空間にofCircleを描く

まずは縦横100個のofCircleを画面のセンターに表示させます。
ofRotateYとかに適当に値を入れると、3D空間にofCircleが描かれているのが確認出来ます。

OA001.h

class OA001 : public ofBaseApp{

	public:
		void setup();
		void draw();

	private :
		float rotateX;
		float rotateY;
		float rotateZ;

		int count;
		float margin;
		float radius;
		int color;
		int alpha;

}

OA001.cpp

void OA001::setup(){

	// 画面全体の回転値を初期化
	rotateX = 0.0f;
	rotateY = 0.0f;
	rotateZ = 0.0f;

	// ofCircleで使用する値を初期化
	count = 100;
	margin = 25.0f;
	radius = 5.0f;
	color = 255;
	alpha = 255;

}

//--------------------------------------------------------------
void OA001::draw(){

	// この段階のMatrixを保持
	ofPushMatrix();

	// 画面センターに移動
	ofTranslate(ofGetWidth()/2, ofGetHeight()/2);
    
	// 画面全体の回転値を摘要
	ofRotateX(rotateX);
	ofRotateY(rotateY);
	ofRotateZ(rotateZ);
    
	// ofCircleの描画
	int totalLen = (count-1)*margin;
	ofTranslate(totalLen/-2, totalLen/-2);
	ofSetColor(color, color, color, alpha);
	for(int i=0; i<count; i++){
		for(int j=0; j<count; j++){
			ofCircle(i*margin, j*margin, 0, radius);
		}
	}
    
	// 保持したMatrixに戻す
	ofPopMatrix();

}

5.PerlinNoiseでゆらゆら

ofNoiseはパターン化されたランダム値を取得する事が出来ます。
ofNoiseからPerlinNoiseを取得するために以下記事を参考にしました。

Noise – 【oF】openFrameworks
https://sites.google.com/site/ofauckland/examples/noise

このPerlinNoiseで得たグレースケール値を、ofCircleの奥行きに摘要していきます。
先程と同じ箇所を改変します。

OA001.cpp

void OA001::draw(){

	// この段階のMatrixを保持
	ofPushMatrix();
    
	// 画面センターに移動
	ofTranslate(ofGetWidth()/2, ofGetHeight()/2);
    
	// 画面全体の回転値を摘要
	ofRotateX(rotateX);
	ofRotateY(rotateY);
	ofRotateZ(rotateZ);
    
	// ofCircleの描画
	int totalLen = (count-1)*margin;
	ofTranslate(totalLen/-2, totalLen/-2);
	ofSetColor(color.r*255, color.g*255, color.b*255, alpha);
	for(int i=0; i<count; i++){
		for(int j=0; j<count; j++){
			float a = i * xForce;
			float b = j * yForce;
			float c = ofGetFrameNum() / 60.0;
			float noise = ofNoise(a,b,c) * zForce;
			ofCircle(i*margin, j*margin, noise, radius);
		}
	}
    
	// 保持したMatrixに戻す
	ofPopMatrix();

}

ここまでの様子は以下の様になります。
ofxSimpleGuiTooで各パラメーターを操作して遊んでみます。
画面収録している分フレームレートが落ちていますが、実際は60fpsで動きます。

openFrameworks x Arduino #001( Process ) from Takepepe on Vimeo.

6.音楽を再生

ofSoundPlayerで音楽を取り込みます。
openFrameworksで音楽をコントロールする、恐らく最も簡単な方法です。
ofSoundPlayerのボリュームをPerlinNoiseに係数として与えることで、
音に併せて揺らめきが変わることが確認出来ます。

OA001.h

class OA001 : public ofBaseApp{

	public:
		void setup();

	private :
		ofSoundPlayer mySound;
		float speed;
		float volume;
		void setupMusic();

}

OA001.cpp

void OA001::setupMusic() {
    
	// 音楽を読み込んでループ再生
	mySound.loadSound("sound.wav");
	mySound.setLoop(true);
	mySound.play();
    
}

//--------------------------------------------------------------
void OA001::update(){

	// ofSoundPlayerを設定
	mySound.setSpeed(speed);
	mySound.setVolume(volume);

}

7.Arduinoの値を摘要

冒頭で取得したArduinoの値をofMapで音楽の音量・スピードに摘要すれば完成です!

OA001.cpp

void OA001::update(){
    
	// Arduinoのセンサー値をofSoundPlayerの設定値に変換
	speed = ofMap(distance, 150, 600, 0.1f, 3.0f);
	volume = ofMap(distance, 150, 600, 0.1f, 1.0f);
    
	// ofSoundPlayerを設定
	mySound.setSpeed(speed);
	mySound.setVolume(volume);
    

}