lecture:基礎ゼミナール

基礎ゼミサポートページ

ここは基礎ゼミのサポートページです.制作,ワークショップなどの情報がこのページと同期しています.授業中もこのページを開きながら作業することをおすすめします. 課題などの提出物はkibacoを利用しますので,ご注意ください.

Arduinoとはマイクロコントローラーと周辺機器がセットになった小型コンピュータのなかで,一般開発ユーザにとって最もメジャーな開発基板の一つです. この授業ではArduinoを利用して,実際にハードウェア,ソフトウェアプログラミングを体験し,PCインタフェースを創ることを目標としています.ではまず Arduinoを実際に使ってみましょう.実はArduinoにはたくさんの種類があり,この授業ではArduino Microと呼ばれるものを使います.

Arduino公式ウェブサイトのソフトウェアページから自身の環境に 合わせたアプリケーションをダウンロードしてください.

ダウンロードに関する注意事項

この記事の執筆時点(平成29年4月19日)では最新バージョンは1.8.2になっています.ダウンロードする際,「Support the Arduino Software」なるページで,寄付支援をするかどうかを聞かれます.Arduinoを支援したい方は金額を選び,Contributeしてください.今回はみなさん初めてArduinoを使うと思いますので,今回は下記の画像で赤丸でくくった箇所の「Just Download」を選択して,アプリケーションをダウンロードしてください. http://tetsuakibaba.jp/workshop/kisozemi/contribute.png

Windowsの注意事項

Windowsの場合:Windows Installerをダウンロード後,ダブルクリックでインストールを開始してください. インストールの途中,下の画像のように,USB Driverの項目にチェックが入っていることを確認し,チェックが入っていなければ必ずチェックを入れた上でインストールを行って下さい.Windows app形式やzipファイルでダウンロードすることはしないでください. https://www.arduino.cc/en/uploads/Guide/DRV_Capture1.png

Mac OSXの注意事項

特にありませんが,Mac OS X 10.7 Lion or newerをダウンロード後,展開されたArduino.appをアプリケーション等に移動し,起動を確認してください.

実際にインストール手続きが終わったら,Arduinoを起動して,下記のようなエディタ画面が表示されるかどうかを確認してください.ここまででArduinoが起動しない場合は,もう一度インストール手続きを行ってみて下さい.念のためofficialのインストールマニュアルへのリンクも確認してください.

http://tetsuakibaba.jp/workshop/kisozemi/arduino.png

それではソフトウェアの準備ができたので,Arduinoのボードを使ってみます.この授業ではArduino Microと呼ばれるものを使います.まずは下記に従って,テストプログラムを動かしてみます.

  1. Arduino MicroとPCをUSBケーブルで接続する
  2. Arduino ソフトウェアを起動する
  3. 「Arduinoソフトウェアメニュー」→「ツール」→「ボード:Arduino/Genuino Micro」を選択
  4. 「Arduinoソフトウェアメニュー」→「ツール」→「シリアルポート:」の中にある括弧書きでArduino Microと書かれた行を選択.
  5. Arduinoソフトウェアメニュー→「ファイル」→「スケッチブックの例」→「01.Basic」→「Blink」を選択
  6. Arduinoソフトウェアメニュー→「スケッチ」→「マイコンボードに書き込む」を選択

以上の手続きを終えた後,Arduino Micro上のLEDが1秒おきに点滅を始めればOKです.

すでにLEDの点滅に関して上記で確認していますが,改めてプログラムを記述してみます.LEDを一秒おきに点滅させるプログラムは下記のようになります.

// Hello LED!! (LEDを点滅させるプログラム)
void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

ArduinoではC言語と呼ばれるプログラム記述方法を利用します.またC言語の一つ上の機能であるC++にも一部対応しているため,ある程度の複雑なプログラムを記述することも可能です.上記の例では2つの関数(setup, loop)の中に幾つかの命令文を記述しています.関数とは一定量の命令記述をまとめたものです.今回記述されているものは全て関数になっています.setup, loopは関数の定義を記述しており,pinMode, digitalWRite, delayに関してはすでに定義済みの関数を呼び出して命令を実行しています.それぞれの関数についてはArduinoのReferenceを参考にすることができます.下記にリンクを貼っておきます.これから新しい関数が登場した場合は,Arduino Referenceページでまずは確認してください.

なお,setup, loopの定義済み関数(the function)についてですが,こちらもArduino Referenceページに説明があります.自身で確認してください.簡単にいうと下記のとおりです.

  • setup: Arduinoの起動時に一回だけ呼び出される関数
  • loop: setupの後に呼び出される関数で,関数内の記述を実行し終わると,再度呼び出されて再帰的にループを実現している

