LoginSignup
28
18

More than 5 years have passed since last update.

ArduinoでLEDの明るさを「線形に」変える

Last updated at Posted at 2017-12-10

この記事はU-TOKYO AP Advent Calendar 2017の11日目の記事です.僕はみなさん一度は触ったことがあるであろうArduinoの初歩的な内容を寄稿します.

はじめに

この記事が役立ちそうな読者は次の2パターンに大別できます.

  • ArduinoでdigitalWrite()関数を使い,Lチカを取り敢えずやってみたArduino初心者
  • analogWrite()関数を使ってLED輝度を制御してみたものの,思い通りにならないと嘆いている人

Arduinoを初めて触る方は,先にLチカを試しておきましょう.ばりばりLED光らせてるぜ!という方にも,この記事の最後に書かれている情報だけは有用かもしれません.

回路を組み立てる

LEDと抵抗,そしてArduinoを下図のように組みます1.Lチカのときにも気を付けたように,LEDが壊れるほどの大きな電流が流れないような抵抗を選びましょう.

LED回路

この際デジタルピンの番号は後述するように,"~"が付いているものを選ぶ必要があります.後に述べるスケッチをそのまま用いたければ,図の通り11番ピンを選んでおいてください.

LED輝度を変える方法

analogWrite()関数を使う

digitalWrite()関数では出力電圧をON/OFFの2値で制御しました.しかしArduinoはそれだけではなく,多段階に電圧を変えることもできます.これを行うには,analogWrite()関数を使う必要があります.

この関数はanalogWrite(pin, value)という形で使います.pinはint型の値で,電圧を出力するデジタルピン番号を指定します.例えばArduino Unoでは,3,5,6,9,10,11番ピンがこの関数に対応しています.Arduinoのボードをよく見ると,それらの数字の横には"~"マークが付いているはずです.

Arduino Uno

またvalueは0から255の範囲をとるint型の値で,0を指定すると0V,255を指定するとArduinoの電源電圧が出力されます.Arduino Unoであれば,valueに255を指定したときの出力電圧は5Vです. 一般にvalueに $v$ を指定したとき,出力電圧は $5.0 \times \frac{v}{255}$ Vになるというわけです.

PWM制御について

前パラグラフを読んで「Arduinoのデジタルピンに繋ぐ…?デジタルなんだからON/OFFしかできないやんけ!!!」と思った人はかなり鋭いところを突いています.実はArduinoでは,PWM制御(Pulse Width Modulation)という技術を使い,ONとOFFだけで出力電圧を多段階制御しています.

PWM制御では,電圧ON/OFFのスイッチングをとても早い周期で行い,1周期の間でONになっている時間を調節することで,出力電圧を調節します.特に電圧がONになっている時間を1周期の時間で割った値を,デューティー比と呼びます.この値が1ならば電圧ONのときの電圧値がそのままかかり,0ならば常に電圧OFFなので電圧は全くかからないというわけです.そして,このデューティー比が例えば0.5ならば,電圧ONのときの電圧値のちょうど半分だけの電圧が得られます.

PWM制御のグラフ

さて,analogWrite()関数の第二引数valueをもう一度見てみましょう.もうお分かりですね…!このvalueは,PWM制御のデューティー比を指定するための値だったのです!valueとして $v$ を取ったとき,デューティー比は$\frac{v}{255}$となり,これから計算される出力電圧は先ほどの式に一致します.

ちなみにvalueが0から255までの整数値を取れることから,Arduinoではデューティー比を8bit精度で制御できることがわかります.

さあ実装!

ここまでくればArduinoで自在にLEDの輝度を操ることができますね!試しに消灯しているLEDを一定の速さで徐々に明るくするスケッチを書いてみましょう.

素直な実装…?

analogWrite()関数の第二引数を0から255まで一定の速さで変化させれば,LEDにかかる電圧,ひいては電流も一定の速さで変化し,一定の速さで徐々にLEDが明るくなっていく気がします.この方針で実装してみましょう.ここでは第二引数をインクリメントする度に,50ms待つことにします.

led.ino
const int LED_PIN = 11;

void setup() {
  pinMode(LED_PIN, OUTPUT); 
}

void loop() {
  for(int i = 0; i <= 255; i++){
    analogWrite(LED_PIN, i);
    delay(50);
  }
  analogWrite(LED_PIN, 0);
  delay(1000);
}

これをArduinoに書き込んで実行したものがこの動画です.

何かがおかしいですね…等速で明るさを変えたいのに,起動直後に一気に明るくなり,明るさが最大付近に留まっている時間がやたら長い気がします.

ここまでの中で,何か見落としていることがあったのでしょうか…?

Weber-Fechnerの法則を反映する

実は,人間に対する物理的刺激量 $R$ とそれによって生じる心理的感覚量 $E$ について,次の関係式が近似的に成立することが知られています.

E = k \log R 

ここで $k$ は定数です.これをWeber-Fechnerの法則と呼びます.今考えている状況では,$R$ はLEDの輝度に,$E$ は人が知覚する光の強さにあたります.

この法則を考慮に入れて,「人が知覚する光の強さ」が等速で変化するようにしてみましょう.そのためには,LEDが出せる最大の輝度を $C$,知覚される最大の光の強さを $L$ として,その $r \;(0 \leq r \leq 1)$ 倍だけの強さの光 $Lr$ を人に感じさせたいときの,第二引数の値 $v$ を求めれば良さそうです.PWM制御のデューティー比が $d$ のときのLED自体の輝度は $Cd$ と表せるので,Weber-Fechnerの法則より

L = k \log (C\cdot 1)\\
\therefore k = \frac{L}{\log C}

が成立します.したがって,やはりWeber-Fechnerの法則より導かれる

Lr = k \log (C d)

に $k$ の式と $d = \frac{v}{255}$を代入して

Lr = \frac{L}{\log C} \log \left(C \frac{v}{255}\right)\\
\therefore v = \exp(\log 255 - (1-r)\log C)

となります.$C$ は用いているLEDの特性に依存する定数ですが,ここではスケッチ内で定数として宣言し,後で実験して良さそうな値に調整できるようにしておきます.

以上の考え方を用いて,改めてスケッチを書いてみましょう!

led.ino
const int LED_PIN = 11;
const double C = 255.0; // 用いるLEDに依存する定数

void setup() {
  pinMode(LED_PIN, OUTPUT); 
}

void loop() {
  for(int i = 0; i <= 255; i++){
    analogWrite(LED_PIN, getDuty(i/255.0));
    delay(50);
  }
  analogWrite(LED_PIN, 0);
  delay(1000);
}

int getDuty(double ratio){
  return round(exp(log(255.0) - (1 - ratio) * log(C)));
}

これをArduinoに書き込んで実行するとこの動画のようになります.

ちゃんと一定の速さでLEDの明るさが変化しているように見えますね!


明日のU-TOKYO AP Advent Calendar 2017はyuinitykさんの遺伝的アルゴリズムを使ったお話です.


  1. 蛇足ですが,この図を書くのにFritzingというフリーソフトを使いました.綺麗な回路のイラストを書くのにおすすめ. 

28
18
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
18