INDEX(各項目ごとの目次)

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

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

6/26/2008

Arduino ビデオ信号/テレビ画面に出力



大抵のテレビにはビデオ入力端子がついており、その端子に映像信号を送り込めば画面に映像を映すことができます。今回は、Arduinoによってパルスを生成し、そのパルスを映像信号としてテレビに送り込む簡単な実験をします。パルスについては、モータサーボなどでも用いたように、Arduinoのデジタル出力端子からHIGH(5V)とLOW(0V)をある一定の周期で交互に出力する方法です。今回用いる映像信号は、NTSC方式というテレビの規格(日本やアメリカの規格、ヨーロッパはPAL方式)にあわせた周期になります。

テレビの画面は、水平な一本の線(走査線)が525本縦に並んで構成されています。編み物でいう横糸だけが525本あるという感じです。さらに一本の線を細かく見ると、点が左端から始まり、右端へ流れていきます。右端まで行った点は、次の列(下方の列)の左端に移動します。この動きを525回繰り返し画面右下まで移動することで、ようやく一枚の画面ができあがります。一本の線における画面左端から右端までの移動時間は63.5マイクロ秒になります。それが525回繰り返されるので、一枚の面ができあがるまでは63.5×525=33337.5マイクロ秒(0.0333375秒)つまり一秒間に約30回画面が切り替わっていることになります(30フレーム/秒)。実際はインターレース方式といって、525本のうち奇数番目の線を最初に描画し、偶数番目をその後描画する仕組みとなっています。それに対しプログレッシグ方式というのがあり、それは上から順に一本ずつ描画していきます。パソコンなどのモニターはプログレッシブ方式になっています。

Arduinoでプログラムするには、上記のような規格に合わせてパルス出力のタイミングを設定する必要があります。パルスのタイミングがずれると、信号を送っているのにもかかわらず乱れた画面となってしまい、ほとんど認識できない映像になってしまいます。今回は、水平同期信号のパルスを送ることで縦縞の模様をテレビ画面に映してみようと思います。色は黒、グレー、白の3色とします。それぞれパルスの電圧は以下のようになります(抵抗をつなげて、5V出力から以下のような電圧になるように調整します)。

水平同期信号:0.0V
黒     :0.3V
グレー   :0.6V
白     :1.0V

まず、一本の走査線の周期である63.5マイクロ秒ごとに、水平同期信号を送ります。これが目印となり、毎回一定の長さの線をつくりだします。
水平同期をとるために、以下の信号を送ります。

黒     :1.5マイクロ秒(フロントポーチ)
水平同期信号:4.7マイクロ秒
黒     :4.7マイクロ秒(バックポーチ)

合計が10.9マイクロ秒となり、この部分を「水平ブランキング期間」といいます。その後、合計が一本の線の周期である63.5マイクロ秒になるように、黒、グレー、白の信号を適宜加えていきます。
例えば、

黒     :1.5マイクロ秒(フロントポーチ)
水平同期信号:4.7マイクロ秒
黒     :4.7マイクロ秒(バックポーチ)
白     :20マイクロ秒 :画面表示
黒     :20マイクロ秒 :画面表示
グレー   :12.6マイクロ秒:画面表示

となります。最後の白、黒、グレーの三色が画面に縦縞となって現れます。それぞれの継続時間は画面上の縦縞の幅に対応しています。水平同期信号の前後にある黒(フロントポーチ、バックポーチ)は、画面右端と左端の余白部分になります。合計が63.5マイクロ秒になっているのであれば、より細かく区切って色を配置していくこともできます。



このような手順でArduinoのプログラムをしていきます。ただ、信号の精度が低いと同期がとれなくなったり映像が乱れたりするので、普段のArduinoのプログラム方法ではやや難があります。通常Arduinoのプログラムは、一旦C言語に翻訳され、アセンブラを通してマイクロコントローラ(AVR:ATMEGA168)に書き込まれます。C言語を用いた方が翻訳の手間がかからず、より精度高くプログラムできるので、今回はC言語でプログラムする要素を少し取り入れます。
電子部品については以下が各一個ずつ必要となります。

RCAビデオ入力プラグ
抵抗:1KΩ
抵抗:330Ω