上記では関数を利用していましたが,次に変数を利用してみます.まずは下記のプログラムを実行してみます.

// 変数を1ずつ増やし(インクリメント),PCで値を確認する
void setup()
{
  Serial.begin(9600);
}

int count = 0;

void loop()
{
  Serial.println(count);
  count = count + 1;
  delay(1000);
}

int count = 0; というのが変数を宣言している箇所です.ここでは count という名前の整数型の変数を一つ準備しました.初期値を0としています.これをloopごとに1ずつ増やしています.Serialというものが出てきましたが,これはクラスになります.クラスとはC++における機能で,変数や関数をひとまとめにして,使いやすくするための機能です.この場合Serialという定義済みクラスを利用して,PCへcountの変数を送信しています.実際に上記プログラムを書き込んでもなにも変化が起きません.そこでArduinoメニューから「ツール」→「シリアルモニタ」を選択してください.するとcountの変数が1秒おきに表示されます.SerialとはPCにおける通信規格で,今回はArduino側から通信用のクラスを利用して,PCに文字列や変数などのデータを送信するプログラムになっています.

みなさんにお配りしたArduino Microにはチップ(32u4)の上に加速度センサ(ADXL335)が載っていて,それぞれ下記のように接続されています.

  • x軸:A4
  • y軸:A3
  • z軸:A2
  • 加速度センサ電源:3pin
  • 加速度センサグランド:23pin

そこで,各加速度値を読み取り,Arduino のシリアルモニタにて値を確認してみましょう.3pinをHigh,23pinをLOWにすることで,加速度センサの電源供給を行い,analogReadを利用して値を読み取っています.

// 加速度センサの値を読み,各値をシリアルモニタに出力するプログラム
void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);
}

int count = 0;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);
  delay(100);
}

この回では,加速度センサだけでまずはクリックを実装してみます.arduinoを手で持ち,クリックするように叩くと,各x,y,z軸の値が変化をします.これをうまく処理して,クリックとして扱いたいと思います.すでに加速度センサの値は観測してみたとおり,1023/2を中心として正方向の加速度はプラス(1023/2 - 1023),負の方向にはマイナス(0-1023/2)に値が変化します.つまりタップ動作が行われた場合も値は下がったり,上がったりしてしまいます.そこでこれらの状況を無視でき,且つどの軸に加速度が加わってもその変化量だけを取得する計算をしたいと思います.そこで3次元のユークリッド距離を考えてみます.x,y,zのそれぞれの値はセンサの更新時間(単位時間)ごとに値が変化する3次元ベクトルであり,各単位時間でどれだけベクトル値が変化したかどうかをみる一つの目安として,各ベクトルの距離を計算すればよいです.

時間をt,センサ更新速度を単位時間として,それぞれ,\( dx, dy, dz \) を考えると.下記の式で距離計算ができます. $$ a = \sqrt{(\frac{dx}{dt})^2+(\frac{dy}{dt})^2+(\frac{dz}{dt})^2} $$

では実際に上記計算式でプログラムを記述してみます.今回はクリックの感度(しきい値)を100,500[ms]をクリック反応後のキャンセリング時間としています

/*
加速度センサの値を処理し,各軸の単位時間あたりの変化量を値とした3次元ユークリッド距離を計算し,その値が閾値を超えたときにクリック動作を行うプログラム
*/
#include "Mouse.h"

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);

  Mouse.begin();
}

int count = 0;
int x_prev, y_prev, z_prev;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

  int a;
  a = pow(x-x_prev, 2)+pow(y-y_prev, 2)+pow(z-z_prev,2);
  a = sqrt(a);

  if( a > 100 ){
   Serial.println("Click!!");
   Mouse.click(MOUSE_LEFT);
   delay(500);
  }

  x_prev = x;
  y_prev = y;
  z_prev = z;
  delay(10);
}

マウスのクリックができるようになったので,次はポインタの移動に挑戦します.マウスクリックは Mouse.press(Mouse_LEFT); で実現できました.ポインタの移動の場合は同じようなやり方で,Mouse.move(x,y,w);を利用します.一般的なパソコン画面上ではx,yだけですが,マウスのスクロールを機能させるために,w(Wheel)のパラメータが用意されています.スクロール操作を行いたい場合は,wのパラメータを指定してください.今回は利用しないので0をいれておきます.

/*
 Arduino Mircoを傾けるとマウスポインタが上下左右に1ずつ移動するプログラム
*/
#include "Mouse.h"

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);

  Mouse.begin();
}

