arduino (12/1に10節を追記しました)

エルゴノミクスコンピューティング実習 (その 2)
入力インタフェースの構築
井村 誠孝 ([email protected])
2015 年 12 月 1 日
バーチャルリアリティシステムの構築にあたって,ユーザの状態を計測する入力インタフェースは,バーチャル
世界にユーザの状態を反映させるために必要不可欠な要素である.本演習では,主にユーザの手の挙動を対象と
して,センサとマイクロコントローラを用いた計測と信号処理の方法について学習する.マイクロコントローラ
としては Arduino を用いる.
■■■ 演習: この項目が演習 各自,実施してください.発展と書かれている項目は,任意です.
1
デジタルピン
USBコネクタ
リセットボタン
電源ピン
アナログ入力ピン
図1
Ardino Duemilanove 328 の外観
Arduino で動作するプログラムの開発
1
本節では,Arduino の紹介と,プログラム開発の流れについて解説する.
1.1
Arduino とは
Arduino はオープンソースのプロトタイピングプラットフォームであり,安価なハードウェアと簡潔なソフト
ウェアから構成される.
Arduino は多様なセンサからの信号を入力し,それを処理し,光 (LED) や動き (モーター) などを通じて外部に
出力することが非常に簡単にできる.
また,シリアル通信を利用して,PC をはじめとする他の機器と連携することができる.
プログラムの開発は C/C++ を簡略化した言語を用いて PC 上の開発環境で行えるため,敷居が低い.
本実習では,Arduino の入力および出力機能を用いて,ユーザの手の位置などに基づく制御の簡単な演習を行う.
1.2
Arduino のボードを見る
Arduino のボードを確認する.これまでに多数の種類のボードが発売されているが,本実習では Arduino
Duemilanove 328 を用いる.図 1 に Arduino Duemilanove 328 の外観を示す.
• USB コネクタ (B タイプ): 電源 (5V) の供給,シリアル通信 (PC や他のデバイスとの通信に用いる)
• デジタルピン: 0 番から 13 番まである.On(5V)/Off(0V=GND) の入力と出力に用いる.0 番と 1 番はシリ
アル通信と共用である (シリアル通信を使用しているときは,別の用途に使用することはできない).PWM
と書かれているもの は,PWM(Pulse Wave Modulation) を用いた擬似的なアナログ出力が可能である.出
力の際には電源として用いることができるが,最大 40mA である.
• アナログ入力ピン: A0 から A5 まである.A/D(analog/digital) 変換に用いる.
• 電源ピン: 外部のデバイス (センサなど) に電源を供給する.5V と 3.3V がある.あまり大容量の電流を取
り出すことはできないので注意.
■■■ 演習: ボード確認 Arduino Duemilanove 328 をパッケージから取り出し,ボード上のピン配置などを確認
する.
1.3
Arduino の接続とプログラムの実行
Arduino Duemilanove 328 を USB ケーブルで PC の USB ポートに接続する.USB は電源の供給および PC と
のシリアル通信に使用される.ボード上の LED(ON) が緑に常時点灯する.
2
これがArduino
図 2 デバイスマネージャーでポートを確認
Arduino に書き込まれているプログラムは,電源が供給されると自動的に実行が開始される.またリセットボタ
ンを押すと,プログラムの最初から再実行する.
■■■ 演習: ボード接続 Arduino Duemilanove 328 を USB ケーブルで PC に接続する.
1.4
ポートの確認
PC で作成したプログラムを Arduino に転送するためには,Arduino が接続されているポートを確認しておく.
図 2 に示すように,スタートボタン ▷ コントロールパネル ▷ デバイスマネージャー ▷ ポート (COM と LPT) を
見る.
USB Serial Port (COM5) 等と書かれているのが Arduino である.複数の機器が接続されている場合に区別する
ために,ポートには番号が付いている.COM の後ろの数字が,接続されているポート番号である.それぞれの
PC 環境によって違う可能性もあるので,注意すること.
■■■ 演習: ポート確認 デバイスマネージャーを開き,Arduino が接続されているポートを確認する.
シリアルポートのプロパティ
デバイスマネージャーで見つけた Arduino の項目の上で,右クリックし,プロパティを確認してみる.ポー
トの設定 のタブに,
• ビット/秒
• データビット
• パリティ
• ストップビット
• フロー制御
とある.これらの詳細は割愛するがシリアル通信の一般的な設定項目であり,PC 側の設定と Arduino 側の設
定が一致していないと,正常な通信が行えない.通信がうまくいかない場合にまずチェックするべき箇所で
ある.
3
図 3 WBuzzer でポートを確認
■大学では 学生はデバイスマネージャーを開くことができない.タスクバー右下の上向き三角をクリックすると
現れる WBuzzer(大概右上のアイコン) を使うと調べることができる.図 3 にある FTDI: USB Serial Port (COM?)
が Arduino である.
プログラム開発の流れ
1.5
Arduino のプログラム開発は,PC を用いて行う.PC 上で動くプログラムの開発との大きな違いは,プログラ
ムを開発する環境と,それが実行される環境が異なることである.開発には Arduino IDE という開発ツールを用
いる.
統合開発環境
IDE は Integrated Development Environment の略であり,テキストエディタ,コンパイラ,デバッガなどが一
つにまとめられた統合開発環境を指す.Visual Studio や Eclipse なども IDE の一種である.
開発の一般的な手順について,以下に示す.
まず開発ツールを立ち上げる.Arduino IDE の起動は,スタートボタン ▷4.Tools ▷arduino により行う.
Arduino IDE を起動すると,既にプログラムの雛形が準備されており,setup() と loop() という二つの関数
を定義するように促される.setup() は起動時に一度だけ実行される関数で,初期化の処理を記述する.loop()
は繰り返し実行される関数で,メインの処理を記述する.
適宜,プログラムを作成する.Arduino ではプログラムのことをスケッチと呼ぶ.Arduino IDE の開発言語は
C/C++ をベースにしているため,C 言語の知識があれば特に問題なくプログラムが記述できる.
プログラムが作成できたら,コンパイルし,エラーが無ければ,Arduino への書き込みを行う.Arduino は書き
込まれたプログラムを即座に実行開始する.
1.6
情報源
• 日本語の情報は,以下のウェブサイトがまとまっているが,情報が古いかもしれない.
http://www.musashinodenpa.com/arduino/ref/
• 最新のリファレンスは英語ではあるが,以下を参照するとよい.
https://www.arduino.cc/en/Reference/HomePage
• Arduino IDE のファイル ▷ スケッチの例には様々なサンプルがある.
■■■ 演習: リファレンス確認 http://www.musashinodenpa.com/arduino/ref/ を開き,特に左側半分を眺
めて,どのような機能があるか,ざっと確認する.
4
1.7
Arduino での制約
PC 上で C 言語によるプログラムの作成と異なる点を示す.
• int 型は 2 バイトであり,格納できる値の範囲は-32768∼32767 である.演算の途中で int 型の範囲を超えて
しまう場合があるので,挙動がおかしい場合は計算途中でのオーバーフローの確認も必要.
• float 型と double 型はどちらも 4 バイトである.double 型を使用しても精度は上がらない.float 型を使用
するのがよい.
(本項は随時追記します)
5
デジタル出力: LED を光らせる
2
Arduino でできることはデジタル/アナログの入力/出力である.最初にデジタル出力の例として,ボード上の
LED を光らせる.
2.1
ソースコードの作成
まずは Arduino IDE を起動し,以下のソースコードを入力する.
■■■ 演習: ソースコードの入力 以下のソースコードを入力せよ.
リスト 1
2
void setup() {
pinMode(13, OUTPUT);
3
}
1
ボード上の LED を点滅させる
4
5
6
7
8
9
10
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
ソースコードの作成が終わったら,一度保存しておくことにする.ファイル ▷ 名前を付けて保存で,ダイアロ
グ「スケッチのフォルダの保存先...」が開くので,本実習用のフォルダ以下に保存する.名前は何でもよいが,例
えば blink とする.デフォルトだと日付入りの名前が付くが,後で何が何だったかわからなくなるので,適切な名
前を付ける.
■■■ 演習: スケッチの保存 スケッチ blink を保存し,保存先のフォルダの内容を確認せよ.
2.2
Arduino IDE の設定確認
ソースコードが完成したら,Arduino への書き込みを行うために,Arduino IDE のボードとポートの設定を確認
する.メニューのツールをクリックすると,現在設定されているボード,プロセッサ (無い場合もある),ポートが
表示される (図 4).それぞれ,
• ボード: Arduino Duemilanove 328
• プロセッサ: ATmega328
• ポート: COM5 (PC によって異なる可能性がある)
となっていることを確認する.適切な設定がされていない場合は,各サブメニューから選択する.
■■■ 演習: Arduino IDE の設定確認 Arduino IDE の設定が,手元の Arduino Duemilanove 328 にプログラムの
書き込みが行える状態 (上記設定) になっていることを確認せよ.
2.3
コンパイルと実行
Arduino IDE の上に並んでいるアイコンの,一番左端のボタン (チェックマーク) でプログラムに文法的誤りが
ないかを検証する.スケッチ ▷ 検証・コンパイル,あるいはショートカットキー Ctrl+R でも行える.
エラーが出なければ,Arduino へのプログラムの書き込みを行う.書き込みは左から二つ目のアイコン (右向き
矢印) で行う.ファイル ▷ マイコンボードに書き込む,あるいはショートカットキー Ctrl+U でも行える.
二つのアイコンの位置を図 5 に示す.
6
図 4 Arduino IDE の設定確認
コンパイル
書き込み
図5
コンパイルと書き込み
書き込みが終了すると,自動的にプログラムが実行され,Arduino 上の LED が 1 秒ごとに点滅する.
■■■ 演習: プログラムのコンパイルと実行 作成したプログラムを実行し,正しく実行されることを確認せよ.
2.4
ソースコードの解説
簡単にソースコードの各関数について解説を行う.
• setup() には初期化の処理を記述する.起動時に一度だけ実行される.
• pinMode() は,各ピンを入力に使用するか,出力に使用するかを設定する関数である.ここでは,13 番の
デジタルピンを出力モード OUTPUT に設定している.OUTPUT および INPUT は定義済みの定数である.13
番のデジタルピンは,Arduino のボード上にある L のラベルが振られた LED と直結しており,13 番のデ
ジタルピンへの出力によりこの LED の点灯状態を制御することができる.
• loop() にはメインの処理を記述する.loop() は自動的に繰り返し実行されるので,中で for(;;) や
while(1) といった無限ループを組む必要はないし,組んではいけない.
7
• digitalWrite() はデジタルピンの出力状態を設定する関数である.HIGH を指定すると 5V に,LOW を指
定すると 0V に設定される.HIGH および LOW は定義済みの定数である.
• delay() はプログラムの実行を指定時間だけ中断して待機する関数である.引数は待ち時間であり,単位
はミリ秒 (ms) である.この例では LED を点灯あるいは消灯してから,その状態を 1 秒間維持する.
2.5
課題
■■■ 演習: 点滅パターンの変更 LED が点滅する速さやパターンを変えてみる.
例: 700ms 点灯し,300ms 消灯する.
■■■ 演習: 点滅パターンの変更 LED が点滅する速さを非常に速くしてみると,どうなるか.
例: 1ms 点灯し,1ms 消灯する.
8
ソースコードの可読性の向上
3
よいソースコードを記述する原則の一つは,マジックナンバー (ソースコード内に埋め込まれた定数) の記述を
避けることである.出力するデジタルピンの番号や点滅速度を変数に定義すると,ソースコードの見通しがよく
なる.const は,変数が定数であることを示す接頭語である.
■■■ 演習: ソースコードの可読性向上 ソースコードを以下のように書き換えて実行せよ.
リスト 2
1
2
3
ボード上の LED を点滅させる (可読性向上版)
const int ledPin = 13;
const int periodOn = 1000;
const int periodOff = 1000;
4
6
void setup() {
pinMode(ledPin, OUTPUT);
7
}
5
8
13
void loop() {
digitalWrite(ledPin, HIGH);
delay(periodOn);
digitalWrite(ledPin, LOW);
delay(periodOff);
14
}
9
10
11
12
一見,タイプ量が増えるが,よいことがある.
• 出力ピンの番号が変わっても,1 箇所変更するだけで済む.
• 数字は見ても意味がわからないが,変数だと名前から意味がわかる.
• const を付けることで,この変数に設定された値は変更できない値であることがわかる.
• 定数の定義がソースコード内の一箇所に固まっているので,修正時にソースコードをあちこち眺める必要
がない.
9
短い方がカソード
長い方がアノード
アノード
カソード
電流の向き
図 6 LED
アナログ出力: LED の明るさを連続的に変化させる
4
デジタル出力ピンで設定できる値は,HIGH(5V) か LOW(0V) のいずれかに限られていた.これに対して,アナ
ログ出力は,0 から 255 までの任意の値を指定することができる.ただし,アナログ出力と言われるものの,実際
は PWM(Pulse Wave Modulation) という,指定された値に応じた時間比率 (デューティー比) でデジタル出力の On
と Off を高速に切り替える出力を行っている.
アナログ出力を使うと,LED の明るさや DC モーターの回転数を変化させることができる.
Arduino では PWM 出力できるピンが限られている.Arduino Duemilanove 328 の場合,横に PWM と書かれて
いるデジタルピン (3,5,6,9,10,11) が PWM に対応している.
4.1
LED の接続
11 番ピンと GND との間に LED を接続する.本来 LED には適正な印加電圧および電流があり,適切な電圧と
電流を得るために電流制限抵抗を直列に接続する必要があるのだが,本実習では電流制限抵抗が内蔵されていて
5V の電圧を直接印加可能な LED を使用する.
LED には極性がある.図 6 に示すように,LED の長い方の足をアノード,短い方の足をカソードと呼ぶ.電流
はアノードからカソードに流れる.よって,アノードに電源電圧の高い方,カソードに低い方を接続する.
本実習では,LED の長い方の足を 11 番ピンに,短い方の足を二つ間を離した GND ピンに接続する.接続した
状態を図 7 に示す.
4.2
アナログ出力
analogWrite() 関数を用いて,アナログ出力ピンと 0 から 255 の値を指定することで,アナログ出力ピンの
PWM のデューティー比を変えることができる.
アナログ出力ピンの指定は,A0 から A5 により行える.これらは定数であり,実際にはピン番号が定義されて
いる.
■■■ 演習: LED の明るさの連続的変化 LED のアノード (長い足) を 11 番ピンに,カソード (短い足) を GND
に接続し,以下のソースコードを入力・実行し,連続的に明るさが変化することを確認せよ.
リスト 3 LED の明るさを連続的に変化させる
1
const int ledPin = 11;
10
図 7 LED を接続した状態
2
3
const int wait = 30;
const int changeStep = 5;
4
5
6
int brightness;
int changeDir;
7
11
void setup() {
pinMode(ledPin, OUTPUT);
brightness = 0;
changeDir = 1;
12
}
8
9
10
13
14
15
16
17
18
19
20
21
22
void loop() {
analogWrite(ledPin, brightness);
brightness += changeStep * changeDir;
if (brightness >= 255) {
brightness = 255;
changeDir = -1;
} else if (brightness <= 0) {
brightness = 0;
changeDir = 1;
}
23
delay(wait);
24
25
4.3
}
ソースコード解説
• 変数のスコープは C 言語と同じである.関数外で宣言すると,大域変数 (グローバル変数) となり,ソース
コード全体で使用できる.この例では setup() で変数の初期化を行っているが,宣言時に代入してもよい.
• if や while といった C 言語の制御構文はそのまま使用できる.
4.4
演習
■■■ 演習: LED の逆差し LED を Arduino に接続するときに,アノードとカソードを逆に差してしまったとす
ると,どうなるだろうか.確認せよ.
■■■ 演習: 点滅パターンの変化 点滅パターンを各自適当に変更してみよう.より高速に点滅させる,消灯時間
11
を挟むなど.
■■■ 演習: [発展] 二つの LED を制御 ボード上の LED と外付け LED を異なる周期で点滅させる方法を考えよ
う.例えば,ボード上の LED を 300msec ごとに点滅させ (周期は 600msec になる),外付け LED を 700msec 周
期で連続的に変化させるにはどうすればよいか.
変数や関数の名前の付け方
変数や関数の名前の付け方には,様々な流儀がある.
• very_very_long_name: スネークケース
• veryVeryLongName: キャメルケース
• VeryVeryLongName: パスカルケース (アッパーキャメルケース)
Arduino では変数名・関数名ともにキャメルケースを用いるのが一般的であるようだ.
12
PC と通信する
5
Arduino と PC の間での通信を行うと,様々なアプリケーションとの連携が可能になる.また,Arduino 単体で
は困難な,実行時の状態の確認を行うことができる.
前述した通り,Arduino と PC との間は,USB ケーブルを介したシリアル通信と呼ばれる通信方式で接続され
る.シリアル通信は,Serial ライブラリを用いて行う.
アナログ出力によって LED の明るさを連続的に変化させるプログラムに,LED の状態をシリアル通信を用い
て PC に送信する機能を追加する.
リスト 4 LED の明るさを連続的に変化させる (シリアル通信付き)
1
2
3
const int ledPin = 11;
const int wait = 30;
const int changeStep = 5;
4
5
6
int brightness;
int changeDir;
7
12
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
brightness = 0;
changeDir = 1;
13
}
8
9
10
11
14
15
16
17
18
19
20
21
22
23
24
void loop() {
analogWrite(ledPin, brightness);
Serial.println(brightness);
brightness += changeStep * changeDir;
if (brightness >= 255) {
brightness = 255;
changeDir = -1;
} else if (brightness <= 0) {
brightness = 0;
changeDir = 1;
}
25
delay(wait);
26
27
5.1
}
ソースコードの解説
Serial.begin() でシリアル通信を開始する.引数は通信速度である.9600 は 1 秒間に 9600 ビットの帯域で
通信するという指定である.このプログラムにおいて,Arduino 側のシリアル通信設定は以下の通りとなる.
• ビット/秒: 9600
• データビット: 8
• パリティ: なし
• ストップビット: 1
• フロー制御: なし
Serial.println() で,変数の値などをシリアル通信で PC に送信する.値は文字列に変換されて送られる.
整数型,浮動小数点型,文字列型,いずれでも適切に変換される.C 言語の printf() と異なり,詳細なフォー
マットの指定は行えないので注意.
なおシリアル通信を使うと,デジタルピンの 0 番と 1 番は使用できなくなるので,注意が必要である.
13
図8
5.2
シリアルモニタ
PC での情報確認
Arduino から PC に送信したデータを見るためには,Arduino IDE のシリアルモニタを使用する.一番右端のア
イコンをクリックするか,もしくはツール ▷ シリアルモニタ (ショートカットキーは Ctrl+Shift+M) を選択する
と,図 8 のようにシリアルモニタのウィンドウが開き,Arduino との情報のやりとりを表示することができる (本
節では使用しないが,PC で情報を入力し,それを Arduino に送ることもできる).シリアルモニタに brightness
の数値が表示されることを確認する.
■■■ 演習: シリアル通信 ソースコードにシリアル通信部分を加え,実行し,Arduino IDE でシリアルモニタを
開いて,LED の明るさ情報が送信されてくることを確認する.
なお,現時点では Arduino IDE のシリアルモニタでしか情報の送受信を確認できないが,自作のプログラム
(Visual Studio でも,processing でも可) でシリアル通信を行うことで,直接 Arduino と情報の送受信を行うこと
ができる.
5.3
演習
■■■ 演習: シリアル通信機能の付加 ボード上の LED を点灯させるプログラムに,シリアル通信機能を付加し
て,LED の状態「HIGH」か「LOW」を確認できるようにする.
■■■ 演習: 通信速度の不一致 シリアルモニタの右下にある通信速度を変えてみる.プログラム側で指定してい
る速度 (9600) と,敢えて異なる値に設定すると,何が起こるだろうか.なぜそうなるのか考えてみる.
14
図9
距離センサ
アナログ入力: 距離センサを使う
6
アナログ入力ピンは,Arduino Duemilanove 328 の場合 6 本あり,それぞれ A0 から A5 の名前が付いている.
ソースコード中でも,A0 から A5 といったシンボル名を使用することができる.
アナログ入力ピンと GND の間の電圧を,0 から 1023 の整数値として得ることができる.0V の場合が 0,5V
の場合が 1023 である.
A/D 変換
連続的なアナログ信号を離散的なデジタル信号に変換することを,A/D 変換 (Analog Digital Conversion) と
呼ぶ.A/D 変換は標本化 (サンプリング) と量子化の二つの過程から構成される.標本化は,ある時刻の信号
を得ることであり,この段階では信号はまだアナログ値である,量子化は,得られた信号をデジタル値に変
換する.
Arduino Duemilanove 328 では,量子化によって信号の値は 1024 = 210 段階のいずれかに離散化される.信
号を表現に用いられるビット数のことを量子化ビット数と呼ぶ.つまり Arduino のアナログ入力の量子化
ビット数は 10 である.
6.1
距離センサ
距離センサとして,シャープ製の GP2Y0A21YK(図 9) を使用する.対象物に赤外光を照射し,その反射光の向
きから三角測量の原理で距離を測定する.
本センサに限らず,電子部品には特性などを記したデータシートが通常用意されている.本センサのデータシー
トは以下にある.
http://www.sharp-world.com/products/device/lineup/data/pdf/datasheet/gp2y0a_d_e.pdf
ケーブル赤を 5V に,ケーブル黒を GND に接続して,センサに電源を供給する.ケーブル黄の電位が対象物ま
での距離を反映した出力となる.このケーブル黄を Arduino のアナログ入力ピンに接続することで,対象物まで
の距離を計測することができる (図 10).
6.2
アナログ入力を行うプログラム
アナログ入力ピン A0 で得られた電圧を,PC のシリアルモニタに出力するプログラムを作成する.
デジタルピンの場合は,入力と出力の双方に用いることができたため,いずれに用いるのか設定が必要だった
が,アナログ入力ピンは,入力専用であるため,pinMode() による入出力の設定は不要である.
リスト 5 赤外線距離センサ (その 1)
1
2
const int sensorPin = A0;
const int wait = 30;
3
15
図 10
図 11
5
void setup() {
Serial.begin(9600);
6
}
4
距離センサと Arduino の接続
距離センサでの計測の様子
7
11
void loop() {
int val = analogRead(sensorPin);
Serial.println(val);
delay(wait);
12
}
8
9
10
センサを上向きにして,手やノートなどの遮光する物体をセンサの上で上下させると,シリアルモニタに出力さ
れる値が変わるのが確認できる (図 11).
■■■ 演習: 距離センサの使用 距離センサを Arduino に接続し,プログラムを作成・実行し,シリアルモニタを
開いて,距離センサから出力されている値と,距離との関係について確認する.
6.3
距離センサと LED の連携
距離センサから得られる出力に応じて,LED の点滅速度を変えてみよう.ここでは最も単純に,得られた値の
時間間隔で,ボード上の LED を点滅させてみよう.
注意: 前節のプログラムと異なり,ここではボード上の LED(13 番ピン) を点滅させている.
■■■ 演習: 距離センサと LED の連携 以下のプログラムを実行せよ.
16
リスト 6 赤外線距離センサと LED の連携
1
2
const int sensorPin = A0;
const int ledPin = 13;
3
5
void setup() {
pinMode(ledPin, OUTPUT);
6
}
4
7
13
void loop() {
int val = analogRead(sensorPin);
digitalWrite(ledPin, HIGH);
delay(val);
digitalWrite(ledPin, LOW);
delay(val);
14
}
8
9
10
11
12
6.4
実習
■■■ 演習: 距離センサと LED の連携 その 2 先程のプログラムでは,手が近付くと点滅が遅くなったが,手が
近付く方が点滅が速くなる方がそれらしいように思われる.出力された値が 0 のときに delay(500),600 のとき
に delay(20) となるような変換を考えて実装してみよ.
ヒント: 2 点を通る直線の式を考える. x = 0 のとき y = 500, x = 600 のとき y = 20 なので,傾きは
(20 − 500)/(600 − 0) である.int/int=int であることに注意が必要.また y 切片は x = 0 のときの y の値なので
自明.
■■■ 演習: 距離センサと LED の連携 その 3 11 番ピンに外付けの LED を接続し,距離に応じて LED の明る
さが変化するようにせよ.analogRead() で得られる値の範囲は 0-1023 であるが,analogWrite() では 0-255
の値を出力するので,そのまま出力するわけにはいかず,スケールの変換が必要であることに留意せよ.
17
距離センサのキャリブレーション
7
距離センサから計測結果が得られるようになったが,得られた値は実際の距離そのものではなく,センサの出力
電圧を更に量子化した値であり,そのまま使用するには直感的ではない.
距離センサの出力電圧と距離との関係を下図に示す.実用区間である 10cm から 80cm の間では,距離と出力電
圧はほぼ反比例のように見受けられるが,出力電圧を距離に変換するためには係数まで含めた曲線の式を決定す
る必要がある.
本節では,距離センサのキャリブレーション (較正) を行う.既知の距離に物体を置いたときのセンサの出力電
圧を計測し,出力電圧と距離の組から,曲線の式を決定する.曲線の形を仮定して,係数を決定することが一般的
である.
キャリブレーションはあらゆるセンサの使用において重要である.
7.1
得られた値を電圧に直す
A/D 変換の量子化処理で行われていることの逆を行い,得られた値を電圧に変換する.すなわち,得られる値
は 0 から 1023 の 1024 段階で,0 が 0V,1023 が 5V に対応していることから,5.0 / 1024 を乗じることで,電圧
に直すことができる.
analogRead() の結果は int 型であるが,変換後の電圧は float 型に格納する必要があることに注意する.また
C 言語は int 型 / int 型 = int 型 であるから,analogRead() の値を先に 1024 で割ってしまうと正しい結果が得ら
れない (常に 0 になる).演算の順序に気を付ける.演算子には優先順位があり,* および / は同じ優先順位で,更
に演算は左結合である.左結合とは,任意の演算子を ◦ としたときに,A◦B◦C を (A◦B)◦C と解釈することであ
る.以上を踏まえると,
1
2
int val = analogRead(A0);
float voltage = val * 5.0 / 1024;
は正しく動作する.参考のため,以下に正しく動作しない例を示す.
正しく動作しない例 1:
1
2
int val = analogRead(A0);
float voltage = val / 1024 * 5.0;
正しく動作しない例 2:
1
2
int val = analogRead(A0);
float voltage = val * 5 / 1024;
18
7.2
出力電圧の平均を求める
曲線の式を求めるためには,ある既知の距離に物体を置いた場合の出力電圧が必要である.これまで見てきて
わかるように,センサの出力電圧にはばらつきがある.まず,電圧を複数回計測し,平均と分散と標準偏差を算出
するプログラムを作成する.曲線を求めるという目的のためには,平均があれば事足りるが,誤差の程度も興味が
あるので,標準偏差も算出してみる.
プログラムは以下のようになる.このプログラムは,連続して 100 回分のサンプリングされた信号を配列変数
に保存した後,統計量を計算し,シリアル通信で PC に送信することを繰り返す.
リスト 7
1
2
3
複数回計測による統計量の算出
const int numData = 100;
float buffer[numData];
int n;
4
5
6
const int sensorPin = A0;
const int wait = 30;
7
10
void setup() {
Serial.begin(9600);
n = 0;
11
}
8
9
12
13
14
15
16
17
void loop() {
int val = analogRead(sensorPin);
float voltage = val * 5.0 / 1024;
buffer[n] = voltage;
n++;
18
if (n == numData) {
float sum;
19
20
21
24
sum = 0.0;
for (int i = 0; i < numData; i++) {
sum += buffer[i];
25
}
26
float average = sum / numData;
22
23
27
31
sum = 0.0;
for (int i = 0; i < numData; i++) {
float d = buffer[i] - average;
sum += d * d;
32
}
33
float variance = sum / numData;
28
29
30
34
float sd = sqrt(variance);
35
36
Serial.println("");
Serial.println(average);
Serial.println(variance, 6);
Serial.println(sd, 3);
37
38
39
40
41
n = 0;
42
}
43
delay(wait);
44
45
}
ソースコードに若干の解説を加える.
• 配列変数は C 言語と同じように使用できる.
19
• Serial.println() で float 型の変数を出力する場合,第 2 引数で小数点以下の桁数を指定できる.
7.3
近似曲線を求める
曲線の式として,出力電圧 v と距離 d の間に,
a
+b
v
(1)
y = ax + b
(2)
d=
の関係を仮定する.係数 a と b の値を求めたい.
ここで,一般化のために, x = 1/v, y = d と置くと,
となる.いま,距離 di に物体を置いたときの出力電圧を vi とすると,xi = 1/vi , yi = di であり,(xi , yi ) の組が n 組
計測されたときに,最良の 1 次近似を与える直線のパラメータ a, b を決定する問題となる.これは最小二乗法に
より求めることができる.誤差の二乗和
e=
n
∑
{yi − (axi + b)}2
(3)
i=1
が最小になる十分条件である,
∂e
∂e
= 0,
=0
∂a
∂b
(4)
を満たすための条件を書き下すと,a, b の連立方程式となり,
(∑n 2
xi
∑i=1
n
i=1 xi
)
) ( ) (∑n
∑n
xi yi
xi a
i=1
i=1
∑n
= ∑n
i=1 yi
i=1 1 b
(5)
左辺の 2 × 2 行列の逆行列を左から乗じることによって,a, b が求まる.
7.4
演習
■■■ 演習: 距離センサのキャリブレーション
1. 複数回計測により統計量を算出するプログラムを作成,実行せよ.
2. 25cm 定規を配布するので前に取りに来る.
3. 配布した定規の左端に距離センサを置き,10cm,15cm,20cm,25cm の 4 点に,順次何か光を反射する
平面を置く (図 12).先程作成した 100 回の計測結果の平均値を出力するプログラムを実行し,各距離にお
ける出力電圧の平均値を得る.例えば以下のような値になる (この値はあくまでも例である.各自の環境に
よって異なる).
• 10cm: 2.51V
• 15cm: 1.62V
• 20cm: 1.23V
• 25cm: 0.94V
4. 得られた結果からパラメータ a, の値を算出する.最小二乗法により係数 a, b の値を算出する excel ファイ
ルを配布するので,各自,a, b の値を求めよ.
5. 求めた a, b の値を用いて,距離をシリアル通信で出力するプログラムを作成せよ.テンプレートを以下に
示す.___ に適切なソースコードを記述すればよい (注意: _ の数は実際の文字数とは一致しない).
6. 作成したプログラムによって,キャリブレーションに用いた距離以外に物体を置いたときも,正しく距離が
計測できていることを確認する.
20
図 12
キャリブレーションの様子
リスト 8 距離計測
1
2
const int sensorPin = A0;
const int wait = 30;
3
4
float convertToDistance(int val)
5
{
const float a = ______;
const float b = ______;
float v = val * _________; // v は電圧
float d = _______________; // 電圧から距離に直す (式 1を使う)
return d;
6
7
8
9
10
11
}
12
14
void setup() {
Serial.begin(9600);
15
}
13
16
21
void loop() {
int val = analogRead(sensorPin);
float distance = convertToDistance(val);
Serial.println(distance);
delay(wait);
22
}
17
18
19
20
21
基本的な信号処理手法
8
センサから得られる信号に基づいて各種の処理を行う場合に必要となる,信号処理の基本的な知識を学ぶ.
題材として,近付いたらライトが付くという人感センサを簡易的に実装する.ライトの代わりに,11 番ピンに
接続した外付けの LED が点灯するものとする.
8.1
距離に応じて LED が点灯・消灯
■■■ 演習: 距離に応じて LED が点灯・消灯 ある距離よりも近くに物体がある場合に,LED が点灯するプログ
ラムを作成せよ.このとき,距離は cm 単位で表すものとし,任意の閾値を設定できるようにせよ.テンプレート
を以下に示す.
閾値とは
一般的に,二つの状態が何らかの値に応じて切り替わる場合の,境界となる値を閾値と呼ぶ.「しきいち」あ
るいは「いきち」と読む.
リスト 9 距離に応じて LED を点灯・消灯
1
2
3
const int sensorPin = A0;
const int ledPin = 11;
const int wait = 30;
4
5
const float distanceThreshold = 20.0;
6
7
float convertToDistance(int val)
8
{
// [TODO] 前節で記述したコードをここに書く
9
10
}
11
14
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
15
}
12
13
16
17
18
19
void loop() {
int val = analogRead(sensorPin);
float distance = convertToDistance(val);
20
// [TODO] distance の値に応じて LED を点灯・消灯させる
// if と digitalWrite()を使う.
21
22
23
Serial.println(distance);
delay(wait);
24
25
26
8.2
}
閾値近辺での挙動の安定化: ヒステリシスの導入
前節のプログラムでは,閾値近辺に手をかざした場合,LED が不規則に点滅する場合がある.センサには計測
誤差があり,また手の微妙な動きによっても距離が変動するため,閾値近辺では挙動が安定しない.計測された距
離と,閾値を 20cm に設定した LED の状態の時間変化を図 13 に示す.
この問題を解決するためには,LED の状態が ON から OFF になる閾値と OFF から ON になる閾値に,ヒステ
リシスと呼ばれる変化方法に応じた差を設ける.設定したい距離を d0 とした場合に,Off から On の場合には,距
22
23
distance
on/off
22
distance [cm]
21
20
19
18
17
0
500
1000
1500
2000
2500
3000
3500
time [ms]
図 13 閾値近辺で状態が不安定になる例
離の値が小さくなる方向に変化するので,閾値 1: d0 − ∆ を使い,On から Off の場合には,距離の値が大きくなる
方向に変化するため,閾値 2: d0 + ∆ を使う.∆ の適切な大きさは問題により異なる.
■■■ 演習: ヒステリシスの導入 上記のヒステリシスを導入した閾値処理を実装せよ.テンプレートを以下に
示す.
■プログラム作成のヒント 現在の状態によって,異なる閾値を用いることから,プログラムには,現在の状態を
保持する変数を追加する必要がある.テンプレートでは int 型の変数 state を用意している.state には HIGH
と LOW のいずれかが格納され,HIGH の時が点灯,LOW の時が消灯とする.
リスト 10 ヒステリシスを導入した閾値処理
1
2
3
const int sensorPin = A0;
const int ledPin = 11;
const int wait = 30;
4
5
6
const float distanceThreshold = 20.0;
const float delta = ______; // [TODO] 適切な値を設定
7
8
int state;
9
10
float convertToDistance(int val)
11
{
// [TODO] 前節で記述したコードをここに書く
12
13
}
14
15
16
17
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
18
state = LOW;
19
20
}
21
22
23
24
void loop() {
int val = analogRead(sensorPin);
float distance = convertToDistance(val);
25
26
27
28
// [TODO] state と distance の値に応じて LED を点灯・消灯させる
// if と digitalWrite()を使う.
// state の更新も必要.
29
23
Serial.print(millis());
Serial.print(" ");
Serial.print(distance);
Serial.print(" ");
Serial.println(state);
delay(wait);
30
31
32
33
34
35
}
36
ソースコードに新しく登場した関数は以下の通り.
• millis(): 起動からの経過時間をミリ秒で返す.
• Serial.print(): 改行しないでシリアル通信に出力する.
8.3
挙動の安定化 2: ノイズ除去 (この項は全て発展課題)
ヒステリシスの導入によって,閾値近辺での挙動が安定したが,状態の判定には依然としてセンサの生の値を用
いているため,突発的なノイズに対しては不安定であることには変わりがない.
この問題に対応するためには,複数のサンプル値を用いてフィルタ処理を行うことが有用である.画像処理に
おけるノイズ除去と同様の手法が効果的である.ここでは,移動平均 (moving average) と中央値 (median filter) を
試してみる.
8.3.1
移動平均
移動平均のうち最も基本的なものが単純移動平均である.単純移動平均とは,直近の n 個のサンプルの平均で
ある.計測値を xi とすると,単純移動平均の出力 yi は,
i
1 ∑
xj
n j=i−n+1
(6)
1
(xi−4 + xi−3 + xi−2 + xi−1 + xi )
5
(7)
yi =
となる.例えば n = 5 とすると,
yi =
である.よって,最近の複数のサンプル値を保持しておき,その都度平均を求めればよい.
より効率を追求すると,サンプルごとに総和の計算を行う必要は無いことに気付く.yi−1 を考えると,
i−1
1 ∑
xj
n j=i−n
yi−1 =
であるから,辺々引いて整理すると,
yi = yi−1 +
1
(xi − xi−n )
n
(8)
(9)
となる.長い区間の移動平均を考える場合には検討する価値がある.
8.3.2
中央値
移動平均は処理が単純で実装も容易であるが,極端に大きい/小さい値が突発的に計測された場合に,その影響
を一定時間受けるという問題がある.これに対応する方法として,移動平均と同じように一定時間内のサンプル
を保持しておき,その中央値を採用する手法があり,中央値 (median) フィルタと呼ばれる.
一定区間内の中央値を得るためには,区間内の値を保持し,ソート (並べ替え) を行った後,n/2 番目の値を使う
のがよい.arduino にはソート関数は提供されていないので,各自,ソートアルゴリズムを調べて実装せよ.
24
8.4
更なる発展課題
• (節電) 点灯してから一定時間経過後に消灯するようにする.消灯した場合,点灯距離内に物体がある場合
は,一定距離以上移動すれば再度点灯する.
• (誤検出対応) 点灯するタイミングを,点灯距離内に物体が入ってから一定時間経過後にする.
25
Processing との連携
9
センシングした結果を,Arduino を経由して PC に送信し,PC 側のプログラムで使用することができれば,活
用の幅が大きく広がる.シリアル通信によって情報を送付することは学習済みであるので,PC 側のプログラムを
適切に作成することによって可能となる.ここでは,Processing を使用してプログラムを作成する.
9.1
通信の設計
Arduino と PC(Processing) の間で通信を行うにあたっては,通信の方法を定めておかなければならない.
• データはバイナリで送るか? テキストで送るか?
• データは Arduino から連続的に送り続けるか? PC からの要求があったときのみ送るか?
バイナリで送る場合,関数 Serial.write() を使用する.1 バイトのデータを送る場合:
1
2
int val = analogRead(sensorPin);
Serial.write(val);
この場合,val の値が 0 以上 255 以下である必要がある.
テキストで送る場合は,既に何度か行っているように,関数 Serial.println() を使用する.
1
2
int val = analogRead(sensorPin);
Serial.println(val)
テキストの方が人間にとっての可読性が高い (シリアルモニタで値を確認するのが容易) が,通信量が増える点
は留意する必要がある.例えば val の値が 120 の場合に送信されるデータは,
• バイナリで送信: 120 (1 バイト)
• テキストで送信: ’1’(=49) ’2’(=50) ’0’(=48) 13 10 (5 バイト)
テキスト送信末尾の 13 10 は改行コード (CR LF) である.
と差がある.例えば,計測を 1 秒間に 100 回行いたい場合,シリアル通信を 9600bps(bps は Bit Per Second の略.
9600 ビット/秒=1200 バイト/秒) で行うと,1 回の送信での最大通信量は 12 バイトであるため,複数の計測結果
を送信する場合,テキストでは帯域が不足する可能性がある.
本節では最も単純な 1 バイトのバイナリ通信を行う.
9.2
Arduino 側のプログラム
ここでは,距離センサから得られた距離情報を,cm 単位の整数に直し (これで 1 バイトに収まる),1 バイトず
つバイナリ送信するものとする.
リスト 11 Arduino と PC で通信: Arduino 側
1
2
const int sensorPin = A0;
const int wait = 30;
3
4
float convertToDistance(int val)
5
{
// [TODO] 以前記述したコードをここに書く
6
7
}
8
10
void setup() {
Serial.begin(9600);
11
}
9
12
26
図 14 Processing の起動
18
void loop() {
int val = analogRead(sensorPin);
float distance = convertToDistance(val);
byte distanceByte = byte(distance);
Serial.write(distanceByte);
delay(wait);
19
}
13
14
15
16
17
※ 8.3 節のノイズ除去を実装した人は,上記プログラムに加えてみよ.
■ソースコードの説明
byte 型は符号無し 1 バイト整数で,C 言語の unsigned char 型に相当する.Arduino では
unsigned char 型も使用できるが,byte 型を使うことが一般的である.
変数 distance に代入するときに使用している関数 byte() は,型名と同じであるが,こちらは型の変換を行う
関数である.C 言語のキャストに相当する.
送信は関数 Serial.write() で行うことは既に述べた.引数に byte 型を与えることで,1 バイトのバイナリ送
信が行われる.
9.3
Processing 側のプログラム
Processing の方では,受け取った値に応じて,さしあたり円の大きさを変えるようにしてみる.
Processing の起動は,スタートボタン ▷3. マルチメディア ▷processing により行う (図 14).統合開発環境の
Processing Environment が起動する.見た目は非常に Arduino IDE と似ている.アイコン一番左端のボタン (右向
き矢印) でプログラムが実行される.
Processing に関連する情報は,本家のウェブサイトが詳しいが,やはり英語である.ただそれほど難しくはない
しサンプルを見れば意味がわかるので,英語を参照する練習としてもよい題材であろう.
• リファレンス: https://processing.org/reference/
• ライブラリ: https://processing.org/reference/libraries/
特にシリアル通信関係: https://processing.org/reference/libraries/serial/index.html
• サンプル: https://processing.org/examples/
また,Processing Environment のメニュー File ▷Examples... を開くと,大量のサンプルプログラムがすぐ実行で
きるようになっている.適宜実行してみると楽しい.
27
日本語の各種情報はウェブを適宜検索せよ.
リスト 12 Arduino と PC で通信: Processing 側
1
import processing.serial.*;
2
3
4
Serial port;
int val;
5
6
7
void setup() {
size(512, 512);
8
printArray(Serial.list());
String arduinoPort = Serial.list()[0];
port = new Serial(this, arduinoPort, 9600);
val = 0;
9
10
11
12
13
}
14
15
16
17
18
19
20
21
void draw() {
background(255, 255, 255);
stroke(64, 64, 64);
fill(0, 64, 255);
int radius = (60 - val) * 8;
if (radius < 0) {
radius = 0;
}
22
ellipse(width / 2, height / 2, radius, radius);
23
24
}
25
26
void serialEvent(Serial p)
27
{
val = p.read();
28
29
}
■ソースコードの説明
Processing 側のソースコードについて,解説する.
• プログラム全体の構造は,Arduino と似ている.初期化は関数 setup() を定義して行う.描画処理は関数
draw() で行う.関数 draw() は適宜自動的に呼び出されるため,draw() 内でループを回す必要は無い (特
に無限ループを回してはいけない).
• 1 行目はシリアル通信を行うために必要である.ライブラリを import する.
• シリアル通信は Serial クラスを利用して行う.3 行目の Serial port; により宣言する.
• Serial.list() は PC のシリアルポートの一覧を得る関数であり,printArray() によってコンソール
(IDE の下の黒い部分) に表示される.Arduino が接続されているポートが何番目であるかを確認し,次の
行の Serial.list()[0] の添字を適切に変更する.例えば
1
2
3
[0] "COM1"
[1] "COM3"
[2] "COM6"
と表示されて,Arduino が接続されているポートが COM6 であれば,0 を 2 に変更する.
• 次の new Serial(...) でシリアルクラスのインスタンスを生成.
• シリアル通信でデータが送られてくると,関数 serialEvent() が呼び出される.引数としてシリアルポー
トが与えられる.p.read() で値を得る (バイナリの場合).
• 描画関係のソースコードをまとめて解説する.
– setup() 中の size() は開くウィンドウのサイズを指定する.
– 変数 width および height はウィンドウのサイズである.自動的に宣言され,自動的に値が設定さ
れる.
28
– background() で背景を初期化する.色は順に RGB である.
– stroke() で線の色を指定する.線を描きたくない場合は noStroke() を指定する.
– fill() で塗り潰しの色を指定する.塗り潰したくない場合は noFill() を指定する.
– ellipse() は楕円を描く関数で,中心の X 座標,Y 座標,X 方向直径,Y 方向直径である.
■■■ 演習: Arduino と Processing の通信 上記二つのプログラムを実行して Arduino と Processing 間で通信を
行い,手とセンサの距離に応じて円の大きさが変わることを確認する.
■■■ 演習: バリエーション 円の大きさを変える代わりに,(1) 色を変える (2) 位置を変える (3) 円ではなく
矩形 rect() を描画する等の変更を加えてみる.rect() については以下を参照.https://processing.org/
reference/rect_.html 他,適宜自由な発想で行うこと.
9.4
複数バイトからなる情報を送る
Serial.println() を使用して送信されたテキスト情報を Processing で受けるには,readStringUntil() を
使用する.
■■■ 演習: [発展] 1 バイトのバイナリ通信では,小数点以下の距離情報が失われてしまう.Arduino 側
では Serial.println() で float 型の距離を小数点 2 桁で送信し,Processing 側では serialEvent() 中で
readStringUntil(10) を使用して数値を文字列として得て,float() を用いて float 型に変換する.値を格納す
る float 型の変数は,適宜グローバル変数として宣言せよ.
9.5
発展課題
• 送信されてくる値の時間変化をグラフとして表示せよ.Processing で配列を使いたい場合,例えば 100 要
素の int 型配列は以下のように宣言する.
1
int[] array = new int[100];
参照: https://processing.org/reference/Array.html
• Processing のサンプル (メニュー File ▷Examples...) の一つを,手とセンサの距離を使用するように変更し
てみよ.例えば Topics ▷Simulate ▷Spring は相性がよさそうだ.
29
様々なセンサ
10
距離センサだけでは飽き足りない人向けに,様々なセンサを使用するための案内を記す.
センサの出力様式としては,主に以下の種類がある.
• 電圧変化
• 電流変化
• 抵抗変化
• 静電容量変化
• パルス出力
既に使用した距離センサは,測定値に応じて電圧が変化するものであり,その変化範囲も Arduino での A/D 変
換に適したものであった.以下で紹介する加速度センサもこのタイプである.
曲げセンサや光センサ (CdS セル) は,測定値に応じて抵抗が変化する.センサに単に電圧をかけるだけでは抵
抗の変化を捉えることはできないため,別の抵抗をセンサと直列に接続して基準電圧を分圧することで,電圧の変
化が生じ A/D 変換可能になる.
10.1
加速度センサ
3 軸加速度センサモジュール KXM52-1050 を使ってみる.データシートはこちら: http://akizukidenshi.
com/download/KXM-52.pdf *1
仕様に従って,センサモジュールの各ピンと Arduino を,以下のように接続する.
• 1 Vdd: 電源入力 Arduino 3.3V に接続 (5V ではないので注意)
• 2 PSD: パワーシャットダウン 1 番ピン (Vdd) と接続
• 3 GND: Arduino GND に接続
• 4 Parity: どこにも接続しない
• 5 SelfTest: 3 番ピン (GND) と接続
• 6 OutX: X 軸加速度出力 Arduino A0 に接続
• 7 OutY: Y 軸加速度出力 Arduino A1 に接続
• 8 OutZ: Z 軸加速度出力 Arduino A2 に接続
配線にはブレッドボードを利用する.ブレッドボードはハンダ付け不要で回路を組むことができるため試行錯
誤には非常に便利である.ボード内部でピン間が電気的に接続されている (図 15).
センサモジュールのピン配置を図 16 に示す.また実体配線を図 17 に示す.センサモジュール上に一部のピン
番号が印刷されているので,確認しながら接続する.
このセンサは最大 ±2G まで計測できる (G は重力加速度).推奨されている電源電圧 3.3V で使用した場合,0G
で 1.65V(ちょうど電源電圧の半分) の出力となる.感度は 0.66V/G である.以上を踏まえると,アナログピンか
らの入力 (0-1023) を,加速度に変換する式は,以下のようになる.
a = (val * 5.0 / 1024 - 1.65) / 0.66;
静止状態では,Z 軸方向に重力がかかっている他,X 軸 Y 軸の両方向も完全に 0 にはならない.この誤差を取
り除くためには,起動時に何サンプルか計測し,平均を零点として使用するのがよい.サンプルプログラムを以下
に示す.
リスト 13 加速度センサ: 最初の 10 サンプルの平均との差を出力
*1
現在販売されていない.後継は KXR94-2050.
30
省略していますが
途中も同様です
図 15
ブレッドボード
1 Vdd
8 OutZ
2 PSD
7 OutY
3 GND
6 OutX
4 Parity
5 SelfTest
図 16 3 軸加速度センサのピン配置
図 17
加速度センサと Arduino の接続
31
1
2
3
4
const
const
const
const
int
int
int
int
xAccPin = A0;
yAccPin = A1;
zAccPin = A2;
wait = 30;
5
6
float convertToAcceleration(float val)
7
{
float a;
a = (val * 5.0 / 1024) / 0.66;
return a;
8
9
10
11
}
12
13
float xAverage, yAverage, zAverage;
14
15
16
void setup() {
Serial.begin(9600);
17
26
const int numAverage = 10;
int xSum = 0;
int ySum = 0;
int zSum = 0;
for (int i = 0; i < numAverage; i++) {
xSum += analogRead(xAccPin);
ySum += analogRead(yAccPin);
zSum += analogRead(zAccPin);
delay(wait);
27
}
28
xAverage = float(xSum) / numAverage;
yAverage = float(ySum) / numAverage;
zAverage = float(zSum) / numAverage;
18
19
20
21
22
23
24
25
29
30
31
}
32
45
void loop() {
int x = analogRead(xAccPin);
int y = analogRead(yAccPin);
int z = analogRead(zAccPin);
float xAcc = convertToAcceleration(float(x) - xAverage);
float yAcc = convertToAcceleration(float(y) - yAverage);
float zAcc = convertToAcceleration(float(z) - zAverage);
Serial.print(xAcc, 3);
Serial.print(" ");
Serial.print(yAcc, 3);
Serial.print(" ");
Serial.println(zAcc, 3);
delay(wait);
46
}
33
34
35
36
37
38
39
40
41
42
43
44
計測時の誤差の除去には,移動平均や中央値フィルタによるノイズ除去も有効である.
10.2
曲げセンサ・光センサ
曲げセンサや光センサ (CdS セル) は,前述の通り,測定結果が抵抗値に反映されるため,別の固定値の抵抗と
直列に接続し,定電圧を分圧することで抵抗値を計測する.ここでは曲げセンサを題材にして説明する.
曲げセンサの抵抗を Rb ,直列に接続する抵抗を R0 とする.電圧 V を印加すると,曲げセンサでの電圧降下 Vb
は,
Vb =
Rb
V
Rb + R0
(10)
となる.Rb の変化と Vb の変化は比例関係にはなく,またそもそも抵抗が計測対象の物理量と比例するとは限ら
ないため,定量性を得るためにはキャリブレーションが必要である.
32
図 18
曲げセンサの使用
曲げセンサの使用時の実体配線を図 18 に示す.
曲げセンサの抵抗は,曲げ無し時に約 10kΩ,曲げると最大約 20kΩ である.CdS セルの抵抗は,光無しで約
300kΩ,光ありで小さくなり,手で遮った程度だと約 20kΩ,ライトを近接させて照射すると 100Ω 以下になる.
いずれも 20kΩ 程度の抵抗を直列に接続する程度がよいだろう.
33