テレビのビデオ入力端子(黄色:映像用)にRCAビデオ入力プラグを差し込みます。音声は使わないので赤と白の端子には、今回は何もつなぎません。RCAビデオ入力プラグの差し込み部分は、中心の棒状の部分がプラスで、周囲の円筒状の部分がマイナスです。Arduinoの8番、9番ピンからの線は、プラス部分につながれています。マイナス部分は、ArduinoのGND端子と共有します。

Arduinoのプログラム:
(高性能なテレビだと映らないかもしれません。映らない場合は、もうひとつのサンプルがさらに下にあります。)



//ディレイのライブラリを取り込む
#include <util/delay.h>
//同期信号、三色の出力を設定、定義しておく
#define SYNC (PORTB=B00000000)
#define WHITE (PORTB=B00000011)
#define BLACK (PORTB=B00000001)
#define GRAY (PORTB=B00000010)

void setup(){
//8番と9番ピンをデジタル出力に設定
//DDRB=B00000011;と書くことも可能
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
//割り込み禁止設定
noInterrupts();
}
void loop(){
//同期、前後余白の設定
BLACK;
_delay_us(2);
SYNC;
_delay_us(5);
BLACK;
_delay_us(5);

//画面表示
WHITE;
_delay_us(17);
BLACK;
_delay_us(17);
GRAY;
_delay_us(17);
}



今回は、より正確な同期タイミングを必要とするために、部分的にC言語で直接Arduino基盤上のマイクロコントローラをプログラムするコマンドを使用しています。パルスをつくるために以前はdelayMicroseconds()を用いていましたが、精度が充分ではないので、#include <util/delay.h>でディレイのライブラリを取り込んで(このライブラリはArduinoに既に含まれているので、ダウンロードする必要はありません)、「_delay_us()」という、より正確に実行するディレイを使います。#defineを使って、水平同期信号、白、黒、グレーのデジタル出力も予め定義しておきます。「PORTB」は、Arduino基盤の8〜13番ピンまでのことを指します(ポートBというひとまとまりのピン)。「B00000000」は、二進数の0(ゼロ)のことですが、「PORTB」に対してすべてのピンを0にする(LOWで出力する)ということになります。「PORTB=B00000001」は、「PORTB」の1番目のピン(Arduino基板上の8番ピン)を1にする(HIGHで出力する)ということになります。「B00000010」ならば、その隣の2番目のピン(Arduino基板上の9番ピン)をHIGHで出力するとなり、「B00000011」ならば、「PORTB」の1番目と2番目のピン(Arduino基板上の8番、9番ピン)をHIGHにするということになります。


今回の回路では抵抗を用いているので、

8番ピンLOWかつ9番ピンLOW:0.0V(水平同期信号)
8番ピンHIGHかつ9番ピンLOW:0.3V(黒)
8番ピンLOWかつ9番ピンHIGH:0.6V(グレー)
8番ピンHIGHかつ9番ピンHIGH:1.0V(白)

という出力の組合わせになります。
void setup(){...}内の、pinMode()設定を、上記のように二進数を用いて表せば、「DDRB=B00000011」となります。これは、ポートBの1番目と2番目のピン(8番、9番ピン)をデジタル出力に設定するということと同じになります。「DDRx」のxの部分にポートのアルファベットを入れれば、そのポートの設定を二進数で表すことができます。各ポートは、以下のようになっています。

ポートB(PORTB):8から13番ピン(デジタル出力ピン)
ポートC(PORTC):0から6番ピン(アナログ入力ピン)
ポートD(PORTD):0から7番ピン(デジタル出力ピン)

noInterrupts()」は、割り込みのプログラムを禁止します。割り込みプログラムによってタイミングが乱されたりせず、より正確な時間を維持することができます。
void loop(){...}内では、#defineで定義しておいたそれぞれの内容を呼び出して、同期や画面表示を行っています。基本的には、1ループの合計時間が一本の走査線の63.5マイクロ秒になるように各色の時間を配分しなければなりません。多少ずれても映るのですが、同期がずれてしまったり、各走査線ごとに長さが違うと、全く見えなくなるときがあります。
厳密には、最初の黒が1.5マイクロ秒必要ですが、小数点以下の精度がでないかもしれないので、敢えて整数にしています(その他の時間についても同様です)。水平同期、画面左右余白の信号が最初にあります。それぞれ、2マイクロ秒、5マイクロ秒、5マイクロ秒あり、合計で12マイクロ秒必要になります。今回は走査線一本の長さを63マイクロ秒に設定したので、63マイクロ秒から最初の12マイクロ秒を差し引き、残り51マイクロ秒の部分に画面表示させる内容をプログラムします。上のプログラムでは、残り51マイクロ秒の部分に、白、黒、グレーをそれぞれ17マイクロ秒ずつ配分しました。