int count = 0;
int x_prev, y_prev, z_prev;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

  int a;
  a = pow(x-x_prev, 2)+pow(y-y_prev, 2)+pow(z-z_prev,2);
  a = sqrt(a);

  // Click
  if( a > 100 ){
   Serial.println("Click!!");
   Mouse.click(MOUSE_LEFT);
   delay(500);
  }

  // Move
  if( x > 550 ){
    Mouse.move(-1, 0, 0);
  }
  else if( x < 450 ){
    Mouse.move(1, 0, 0);    
  }

  if( y > 550 ){
    Mouse.move(0, 1, 0 );
  }
  else if( y < 450 ){
    Mouse.move(0, -1, 0);
  }
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);

  x_prev = x;
  y_prev = y;
  z_prev = z;
  delay(10);
}
/*
  上記例を応用して,よりポインティング作業がよくなるようなプログラムを作成せよ
*/
#include "Mouse.h"

void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);

  Mouse.begin();
}

int count = 0;
int x_prev, y_prev, z_prev;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

  int a;
  a = pow(x-x_prev, 2)+pow(y-y_prev, 2)+pow(z-z_prev,2);
  a = sqrt(a);

  // Click
  if( a > 100 ){
   Serial.println("Click!!");
   Mouse.click(MOUSE_LEFT);
   delay(500);
  }

  // Move
  Mouse.move((505-x)/5, (y-509)/5, 0);

  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);

  x_prev = x;
  y_prev = y;
  z_prev = z;
  delay(10);
}

PONGで高得点を目指す

Pong Game(ブラウザ上でPongが楽しめます)を自作した加速度マウスポインタを利用して高得点が取れるようにプログラムを修正してみましょう.

ここまで主にArduinoを対象に説明を行ってきました。一方でArduinoから得られたデータをコーンピュータに送信し、ゲームをしたり、映像、音を操作することもよくあります。プロトタイピングをする際、なにか一つのデバイスを制作するとします。例として、デバイスを振ると音がなるデバイスを考えてみましょう。Arduinoと複数の音声再生モジュールやスピーカを組み合わせることで、デバイス単体でこのような機能を再現することは可能ですが、ハードウェアに知識や必要なモジュールを買い揃える必要があります。プロトタイプの段階ではまず動作させ、体験価値を作り出すことが重要です。そこでよく用いられる手法はセンシング部分だけはArduinoを利用し、その他の処理は全てPCで行います。例えばArduinoからは加速度センサの値だけを出力し、Processing側でその値を処理し、音声再生をおこなうプログラムを作成することで、ハードウェアにかける時間を最低限にし、ソフトウェア場で基本的な体験が可能になるわけです。

ArduinoとPCを連携させるのに手軽なソフトウェアとしてProcessingがあります。ここではProcessingを通じてPCとArduinoを連携させてみます。Processingはプログラムを学習する上で効率的なプログラミング環境を構築しています。ぜひ下記で自習してみてください。

プログラムの基礎、Processingの可能性を体験したところで、Processingを使ってArduino上の加速度センサの値を読めるようにします。ArduinoとProcessingのデータやりとりにはシリアル通信を利用します。シリアル通信を使う場合、まずArduino上で次のプログラムを記述しておきます。

// 加速度センサの値を読み,x値だけをシリアル送信するプログラム
void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);
}

int count = 0;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

	x = map(x, 0, 1023, 0,99); // x値の範囲を 0-1023から0-99へ変更
  Serial.write(x);
  delay(30);
}
// では次はProcessing側の準備です。次の例はシリアルデータを読み取り、画面上にその値を表示し、値に応じて円の直径を変化させてみます。
import processing.serial.*;

Serial myPort;  // Create object from Serial class
int val;      // Data received from the serial port

void setup()
{
  size(500,500);
  String portName = Serial.list()[0];
  println(Serial.list());
  // Change the portname as your PC
  myPort = new Serial(this, "/dev/tty.usbmodem14321", 9600);
}

void draw(){
  background(255);
  
   if ( myPort.available() > 0) {  // If data is available,
    val = myPort.read();         // read it and store it in val
  }

	text(val, 20,20);
  noStroke();
  fill(34,150,90);
  ellipse(width/2, height/2, val,val);
}

さあ、これでArduinoからのデータを受け取ることができました。しかしまだxの値のみです。そこでy, zの値も同様にしてPCに送信したいと思います。しかし実際にこのやり方では問題が生じます。ArduinoからPCへ値を送信した場合に、そのデータがx,y,zのどの値なのかが分からなければなりません。そこでデータの送信形式(フォーマット)を事前に決めておく必要があります。

