yoppa.org


バイオメディア・アート

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; i 0){
    //アナログ入力の数だけ繰り返し
    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; i

データの流れを視覚化する

データの流れを視覚化する1

データからノイズを除去する

  • センサーからの入力値には、意図しない信号(ノイズ)がのってしまう場合がある
  • Processing側の工夫で、急激な入力信号の変化は除去したい
  • 実際の値の変化を、指定した値でeasingする → なめらかな変化となる

Arduino側

//アナログ入力の数を定義する
#define NUM 1

//アナログ入力の値を格納する配列
    int val[NUM]; 

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

void loop() {
    //アナログ入力の数だけ繰り返し
    for(int i=0; i 0) {
    //アナログ入力の数だけ繰り返し
    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

サンプルコードのダウンロード

今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です