試しにプログラムをランさせて、テレビの画面が乱れていれば、多少数値を足したり引いたりして調整して下さい。プログラム上のそれぞれのコマンドで多少の時間のずれが生じることがあります。必ずしも計算通りに63.5マイクロ秒になるとは限らないので、画面の状態を見ながら調整する必要があるかもしれません。「Fablic Square」と関連して、電子的な織物という意味で今回の実験内容をとらえてみて下さい。
このサンプルでは水平同期だけを用いましたが、この他に垂直同期の方法も用いれば、縦縞だけでなく横方向の操作も可能になります。

参考サイト:
http://www.nahitech.com/nahitafu/mame/mame6/sync.html
http://javiervalcarce.es/wiki/TV_Video_Signal_Generator_with_Arduino
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1187659197


もうひとつのサンプル(矩形の描画):

水平同期信号だけでは、表示できないテレビモニターもあったので、垂直同期信号も加えたサンプルを以下に書いておきます。垂直同期信号は、一画面分の走査線数(全走査線525本中の半分262本)ごとに必要になります。以下のサンプルでは、262本中の、前半の3〜5本目の走査線に垂直同期信号を入れています。


#include <util/delay.h>
#define SYNC (PORTB=B00000000)
#define WHITE (PORTB=B00000011)
#define BLACK (PORTB=B00000001)
#define GRAY (PORTB=B00000010)

//水平同期信号
void hsync(){
SYNC;
_delay_us(5);
BLACK;
_delay_us(7);
}

//垂直同期信号
void vsync(){
SYNC;
_delay_us(25);
BLACK;
_delay_us(5);
}

void setup(){
//8,9番ピンをデジタル出力に設定
//DDRB=B00000011; //でも可
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);

//割り込み禁止
//noInterrupts(); //でも可
cli();
}

int count=1;//走査線を数える変数

void loop(){
if(count>=3 && count<=5){
vsync();//垂直同期信号2回送信
vsync();
}else if(count>=5&&count<100){
hsync(); //水平同期信号
BLACK; //矩形の上部背景描画
_delay_us(48);
}else if(count>200){
hsync(); //水平同期信号
BLACK; //矩形の下部背景描画
_delay_us(48);
}else{
hsync(); //水平同期信号
BLACK; //矩形の左側背景描画
_delay_us(20);
WHITE; //矩形の描画
_delay_us(8);
BLACK; //矩形の右側背景描画
_delay_us(20);
}
count++; //走査線の数をカウントアップ
if(count>262){//走査線が画面下端へ行ったら
count=1; //一番目の走査線に戻る
}
}



水平同期信号以外にも、垂直同期信号を加えることで、縦縞だけではなく矩形のようなかたちを表示することができます(525本の走査線の半分である262本を用いています)。走査線1本分の時間は、60マイクロ秒に設定してあります(1ループ:60マイクロ秒)。countという変数を用意して、1本ずつ表示される走査線の数を数えていきます(1ループで1本のため、プログラムの最後の部分のcount++でカウントアップしています)。
変数countを用いて、何番目の走査線がどのような色になるかをif文で条件分岐し:

矩形の上部背景部分(走査線:5〜100番目)を黒で描画
矩形の左側背景部分(走査線:100〜200番目の前半の20マイクロ秒)を黒で描画
矩形の本体部分(走査線:100〜200番目の中間の8マイクロ秒)を白で描画
矩形の右側背景部分(走査線:100〜200番目の後半の20マイクロ秒)を黒で描画
矩形の下部背景部分(走査線:200番目以降)を黒で描画

というように図形と背景をブロック分けして描画しています。つまり、矩形の高さは走査線の本数で定義し、横幅は一本の走査線の中のパルス長によって定義されています。
262番目の走査線を描画したら(262ループしたら)ようやく1画面全部が描画されるので、再び1本目に戻り次の画面を描画し直します。


6/22/2008