// Arduino
void setup()
{
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(23, OUTPUT);
  digitalWrite(3, HIGH);
  digitalWrite(23, LOW);
}

int count = 0;

void loop()
{
  int x,y,z;
  x = analogRead(4);
  y = analogRead(3);
  z = analogRead(2);

  x = map(x, 0, 1023, 0,99); // x値の範囲を 0-1023から0-99へ変更
  y = map(y, 0, 1023, 0,99);
  z = map(z, 0, 1023, 0,99);
  Serial.write(100);
  Serial.write(x);
  Serial.write(y);
  Serial.write(z);
  delay(30);
}
// Processing
import processing.serial.*;

Serial myPort;  // Create object from Serial class
int val;      // Data received from the serial port

void setup()
{
  size(500,500);
  String portName = Serial.list()[0];
  println(Serial.list());
  // Change the portname as your PC
  myPort = new Serial(this, "/dev/tty.usbmodem14321", 9600);
}

int x,y,z;
void draw(){
  background(255);
  
   while( myPort.available() > 4) {  // If data is available,
    val = myPort.read();
    if( val == 100 ){
      x = myPort.read();
      y = myPort.read();
      z = myPort.read();
    }
  }
  
  text(x, 20,20);
  text(y, 20,40);
  text(z, 20,60);

  noStroke();
  fill(34,150,90);
  ellipse(width/2, height/2, val,val);
}

https://www.jstage.jst.go.jp/article/jssd/58/0/58_0_86/_article/-char/ja/

// Tetsuaki Baba
// Processing のサンプルコード
// str_format: データ名をカンマ区切りで書いておく
PrintWriter output;
boolean flg_start = false;
String str_format = "time";
int count = 0;
float x, y;
int time_start;

void setup() {
  pixelDensity(displayDensity());
  size(800, 600);
  x = random(25, width-25);
  y = random(25, height-25);
}

void draw() {
  background(0);
  if ( flg_start == false ) {
    textSize(30);
    text("Press space key to start", width/2, height/2);
    return;
  }
  if ( sqrt( (x-mouseX)*(x-mouseX) + (y-mouseY)*(y-mouseY) ) < 25 ) {
    x = random(25, width-25);
    y = random(25, height-25);    
    output.print(str(millis()-time_start)+"\n");
    time_start = millis();
    count++;
    if ( count == 10 ) {
      flg_start = false;
      output.flush(); 
      output.close();
    }
  }
  ellipse(x, y, 50, 50);
  println(millis());
}

void keyPressed() {
  if ( key == ' ' ) {
    flg_start  = !flg_start;
    time_start = millis();
    count = 0;

    if ( flg_start == true ) {
      String filename = nf(year(), 4) + nf(month(), 2) + nf(day(), 2) + nf(hour(), 2) + nf(minute(), 2) ;
      output = createWriter( filename + ".csv"); 
      output.println( str_format );
    }
  }
}

// Tetsuaki Baba
// Processing のサンプルコード
// str_format: データ名をカンマ区切りで書いておく
PrintWriter output;
boolean flg_start = false;
String str_format = "time";
int count = 0;
float x, y;
int time_start;

void setup() {
  pixelDensity(displayDensity());
  size(800, 600);
  x = random(25, width-25);
  y = random(25, height-25);
}

void draw() {
  background(0);
  if ( flg_start == false ) {
    textSize(30);
    text("Press space key to start", width/2, height/2);
    return;
  }
  if ( time_start < millis() ) {
    text("Press 'j' key", width/2, height/2);
  }
}


void keyPressed() {
  if ( key == ' ' ) {
    flg_start  = !flg_start;
    time_start = millis()+(int)random(3000.0f, 10000.0f);


    count = 0;

    if ( flg_start == true ) {
      String filename = nf(year(), 4) + nf(month(), 2) + nf(day(), 2) + nf(hour(), 2) + nf(minute(), 2) ;
      output = createWriter( filename + ".csv"); 
      output.println( str_format );
    }
  }
  if ( key == 'j' && time_start < millis() ) {
    output.print(str(millis()-time_start)+"\n");
    time_start = millis()+(int)random(3000.0f, 10000.0f);
    count = count + 1;
    if ( count == 10 ) {
      flg_start = false;
      output.flush(); 
      output.close();
    }
  }
}

Google SpreadSheetに集まったデータをグラフにしてみました.各グラフバーにくっついているのは標準偏差のことを指しています.

上記エクセルグラフを含むデータ:基礎ゼミ2018ポインティング速度データ.xlsx

開発したデバイスを利用して,パラメータの変更により,どの程度ポインティング時間が変化するかを実験から論じる. ただし,既存デバイスとの比較はしなくともよいが,余裕があればしてもよい.

