みなさん,こんにちは.
おかしょです.
この記事ではロータリーエンコーダーの使用方法とロータリーエンコーダーから角度を求める方法を解説します.
ロータリーエンコーダーというのは,回転角度や回転速度を測定するセンサーのことを言います.
これを使うことで,モーターなどの回転速度の測定がでいるので,モーターの数学モデルなどを求めることができます.
このブログでは倒立振子ロボットを作るという目標に向かって電子工作を行い,その情報を公開してきました.(倒立振子ロボットの開発状況は以下の記事を参照してください)
前回の記事で,動力となるモーターをArduinoで制御することに成功しました.これまでの記事では,電子工作をするにおいて,誰もが通るであろう基礎的なことを行ってきました.
これから解説するロータリーエンコーダーで倒立振子ロボットで使用するモーターの数学モデルを求めたいと考えています.
そのために,まずはロータリーエンコーダーの使用方法を確認します.
この記事を読むと以下のようなことがわかる・できるようになります.
- ロータリーエンコーダーの使い方
- ロータリーエンコーダーエンコーダーを使った角度の求め方
この記事を読む前に
この記事ではArduinoやブレッドボードを使用してロータリーエンコーダーの使い方を確認しています.
Arduinoの使い方が不安な方は以下の記事を先に読むようにしてください.
ブレッドボードの使用方法がわからない方は以下を参照してください.
必要なもの
この記事で使用する電子部品は以下のものになります.
- Arduino
- USBケーブル(書き込み&電源用)
- ブレッドボード
- ジャンパー線×7本
- 抵抗器(10kΩ×2個)
- ロータリーエンコーダー(EC12E2430803)
- ロータリーエンコーダーDIP化基板(AE-RECNV-3)
それぞれのロータリーエンコーダー以外の電子部品でわからないものがある場合は,以下の記事で解説しているのでそちらを参照してください.
ロータリエンコーダとは何か
そもそもロータリエンコーダとは何なのでしょうか.(ここでは,詳しい構造などについては説明しません)
ロータリエンコーダとは,正しくは”rotary encoder”と表記します.”rotary”回転の”encoder”符号器のことです.つまり,回転を符号化,プラスかマイナスかで表現します.
ロータリエンコーダは直方体の土台に円筒が取り付けられたような形をしていて,この円筒が回転します.すると,回転に合わせて+1や0,-1などが出力されます.
この出力された数字を処理することによって,円筒の回転した角度や,回転数などを算出できます.
なぜロータリエンコーダを使うのか
ロータリエンコーダとはどのようなものなのかは理解できたと思います.
では,なぜ倒立振子を自律させるためにロータリエンコーダ―を使う必要があるのでしょうか.
その理由は,ロボットを自律させるためには制御器というものを設計する必要があるからです.
制御器というのは,ロボットにどのくらいの力,もしくは電圧を与えれば姿勢を安定化できるのかを計算する頭脳のような部分を言います.前回の記事でモータをArduinoで制御しましたが,この場合はどのくらいの電圧を与えればモータの回転数を制御できるかを計算するのが制御器です.
その制御器を設計するためには,倒立振子ロボットに入力として与えられるモーターがどのくらい回転しているのかという情報を与える必要があります.
なぜこの情報が必要なのかについては,以下の記事で詳細に説明しています.
この回転数がわかるか否かで,倒立振子ができるかどうかが決まると言っても過言ではありません.そのため,ロータリエンコーダを使用して,モータの回転数を取得する必要があります.
回路を組む
それでは,ロータリエンコーダをArduinoと接続する回路を組みます.
回路は以下のようになります.
赤い線は5v出力端子と接続しています.また,プルダウン抵抗として10kΩの抵抗を接続しています.
実際に組んだものが以下になります.
あとはArduinoとパソコンをUSBケーブルで接続すれば動作するようになります.
プログラムを書きこむ
次に,ロータリエンコーダ―からデータを取り出すプログラムを書いていきます.
先に,とりあえずデータを取り出すだけのプログラムを以下に示します.
int A, B;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
}
void loop() {
A = digitalRead(2);
B = digitalRead(3);
Serial.print("A: ");
Serial.print(A);
Serial.print(", B: ");
Serial.println(B);
}
順番に解説していきます.
最初に書かれているsetup関数よりも上のところで変数の定義をします.このプログラムでは変数AとBをint型(整数型)で定義しています.
このAとBにはあとのプログラムでエンコーダーからのデータを代入します.
次にsetup関数ではpinの設定をします.Arduinoは ロータリーエンコーダーからデータを 入力されるので,INPUTピンとして設定します.この時に設定するピンはdigital読みとりができるピンだったらどのピンでも構いません.今回は2番と3番のピンにしました.
そして,loop関数ではデータを読み取り,それを変数AとBに代入,その後データを表示します.ここで使用している関数digitakReadの()の中はsetup関数でINPUTとして設定したピンの番号が入ります.
このプログラムをArduinoに書き込み,データをシリアルモニタで確認すると左のように表示されます.
ロータリエンコーダのつまみ部分を持って,回転させると以下のように表示される値が変化します.
しかし,このままではロータリエンコーダ―がどのくらい回転したのかがわかりません.そこで,ロータリーエンコーダーのつまみを360°回した時にこの値がどのように変化するのかを調べます.
そのために作成したプログラムが以下になります.
int A, B, C, D, E, F;
int count00, count10, count01, count11;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
count00 = 0;
count10 = 0;
count01 = 0;
count11 = 0;
C = 0;
D = 0;
E = 0;
F = 0;
}
void loop() {
A = digitalRead(2);
B = digitalRead(3);
if(A==0&&B==0&&C==0)
{
count00 = count00+1;
C = 1;
D = 0;
E = 0;
F = 0;
}
else if(A==1&&B==0&&D==0)
{
count10 = count10+1;
C = 0;
D = 1;
E = 0;
F = 0;
}
else if(A==0&&B==1&&E==0)
{
count01 = count01+1;
C = 0;
D = 0;
E = 1;
F = 0;
}
else if(A==1&&B==1&&F==0)
{
count11 = count11+1;
C = 0;
D = 0;
E = 0;
F = 1;
}
Serial.print("A: ");
Serial.print(A);
Serial.print(", B: ");
Serial.print(B);
Serial.print(", count: ");
Serial.print(count00);
Serial.print(", ");
Serial.print(count10);
Serial.print(", ");
Serial.print(count01);
Serial.print(", ");
Serial.println(count11);
}
このプログラムは先程のプログラムを基に作成しています.
このプログラムでは変数をA, Bに加えて,C, D, E, Fとcout00, count10, count01, count11を追加しています.
cout00, count10, count01, count11にはloop関数内でそれぞれの値が取得される回数を代入しています.
例えば,count00にはA=0, B=0というデータが得られた回数を記録します.これによって,ロータリーエンコーダーのつまみを一回転させた時にA=0, B=0というデータが何回得られたかがわかります.それぞれのデータが一回転するうちに何回出力されるのかを知ることによって,つまみを何度回転させればデータが変化するのかがわかります.つまり,データが変化するとつまみが何度回転したかが算出できるようになります.
しかし,つまみを回転させていない時はデータが変化せずに出力されることになります.データが変化していない時はカウントする必要はないので,変数C, D, E, Fを導入してカウントしないようにしています.
たとえば,現在のデータがA=0, B=oだとします.この時はcount00がプラス1されます.さらにC=1とすることで,count00にプラス1をするif文の中に入ることを防いでいます.そのほかのデータの時も同様の処理を行っています.
このようにしてロータリーエンコーダーのつまみを一回転させた時にデータが何回出力されたのかを調べた結果以下のようになりました.
上の図にあるように,count00が25, それ以外のcountは24となっています.
count00だけ1多いのは最初のデータも数えているためです,つまり,ロータリエンコーダ―のつまみを一回転させるとすべてのデータが24回カウントされる.
このことから,データが変化するとロータリエンコーダ―のつまみが何度回転したかが次式によって求められます.
$$\frac{360}{24\times4}=3.75°$$
つまり,データが変化するごとにロータリーエンコーダのつまみは3.75度回転していることになります.
このことから,ロータリエンコーダ―の角度を表示するプログラムは以下のようになります.
byte value = B00000000;
float degree = 0.00;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
pinMode(3, INPUT);
}
void loop() {
value = (value<<2)+(digitalRead(2)<<1)+digitalRead(3);
value = value & B00001111;
if(value==B00000001||value==B00000111||value==B00001110||value==B00001000)
{
degree = degree+3.75;
}
else if(value==B00000010||value==B00001011||value==B00001101||value==B00000100)
{
degree = degree-3.75;
}
if(degree>180)
{
degree = degree-360;
}
else if(degree<=-180)
{
degree = degree+360;
}
Serial.print("degree: ");
Serial.println(degree);
}
このプログラムでは少し複雑なことをしているので詳細に説明します.
まずsetup関数の前にbyte型の変数valueとfloat型の変数degreeを定義しています.
byte型では0~255までの8bitの数値を格納することができます.最初はすべてのbitが0としています.この変数にはロータリエンコーダ―のデータを代入して処理をしていきます.
float型は浮動小数点型と呼ばれ,小数が扱えます.サイズは4byte分のデータを代入することができます.しかし,float型の計算は時間がかかるため,なるべく使用は避けた方が良いです.変数degreeには初期角度0.00°を定義しています.
setup関数はこれまでと変わらず, pinの設定をしています.Arduinoは ロータリーエンコーダーからデータを 入力されるので,INPUTピンとして設定します.この時に設定するピンはdigital読みとりができるピンだったらどのピンでも構いません.今回は2番と3番のピンにしました.
次に,loop関数ではロータリエンコーダ―からのデータを処理し,現在の角度を表示しています.
最初から丁寧に説明していきます.
value = (value<<2)+(digitalRead(2)<<1)+digitalRead(3);
ここでは右辺第1項によって2bit分左にずらされた変数valueに対して,2番ピンから得られたデータを左に1bitずらしたものと3番ピンから得られたデータを連結して変数valueに代入しています.
つまり,初期設定ではvalueの値は00000000 (0が8個並んだ状態)でした.
ロータリーエンコーダから出力されたデータが2番ピンは1,3番ピンは0であったとします.
まず,右辺第1項によってvalueは左に2bit移動され,00000000 (0が8個並んだ状態) となります.もともとの値がすべてのbitで0だったので左にずれても変化はありません.左にずらすと右のbitに空きが生まれますが,そこには0が格納されます.
次に,右辺第2項によって2番ピンのデータである1が左に1bit移動されます.その結果,2番ピンのデータは00000010となります.この値が先程の第1項と足されるので,結局00000010となります.
最後に右辺第3項によって3番ピンのデータである0がさらに足されるので,変数valueの値は00000010となります.
そして,次の行で
value = value & B00001111;
とすることで,後ろの4bit分のデータのみを取り出します.
つまり,先程のデータに対してこのプログラムを実行すると00000010となります.
ここまでのプログラムでvalueを書き換えていくことによって,データの変化の仕方を調べることができます.つまり,valueの後ろの4bitの部分には前回のロータリーエンコーダーからの出力と,新たな出力が格納されていることになります.この値の変化の仕方を見ることによって,ロータリーエンコーダーが正回転しているのか,それとも逆向きの回転をしているのかを判別することが可能になります.
実際に判別しているのは以下のif文の部分になります.
if(value==B00000001||value==B00000111||value==B00001110||value==B00001000)
{
degree = degree+3.75;
}
else if(value==B00000010||value==B00001011||value==B00001101||value==B00000100)
{
degree = degree-3.75;
}
ここではデータが変化すると先程調べた,データが一度変化するあたりに3.75°回転するという結果を使用しています.
しかし,このままだとロータリエンコーダを正の方向に回転させ続けたり,もしくは負の方向に回転させ続けた時,変数degreeの値は増加,もしくはげんしょうしつづけてしまうため,角度が1000°を超えたりすることがあります.
角度の情報は
$$0\leq degree<360$$
もしくは
$$-180<degree\leq180$$
であることが望ましいです.このようにデータを処理しているのが以下の部分です.
if(degree>180)
{
degree = degree-360;
}
else if(degree<=-180)
{
degree = degree+360;
}
このように処理を行うプログラムを書き込み,実行すると,シリアルモニタには以下のように表示されます.
思った通りに-180°から180°の間で表示させることができました.
まとめ
この記事ではロータリーエンコーダーのつまみを手で回して,その角度を表示するプログラムを公開しました.
ここで解説したロータリーエンコーダーとモーターを接続して,モーターの数学モデルを構築するということもできます.
モーターの数学モデルを取得することは倒立振子ロボットを自律させるためには,必要な工程です.
続けて読む
今回解説したロータリーエンコーダーの他にもさまざまなセンサーがあります.
その多くはI2C通信という通信方式で,データの受け渡しを行います.
ロータリーエンコーダー以外のセンサーを使用したい方は,以下の記事でI2C通信について解説をしているので読んでみてください.
Twitterでは活動の進捗や記事の記事の更新情報をつぶやいているので,ぜひフォローお願いします.
それでは,最後まで読んでいただきありがとうございました.
コメント