素材実験/小型・薄型の材料



電子部品のなかには、「チップLED」や「チップ抵抗」などのように、1mm以下の極小さい部品もあります(Arduino基板上にも載っています)。これらを用いることで、小さく薄いものもつくることが可能になります。バッテリーにおいても、腕時計などに用いられている小型のボタン電池もありますが、「ぺーパーバッテリー」というものもあります(厚さ0.6mm)。LEDを点灯させるには、2.5V程度必要になるので、1.5Vの「ぺーパーバッテリー」であれば2つ直列つなぎする必要があります。「導電性テープ」などを用いて、接点同士をつなげるといいでしょう。「導電性テープ」は接着面も導電性があるので、重ねて貼ることができます。「チップLED」などは、導電性テープ上に直接ハンダづけしてもいいのですが、熱で損傷しやすいので、イージスペンなどを接着剤のようにつかって固定するのがいいでしょう(ただ取れやすいので、きちんと固定したい場合は、ハンダづけするほうがいいかもしれません)。

素材実験/鉛筆の描画線



導電性素材実験として、紙の上に鉛筆で描いた線を可変抵抗器として使う実験をします。
まず、普通のコピー紙などにHBの鉛筆でやや濃いめに太さ5mm、長さ100mm前後の太い線を描きます。テスターを用いて、その線の両端での抵抗値を求めます。今回は結果として、約100KΩの抵抗値を確認できました。上図のように、ミノムシクリップで両端をはさみ、鉛筆で描いた線と同程度の抵抗(100KΩ)をマイナス側に直列つなぎし、Arduinoの5V端子とGND端子に接続します。もうひとつのミノムシクリップ(緑)で鉛筆の線の途中部分をはさみ、Arduinoのアナログ入力へ接続します。これで、一応鉛筆で描いた可変抵抗器ができあがります。抵抗値の変化をanalogRead()で読み取り(今回は読み取り値を4で割る)、analogWrite()でLEDに出力します。緑のミノムシクリップの位置を変えることで、LEDの明るさが変化します。

Arduinoのプログラム:

void setup(){
//特になし
}
void loop(){
//アナログ入力:0番ピンを読み取り4で割る
int val=analogRead(0)/4;
//アナログ出力:3番ピン
analogWrite(3,val);
}


鉛筆自体の濃さ、線の濃さ、太さ、面積などで抵抗値は変わります。接触不良になりがちなので、多少濃いめに描くといいでしょう。その他の素材/材料などでも同様の実験は可能です。最初にテスターで抵抗値や導電性を確かめてから実験を行ってください。テスターで抵抗値を計測するには、ダイヤルなどで機能を切替する箇所を「Ω」のマークがついている部分に合わせてください。


MonotaRO デジタルカードテスター

6/18/2008

導電性素材/材料について



Fabric Square」では、電子工作関係のショップで手に入るコンポーネント化されたセンサやアクチュエータの組合わせの実験/制作だけではなく、物質を通して電気が流れるという現象を素材/材料レベルにおいても実験していきたいと思います。そのため、電子部品以外の身の回りにある素材や、電子工作関係以外にある特殊な素材についても今後フォーカスしていきます。

次回の授業(6/21)では、導電性素材の実験を行いたいと思います。
各自導電性素材/材料をいくつか持参して来て下さい。
テスターがあると便利です。今後も実験や制作で使用するので購入することをおすすめします。

一般的には、鉄や銅などの固形で硬質な金属に電気を流しますが、このような素材/材料以外のもので、柔軟性があったり、固形ではなく流動的であったり、形が変化するもの、普段通電するためのものとして使っていないけれども導電性のあるものも利用していきます。以下にいくつかサンプルを挙げておきます。

電子工作材料:
 導電性テープ(銅箔/アルミ箔テープ)
 導電性接着剤
 導電性エポキシ接着剤
 イージスペン(ペン型簡易回路形成用導電ペースト)
 フレキシブルストレッチセンサ(伸縮ゴム抵抗)
 バイオメタル人工筋肉(電気で伸縮するワイヤー)

その他素材/材料:
 導電性糸
 導電性布地
 導電性ゴム
 金属箔(金箔など)
 金網
 磁石
 カーボン(ファイバー/粉末など)