授業の中でArduino Microを利用し,マウスポインタを作成しました.マウスポインタの移動には加速度センサの値を利用しました. 「上記例を応用した,よりポインティング作業が早くなるプログラムサンプル」を再び利用して,arduinoをマウスに一度変更 します.

対象となるプログラムの中で,

Mouse.move((505-x)/5, (y-509)/5, 0);

と記述された行がありますが,これはx,y軸のそれぞれの移動量を傾き具合によって変化させるコードになっています. ただしこの場合の505,509といった数値はデバイスを水平に保ったときの値を指しており,みなさんのデバイス上では 微妙に値がことなるので注意してください.ここでいうところのパラメータとはそれぞれの移動量パラメータのことを さしています.

例えばサンプルコードをなるべく変更せずにマウスの移動速度を変化させる場合は,

(505-x)/a, (y-509)/a

のaを更に具体的になパラメータとして,a=1, a=2, a=3, a=4, a=5等の場合でユーザのポインティング速度がどれだけ 変化するかを実験することで,マウスの移動速度とポインティング速度の関係性を明らかにできるようになります.

またこの他にもここで紹介した計算式以外でポインティング移動量をパラメータとして実験していただいても 構いません.

上記で説明した実験手順で例えばa=5とa=10を実験したとします.このときにa=5の場合,ポインティング時間は平均800[ms]程度, 一方a=10のときは1,200[ms]程度だったとします.この場合,a=5のほうがポインティング時間短縮という意味では優れているわけですが,a=6のとき,700[ms]だった場合,a=7 - a=9 の値も調べ,どのあたりからポインティング時間が移動速度が増加しているのにもかかわらず,増大しているのかを議論すると,研究としての再現性及び有用性が高まります.

  • 締切:7月24日 17:00 迄
  • 方法:kibacoの最終課題ページから論文を添付送信します.
  • 提出フォーマット:PDFファイル(枚数の目安としては,2-6枚程度です.実験した内容や検討した事項により文章は大きく増減しますので,あまり枚数は気にせず,必要な情報を載せ,必要な議論を論じることに集中してください.)

添削された論文はkibacoで利用している各自メールアドレス宛に届きます.なので,ed.tmu.ac.jp のメールチェックを忘れないようにお願いいたします.

  • 質問:Arduinoにマウス/キーボードプログラムを書き込みしたところ,プログラムミスで,マウス/キーボードで制御不能なふるまいになってしまい,別のプログラムを記述することが困難になりました.

一度目の提出お疲れ様でした.レポートを書くとは異なり,「新規性」,「再現性」,「有用性」を意識しながら書くのは少し慣れも必要かと思います. 最初から上手にかけている人もいれば,そうでないもの楽しく拝見させていただきました.今回の提出一回で合格となった人は一名でした.合格者は 論文中に「合格」と記載してあります.それぞれ簡単に赤を入れているので,各自の修正箇所はその赤に従ってください.

全体的に予め指摘したほうが良いことがいくつかありましたので,赤が入っている/入っていないにかかわらず,下記を読んでいただきご自身の論文修正に 反映させてください.

再現性

最も多くの方がこの再現性の面で問題がありました.基礎ゼミで制作したデバイスやプログラムをそのまま流用しているので,馬場や他の学生であれば 詳細を記述しなくともなにをやっているか理解はできますが,あくまで第3者がこの論文を読んだ際,必要な情報が含まれているかを改めて確認してみましょう. 例えば

  • プログラム中のaの値を変更し → プログラムの中でaがどのように利用されているかを数式等で論文中で示さないといけません
  • 実験を繰り返しデータを取得した → 具体的にポインティングの試行回数は何回でしょうか? データとは具体的にはなんのことでしょうか?
  • 実験を行った. → 具体的に何人の被験者で,年齢層や性別といった情報がは最低限入れておきましょう.

といった記載が目立ちました.なにも知らない人が論文を読んだ際,再現性を担保するために必要な情報は全て含んでいるでしょうか?もう一度確認してください.

表と図

表は下,図は上にキャプション(説明文)を入れます.また文中で必ず 表XXはなになにを示し,,といった具合に必ず参照してください.またキャプションだけを読んでも図表が理解できるものになっていますか?

単位

グラフに単位を記載し忘れていませんか?単位があるものは必ず単位に関して記述してください.

  • /home/users/2/lolipop.jp-4404d470cd64c603/web/ws/data/pages/lecture/基礎ゼミナール.txt
  • 最終更新: 2018/07/25 16:09
  • by baba