バイオメディア・アート
ArduinoとProcessingの連携2:大きな値を送信する、データの流れを視覚化する
ArduinoとProcessingの連携2:大きな値を送信する、データの流れを視覚化する
今日の内容
先週の課題の講評会
センサーからの入力を視覚化する – ArduinoとProcessingの連携のつづき
- 大きな値を送受信する – 大きな値 → 0〜1023 (1024段階 = 210 = 10bit)
- 先週のように、0〜255にマッピングするのではなく、全ての値が反映されるように送受信する方法を学ぶ – ビット演算の使用
- データの流れを視覚化する
- ノイズの影響などを除去しながらデータが変化する様子を視覚化する
先週の課題の講評会
先週の課題:
- Generative Gestaltungのコードを活用して、Arduinoにとりつけたセンサーや可変抵抗、スイッチなどを操作すると形態や動きが変化するプログラムをProcessingで作成する。
- 使用するセンサーは自由に選択してください。温度センサー、光センサー、可変抵抗以外のものでもOK
- 複数のセンサー、可変抵抗を組み合せても構いません
大きな値を送受信する
先週の授業でのArduino → Processingのデータの送受信
- Arduino – 0 〜 1023 (1024段階、210)
- Serialで一度に送出できる範囲 – 0 〜 255 (256段階、28)
- Arduinoの値をSerialにあわせるため、map() 関数で値を補正 – map(val, 0, 1023, 0, 255);
- 値の解像度が1/4になってしまっていた – できることなら全ての値を送りたい
今日用いる手法:値を分割して送信する
- 値を8bitずつで分解、2回にわけて値を送出する
- bit演算を用いて、分解、再合成を行う
ビット(bit)とは
- ビット (bit) は、デジタルコンピュータが扱うデータの最小単位 – “binary digit” の略
- 2通りの状態しか表現できない – “0” または “1”
例:3bit – 3桁のbit
- 3bitで表現できる数 → 0 〜 7 の8通り
- bitで表現できる数は、2の乗数で計算できる
- 3bit = 2^3 = 8
8bit = 1byte
- 00000000 から 11111111
- 10進数の数値にすると、0 から 255
- Serial通信で一度に送れる数値は、1byte つまり 0 (00000000) 〜 255 (11111111)
Arduino – Processingの通信の流れ
- 1024 = 16bit → ArduinoのAnalog inの精度
- 255 = 8bit → Serial で一度に送出できる値
- Arduinoの入力値16bitを、8bit のかたまり2つに分解する
- 2回にわけて8bit( = 1byte) ずつSerialで送信
例:入力の値が、950 (0000001110110110)だったら
16bitの列から上位の8bitをとりだすには? – bit シフトを行う – 各桁を左または右に移動する
例
- 右方向に1bitシフト
- 0000001110110110 >> 1 = 0000000111011011
- 右に1桁移動
16bitの列から上位の8bitを取り出すには
- 右方向に8bitシフト
- 0000001110110110 >> 8 = 0000000000000011
- 右に8桁移動 → 上位の8桁をとりだした状態
- 10進数で表現すると
- 950 >> 8 = 3
16bitの列から下位の8bitをとりだすには?
- bit マスクを行う – bit単位のAND計算
例:下位8bitだけをとりだす
まとめ – 16bitの数値の、上位 8bit と下位 8bit を分解するには
- 上位 8bit:8桁 bitシフトを行う
- 下位 8bit:下位 8bit をマスクする
例:入力が 950 (0000001110110110) の場合
- 上位8bit:950 >> 8 = 3 (00000011)
- 下位8bit:950 & 255 = 182 (110110110)
それぞれを、Serialで送信してあげればよい
以上の手順を、ArduinoとProcessing側双方で実装してみる
Arduino側
//アナログ入力の数を定義する #define NUM 1 //アナログ入力の値を格納する配列 int val[NUM]; void setup() { //シリアル通信の開始 Serial.begin(9600); } void loop() { //アナログ入力の数だけ繰り返し for(int i=0; i0){ //アナログ入力の数だけ繰り返し for(int i=0; i > 8, BYTE); //下位 8bit を送出 Serial.print(val[i] & 255, BYTE); } //合図用データを読み込んでバッファ領域を空にする Serial.read(); } }
Processing側
import processing.serial.*; //アナログ入力の数を指定 int NUM = 6; //Serialクラスのインスタンス Serial myPort; //Serialより読み込んだデータを格納する配列 int[] val = new int[NUM]; void setup() { //画面を生成 size(400, 400); //ポートの名前を取得 String portName = Serial.list()[1]; //Serialクラスをインスタンス化 myPort = new Serial(this, portName, 9600); } void draw() { //背景を黒に background(0); //塗りを白に fill(255); //入力の数だけ繰り返し for(int i=0; iNUM * 2 - 1) { //入力の数だけ繰り返し for(int i=0; i データの流れを視覚化する
データの流れを視覚化する1
- 現在は、Serialから送られてきた最新の値を利用して、視覚的な効果に応用しているだけ
- データの流れ、時間的な変化を感じられるような視覚化の方法を考える
- 参考 - 2010.06.14 : Processingによるデータの可視化 – 時系列のデータを可視化する
データからノイズを除去する
- センサーからの入力値には、意図しない信号(ノイズ)がのってしまう場合がある
- Processing側の工夫で、急激な入力信号の変化は除去したい
- 実際の値の変化を、指定した値でeasingする → なめらかな変化となる
Arduino側
//アナログ入力の数を定義する #define NUM 1 //アナログ入力の値を格納する配列 int val[NUM]; void setup() { //シリアル通信の開始 Serial.begin(9600); } void loop() { //アナログ入力の数だけ繰り返し for(int i=0; i0) { //アナログ入力の数だけ繰り返し for(int i=0; i > 8, BYTE); //下位 8bit を送出 Serial.print(val[i] & 255, BYTE); } //合図用データを読み込んでバッファ領域を空にする Serial.read(); } } Processing側
import processing.serial.*; //アナログ入力の数を指定 int NUM = 1; //現在走査しているX座標 int x; //なめらかさ float easing = 0.05; //なめらかに変換した値 float easedVal; //現在Serial信号を受信しているか boolean running = false; //Serialクラスのインスタンス Serial myPort; //Serialより読み込んだデータを格納する配列 int[] val = new int[NUM]; void setup() { //画面を生成 size(1024, 480); //フレームレート frameRate(30); //ポートの名前を取得 String portName = Serial.list()[1]; //Serialクラスをインスタンス化 myPort = new Serial(this, portName, 9600); //背景を黒に background(255); } void draw() { //もし現在信号を受信していたら if(running) { //入力値から目標とする値をマッピング float targetVal = map(val[0], 0, 1024, 0, height/2); //目標値をなめらかに変換 easedVal += (targetVal - easedVal) * easing; //グラフの描画 //白い線で前の値を消す stroke(255); line(x, 0, x, height); //色を指定 stroke(63,127,255); //現在走査している場所を線で示す line(x+1, 0, x+1, height); //なめらかにしていない値を描画 line(x, height/2, x, targetVal); //なめらかにした値を描画 line(x, height, x, easedVal + height/2); //X座標の更新 x++; //もし画面の端まできたら、最初から if(x > width) { x = 0; } } else { //まだ信号を受信していなかったら //画面をクリックするようにメッセージを出す background(255); fill(0); text("click on screen", 10, 20); } } //シリアル入力を検出した際に発生するイベント void serialEvent(Serial p) { //もし、バッファーにアナログ入力の数の2倍(上位8bitと下位8bit)あれば if(myPort.available() > NUM * 2 - 1) { //入力の数だけ繰り返し for(int i=0; iデータの流れを視覚化する 2
- 視覚的な部分をもう少し工夫してみる
- データを横にスキャンするのではなく、円形にスキャンしてみる
Arduino側のプログラムは同じ
Processing側import processing.serial.*; //アナログ入力の数を指定 int NUM = 1; //半径 float radius; //現在の角度 float angle; //現在Serial信号を受信しているか boolean running = false; //Serialクラスのインスタンス Serial myPort; //Serialより読み込んだデータを格納する配列 int[] val = new int[NUM]; void setup() { //画面を生成 size(640, 640); colorMode(HSB,360,100,100,100); frameRate(15); strokeWeight(3); //ポートの名前を取得 String portName = Serial.list()[1]; //Serialクラスをインスタンス化 myPort = new Serial(this, portName, 9600); //背景を黒に background(0); //スムージング smooth(); } void draw() { if(running) { //入力された値から円の半径をマッピング float radius = map(val[0], 0, 1023, 0, height/2); //円の中心点を指定 int middleX = width/2; int middleY = height/2; //三角関数を仕様して角度から座標を算出 float x = middleX + cos(angle) * height/2; float y = middleY + sin(angle) * height/2; //黒い線で前の値を消す stroke(0,0,0); line(middleX, middleY, x, y); x = middleX + cos(angle) * radius; y = middleY + sin(angle) * radius; //値によって色相を変化させる float h = map(val[0], 0, 1023, 180, 360); //算出した色相で線を描く stroke(h, 75, 100); line(middleX, middleY, x, y); //角度の更新 angle += 0.01; } else { //まだ信号を受信していなかったら //画面をクリックするようにメッセージを出す background(0); fill(0,0,100); text("click on screen", 10, 20); } } //シリアル入力を検出した際に発生するイベント void serialEvent(Serial p) { //もし、バッファーにアナログ入力の数の2倍(上位8bitと下位8bit)あれば if(myPort.available() > NUM * 2 - 1) { //入力の数だけ繰り返し for(int i=0; iサンプルコードのダウンロード
今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です