日常にあるもの:
 アルミホイル
 銀紙(タバコや菓子類の箱の中紙など)
 鉛筆(芯部分/紙上に描いた線や面)
 食塩水
 果物(レモン果汁など)
 鎖(あるいは自転車のチェーンなど)
 炭(備長炭など)
 石鹸(適度な柔らかさがあり、LEDのピンなどをさしやすい)
 人間の体(危険なので高電圧/高電流では実験しないで下さい)


*尚、上記以外にも、通電可能な素材/材料あるいは通電させるための工夫を見つけたら、随時このページに掲載していきます。家庭用電源AC100V(交流)での実験は危険なので、乾電池やテスターなどで実験してください。


カスタム デジタルテスタ

6/16/2008

Arduino 加速度センサ

今回は秋月電子で購入した「KXM52-1050」という3軸加速度センサモジュールを使い、重力方向に対する傾斜角を読み取ります。このセンサでは、XYZ軸の3軸ありますが、XとY軸だけでも三次元的な傾斜角を計測することができます。一応、センサのXYZの3つの出力端子をArduinoのアナログ入力端子にそれぞれ接続することにしますが、実際使うのはXとYの出力値とします。データシートをみながらセンサの端子を以下のように接続します。

1:5V(Arduino5V端子と共有)
2:5V(Arduino5V端子と共有)
3:GND(ArduinoGND端子と共有)
4:無接続
5:GND(ArduinoGND端子と共有)
6:X軸(Arduinoアナログ入力0番ピン)
7:Y軸(Arduinoアナログ入力1番ピン)
8:Z軸(Arduinoアナログ入力2番ピン)



加速度センサを水平なところにおけば、X軸とY軸は重力方向に対して直角なので0Gとなります。5V電源の場合、0Gは2.5Vとして出力されるとデータシートには書いてあります。ArduinoのanalogRead()の1024段階(10ビット)であれば511になるはずですが、さまざまな条件で多少の誤差を含みます。実際に使用する前に、念のためArduinoの「Serial Monitor」で加速度センサの出力値をモニタリングしてみます(Arduinoのモニタリング方法については「Arduino 圧電スピーカ」を参照」。

Arduino (Serial Monitor)のプログラム:

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

void loop(){
//3つの値をアナログ入力で読み込む
int x=analogRead(0);
int y=analogRead(1);
int z=analogRead(2);

//Xの値を出力(十進数)
Serial.print(x,DEC);
//値と値の間に区切りを入れる
Serial.print(",");
//Yの値を出力
Serial.print(y,DEC);
//値と値の間に区切りを入れる
Serial.print(",");
//Zの値を出力し改行する
Serial.println(z,DEC);
delay(100);
}
}

3つの値を一行で出力する際に、Arduinoの出力画面上で読みやすいようにそれぞれの値の間に「","」の区切りの記号(コンマ)をいれます。この区切り記号は何でもいいのですが、これがないとそれぞれの数値同士が隣り合わせになって読みにくくなります(また、3つの数値をそれぞれ改行して出力すると、どれがX軸の値でどれがY軸の値なのか分かりにくくなるので、3つ出力してから改行しています)。
固定した角度で計測しても数値が安定しないので、100個の値をサンプリングして平均値を求めたいと思います。平均値のプログラムを付け加えます。100個分の値の合計となると、数字も大きくなるので「int」型の整数ではなく、より大きい値が扱える「long」型の整数を変数として使います。


//加算用の変数
long x_sum, ysum, z_sum;
//回数の変数
int count=0;

void setup(){
Serial.begin(9600);
}

void loop(){
int x=analogRead(0);
int y=analogRead(1);
int z=analogRead(2);

//それぞれに値を足していく(合計数)
x_sum+=x;
y_sum+=y;
z_sum+=z;

//回数を+1する(カウントアップ)
count++;

//100回カウントしたら
if(count>99){
//合計数を100で割って平均値を出す
Serial.print(x_sum/100,DEC);
Serial.print(",");
Serial.print(y_sum/100,DEC);
Serial.print(",");
Serial.println(z_sum/100,DEC);
//カウントを0に戻す
count=0;
//合計数を0に戻す
x_sum=0;
y_sum=0;
z_sum=0;
}
}

