前回に引き続き乱数生成の方法について確認します。
Arduinoの乱数初期化の考え方
最近はあまり機会がないと思いますが、ダイヤル式のラジオを聞いたことはありますでしょうか。ラジオといってもインターネットラジオではなく、電波を受信して聞くラジオです。
そのようなラジオで番組を聞くとき、ダイヤルを回して周波数を合わせるわけですが、周波数が合っていないと「ザーッ」という雑音が聞こえてきます。このような電気的な雑音はノイズと呼ばれています。
また、ダイヤル式のラジオを聞いたことがない方でも、電子レンジを使っているときにネットの調子が悪くなったりというような経験はないでしょうか。これも電子レンジから漏れている電気的なノイズ(電磁波)が無線通信に影響してしまっているためです。
このように身の回りには電気的な雑音、つまりノイズがたくさん飛び交っているんです。
乱数とは全然関係ない話から始まってしまいましたが、Arduinoの乱数初期化では、このノイズを利用します。
実際にノイズを測定してみると一定の値ではありません。測定するたびに毎回違う値が測定されます。そこで、Arduinoでノイズの値を読み取って、その読み取った値を randomSeed命令の引数にしてしまおう、というわけです。
Arduinoの乱数初期化の定番は、以下のようにノイズを測定して、その値で乱数を初期化する、という方法になります。
randomSeed( 読み取ったノイズの値 );
ということで、今回はArduinoでノイズを測定してみます。
電圧を読み取るanalogRead命令
「ノイズを測定する」と説明しましたが、具体的にはノイズの「電圧」を測定します。
Arduinoには「ノイズを測定する」という命令はありませんが、「電圧を測定する」という命令があります。そこで、これからこの「電圧を測定する」命令を使ってノイズを測定してみます。
Arduinoでは、ピンの電圧を読み取るために、以下の命令が用意されています。
このanalogRead命令を使うときは3点注意があります。
1点目は、この命令を使用する前に、pinMode命令でピンの設定は必要ありません。
2点目は、引数の「ピン名」は、Arduinoボードに書かれているピンの名称を指定できます。例えば、ブレッドボードの5列目のピン名は以下のように「A1」と書かれています。
このピンの電圧を読み取る場合は以下のように書きます。
analogRead(A1);
3点目は、「電圧を読み取る」といっても「何ボルト」と読み取るわけではなく、0〜1023の数字で読み取ります。実際の電圧は1023が5Vに対応しています。例えば読み取った数字が半分の512であれば、そのときの電圧は半分の2.5Vということになります。
このようにanalogRead命令を使用するとピンの電圧を読み取ることができるわけですが、もう一つ非常に重要なことがあります。
現在のブレッドボードの回路は、以下のようにA1ピンやA2ピンは何も接続されていません。
このとき、A1やA2ピンの電圧を読み取ると、その時の数字はいくつになると思いますか? 何も接続されていないのだから、0V、つまり読み取った数字は0でしょう、と思われませんか?
実は、何も接続されていないピンの電圧は不定なんです。少なくとも0Vではありません。
電子回路に慣れていないと、ちょっと???と思われるかもしれませんが、どこにも接続されていないピンや電線の電圧は決まっていないんです。では、どのぐらいの電圧になるのかというと、、、その時々で変わるんです。ノイズなどの外部の影響を受けてしまうためです。
ということで、これからA1ピンの電圧を読み取ってみましょう。
ノイズを測定してみよう
A1ピンはどこにも接続されていませんので、analogReadで読み取った値は不定になります。
ぞれでは、新規スケッチを作成して以下のスケッチをコピペして、Arduino Microに書き込んでください。
/*
* ピンの電圧を読み取る
*/
void setup() {
// シリアルモニタ初期設定
Serial.begin(9600);
while(!Serial){
}
}
void loop() {
//0.2秒ごとに読み取った電圧値をシリアルモニタ/シリアルプロッタに表示
Serial.println( analogRead(A1) );
delay(200);
}
書き込んだら、シリアルモニタのウィンドウを開いてください。以下のように読み取った数字が次々に表示されます。
ところで、数字が出てくるだけですとあまりよく分かりませんので、グラフでみてみましょう。
一度、シリアルモニタのウィンドウを閉じてください。
次に、Arduino IDEの「ツール」メニューから「シリアルプロッタ」を選択してください。この「シリアルプロッタ」はArduino Microから受け取った数字を文字で表示するのではなく、グラフにしてくれます。
しばらく待っているとデータが溜まってきますので、以下のように変化しているのが分かります。
数値が変化するのを確認したら、次はブレッドボードの置いてある位置を変更してみてください。30cm程度違うところに置いてみましょう。するとグラフが変化すると思います。
さらに、Arduino Microに触れないように、手のひらで覆ってみたり、顔を近づけてみたりしてみてください。波形が乱れたりするのが確認できると思います。
このように、身の回りで発生しているノイズは場所によっても、周りの環境によっても、また時間によっても変わってきます(太陽の影響も受けますので、昼と夜で違うこともあります)。
このように何も接続されていないピンの電圧をanalogReadで読み取ると、適当な数字を取得できることがわかりました。
乱数の初期化にはこのように毎回異なる数字になることが期待されるanalogReadを取得した値を使います。
ということで、今までの内容をまとめると、Arduinoの乱数の初期化は定番の書き方は以下のようになります。
randomSeed( analogRaed(A1) );
スケッチのsetup関数にこのように書くと、乱数の初期化ができます。
再度、乱数発生スケッチ
それでは、前回の乱数発生スケッチに対して、乱数初期化の命令を追加して動作を確認しましょう。
random命令で乱数を発生させる前に、randomSeed命令で乱数を初期化すればOKです。スケッチは以下になります。何回かスケッチを動かして、結果を確認してみてください。毎回異なる適当な数字が30個表示されます。
/*
* random関数の動作確認用スケッチ
* 今回は、randomSeedで乱数の初期化をする
*/
void setup() {
// シリアルモニタ初期設定
Serial.begin(9600);
while(!Serial){
}
// 乱数の初期化
randomSeed( analogRead(A1) );
// 乱数を30個生成してシリアルモニタに表示する
for(uint8_t count=0; count<30; count++){
Serial.println( random(10) );
}
}
void loop() {
}
乱数はanalogReadでいいのでは?
analogRead命令を使って、接続されていないピンの電圧を読み取ると適当な数字が取得できることがわかりました。
ところで、random命令では乱数を求めるのに計算していましたので、乱数を初期化する必要がありました。こんなややこしいことをするのであれば、analogRead命令で数字を取得して、それを乱数としてしまえばよいのではないか、と思われた方もいるかもしれません。
確かに乱数っぽいですが、残念ながら乱数には使えません。乱数は、全ての数字が均等な確率でランダムに出現する必要があります。
先ほどの私の環境で動作確認したシリアルプロッタのグラフを見ると、規則性がありますよね。これはおそらく何か周期的な信号を受信してしまっているためです。このようにノイズといってもパターンが出てくるケースもあります。
また、ノイズといっても全ての数字が均等な確率で出てくるとも限りません。
ということで、Arduinoの世界では、analogReadで初期化の数字を決めて、randomSeed命令で乱数の初期化、random命令で乱数を取得するのが一般的になっています。
ところで、普通のPCやスマホなどはどのように乱数を初期化しているのでしょうか。
これはプログラマによりますが、例えば現在の時刻をミリ秒で取得して、その時刻の数字を使用して乱数を初期化したりしています。また、他にもセンサの値が取得できる状況であればセンサの値を使用して乱数の初期化を行うことも可能です。
ということで、世の中、乱数を発生させるのには意外に苦労しています。
ミニチャレンジ課題
乱数の初期化は、randomSeed(analogRead(A1));、乱数を生成するにはrandom(数値);ということがわかりましたので、次のミニチャレンジ課題に挑戦してみてください。
ブレッドボードにLEDが4個ありますので、LEDおみくじのスケッチを作ってみてください。
動作としては、スイッチを押すとどれか1個のLEDが点灯するようにしてください。点灯したLEDにより例えば以下のような結果になる、というイメージです。
LEDの色 | 結果 |
---|---|
青 | 大吉 |
緑 | 吉 |
黄 | 末吉 |
赤 | 凶 |
更新履歴
日付 | 内容 |
---|---|
2021.11.8 | 新規投稿 |