まずは、X軸について計測することにします。水平状態(0G)に対して定規などを用いて−90度傾けて−1Gの値、90度傾けて+1Gの値を上記プログラムを用いて計測することにします。
プログラム上では、xの値をx_sumに足していき、変数countで何回足したかを数えておきます(1ループで一回足されます)。countが100になったら、100回分の合計数であるx_sumを100で割り、その値を出力します(yについても同様に計測します)。
0Gの値については、水平に置いて計測してもいいのですが、今回は−1Gの時の値と+1Gの時の値の中点を用いることにします。よって、以下のような計測結果になります。

  角度:   重力:X軸平均値:Y軸平均値
-90度:  -1G:  316:  271
中点0度:   0G:  536:  491
+90度:  +1G:  756:  711 

これらの値は、今回使用した加速度センサと計測状況において求められた値なので、各自で似たような方法で計測してください。

それでは、この計測結果をもとに、Processingにセンサからの出力値をシリアル通信し、Processing上の3D立体を動かしてみたいと思います。センサを傾ければ、同様に3D立体も同じ角度で傾くようにします。シリアル通信は、1024段階の値を文字列で送ることにします(「Arduino-Processing シリアル通信5」を参照)。このプログラムでは、X軸とY軸だけを読み取ることにします。

Arduinoのプログラム:

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

void loop(){
//2つの値をアナログ入力で読み込む
int x=analogRead(0);
int y=analogRead(1);

if(Serial.available()>0){
//Xの値を出力
Serial.print(x,DEC);
//値と値の間に区切りを入れる
Serial.print(",");
//Yの値を改行して出力
Serial.println(y,DEC);
//合図用データを読み込みバッファを空にする
Serial.read();
}
}

センサから読み取ったXとY軸の値をそのままArduinoから送信します。
Processingでは、受け取った値を角度に変換する計算が必要になります。まず0Gを基準にして、水平時の値が0になるようにオフセット値(X軸の場合:536、Y軸の場合:491)を設けて差し引いておきます。そうすれば、

  角度:   重力:   X軸:   Y軸
-90度:  -1G: -220: -220
  中点:   0G:    0:    0
+90度:  +1G: +220: +220

となります。振り幅は0Gを基準にプラスマイナス220となります。
次に角度の計算ですが単位はラジアンを用います。−90度から+90度までの範囲なので、ラジアンでいうと−PI/2から+PI/2になります(PIは円周率のπです)。X軸の値が110であれば、振り幅である220(1G)の半分なので0.5Gになります。角度については90度の半分なので45度になりそうですが、実際は30度になります。−45度の場合は、以下の図のように約−156になります。



この計算方法は以下のようにして求められます。

acos()、asin()を用いる場合:
まず、Arduinoから送られて来たX軸の値をx、オフセット値をx_offset(今回のオフセット値は536)、オフセット調整した値をx0とすると、

x0=x-x_offset;

になり、角度をradX(ラジアン)とすると

sin(radX)=x0/220;

という関係になります。例えば、x0=110を代入すればsin(radX)=1/2なので、radXは30度となります。
Processingにはasin()acos()の関数があるので、それを利用すると

radX=asin(sin(radX));

という関係になり、sin(radX)にx0/220を代入し

radX=asin(x0/220);

となることで角度radXが求まります。
Y軸についてはacos()で求めると、

radY=acos(y0/220);

になります。

atan2()を用いる場合:
また、この関係をタンジェントで表せば、

tan(radX)=x0/sqrt(220*220-x0*x0)

となります。sqrt()は平方根(ルート)を求める関数です。
角度を求めるには、atan2()という関数を用いて、

radX=atan2(x0,sqrt(220*220-x0*x0));

とします。そうすると角度radXが求められます。

加速度センサのX軸プラス方向をProcessingの3D空間のX軸マイナス方向に対応させるために-radXに変換します。加速度センサのY軸方向を3D空間のZ軸方向に対応させて、

rotateX(-radX)
rotateZ(radY)

となります。
もし、加速度センサの回転方向と、3D立体の回転方向が逆になってしまうときは、値にマイナスを掛けます。また、90度ずれているときはPI/2を足します。実際にセンサを動かして、同じように3D立体が動くか確かめて下さい。

Processingのプログラム:

import processing.serial.*;
Serial port;

//読み取り値の変数
int x,y;

//X軸-1G時316、+1G時756であることから
//X軸のオフセット値
int x_offset=536;
//X軸の振り幅(-1G〜0G又は0G〜+1G)
int x_range=220;

//Y軸-1G時271、+1G時711であることから
//Y軸のオフセット値
int y_offset=496;
//Y軸の振り幅(-1G〜0G又は0G〜+1G)
int y_range=220;

//角度(ラジアン)の変数
float radX,radY;

void setup(){
//3D画面サイズ400×400
size(400,400,P3D);
//シリアルポート設定
port = new Serial(this,"/dev/tty.usbserial-A50019vD",9600);
//念のためバッファを空にする
port.clear();
//「10」(ラインフィード)が来る度にserialEvent()作動
port.bufferUntil(10);
//図形塗り面なし(ワイヤフレーム描画)
noFill();
}

void draw(){
//背景色を白
background(255);

//3D立体の座標を画面中央、-100奥に配置
translate(width/2,height/2,-100);

//オフセット調整(最小値-220、最大値220)
int x0=constrain(x-x_offset,-220,220);
int y0=constrain(y-y_offset,-220,220);

//角度の計算(ラジアン)
radX=asin(x0/x_range);//asin()で求める
radY=acos(y0/y_range);//acos()で求める
//radX=atan2( x0,sqrt(x_range*x_range-x0*x0) );//atan2()で求める場合
//radY=atan2( y0,sqrt(y_range*y_range-y0*y0) );

//センサX軸の角度は3D立体のX軸の角度に対応
//センサY軸の角度は3D立体のZ軸の角度に対応
//角度をそれぞれ代入
rotateX(-radX);
rotateZ(radY);

//直方体を描画
box(200,30,100);
}

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

//データが空でないとき
if(stringData!=null){
//改行記号を取り除く
stringData=trim(stringData);
//コンマで区切ってデータを分解、整数化
int data[]=int(split(stringData,','));

//データ数が2個のとき
if(data.length==2){
//データの値を代入
x=data[0];
y=data[1];
//合図用データ送信
port.write(65);
}
}
}

//マウスボタンを押して通信開始
void mousePressed(){
//合図用データ送信
port.write(65);
}

void draw(){...}内の「オフセット調整」箇所の

int x0=constrain(x-x_offset,-220,220)

は、constrain()を用いて、読み取った値xからオフセット値であるx_offsetを差引き、最小値−220から最大値220までの値になるように制限しています。

ノイズのせいか、動きがぎこちない場合はフィルターのプログラムを挿入し滑らかにします。そのためには、radX、radYと同様にプログラムの冒頭でフィルター用の変数:
float filterX,filterY;

を用意しておき、void draw(){...}内の最後の角度を求める箇所を以下のように変更してください。
radX=asin(x0/x_range);//変更なし
radY=acos(y0/y_range);//変更なし

//フィルターの式
filterX+=(radX-filterX)*0.3;//新たに挿入
filterY+=(radY-filterY)*0.3;//新たに挿入

rotateX(-filterX);//変更
rotateZ(filterY); //変更

フィルターの式の「0.3」は係数であり、1.0に近づくほどフィルターの効果はなくなります。逆に0.1のように係数の値を小さくすれば、滑らかになりつつ反応が鈍く動くようになります。適度に調整してみてください。


尚、もっと簡単に加速度センサを扱いたい場合は(あまり正確な角度にこだわらないのであれば)、
//オフセット調整(最小値-220、最大値220)
int x0=constrain(x-x_offset,-220,220);
int y0=constrain(y-y_offset,-220,220);
//角度の計算(ラジアン)
radX=asin(x0/x_range);
radY=acos(y0/y_range);

の部分を、
radX=2.0*x*PI/1023;
radY=2.0*y*PI/1023;

に置き換えてもセンサを傾けた方向に3D立体が傾きます。この計算では、読み取った直接の値に比例して角度も変わります(比率が多少ずれてしまいます)。この場合は、モニタリングで調べた最小値/最大値/オフセット値などの設定もする必要はありません。式の中の「2.0」というのは係数であり、大きくすれば傾きも大きくなるので画面で確認しながら調整してください。

−90度や+90度付近では、出力値の変化が微妙になるので、きちんとした角度が出ない場合があります。出力値補正のためにZ軸の出力も利用すれば、計算は少し複雑になるかもしれませんが、±90付近まで計測可能になります。


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