Raspberry Piでロータリーエンコーダを使って回転速度を計測しよう
こんにちは、タクト工房へようこそ!前回、DCモータをモータドライバを使用して回転させました。今回は、Raspberry Piとロータリーエンコーダを使ってDCモーターの回転速度を計測する方法を解説します。ロータリエンコーダはモーターの回転速度や回転方向を検出するために使われるセンサーで、ロボットやスマートカーの精密な制御に役立ちます。
↓前回の記事
今回使用するもの
今回使用するものは前回と同じです。
Raspberry Pi
モータドライバ(L298N)
エンコーダ付きDCモータ
ジャンパーワイヤー
バッテリーまたは外部電源
ブレッドボード
ロータリーエンコーダとは?
ロータリーエンコーダは、回転運動を検出し、モーターや回転軸の角度や回転速度を計測するために使用されるセンサーです。回転角度や回転数をパルス信号(オンとオフを繰り返す信号)として出力し、制御システムにフィードバックを提供する重要な役割を担います。
ロータリーエンコーダの種類
ロータリーエンコーダには大きく分けて、次の2種類があります:
インクリメンタルロータリーエンコーダ:回転ごとにパルスを生成し、そのパルス数をカウントすることで、回転速度や位置を計測します。絶対的な位置は計測できず、電源を切るとカウントがリセットされます。
アブソリュートロータリーエンコーダ:回転軸の絶対的な角度を計測することができ、電源を切っても位置情報が保持されます。再起動後でも正確な位置がわかるため、正確な位置制御が必要な場面に適しています。
今回は、インクリメンタルロータリーエンコーダを使用します。
インクリメンタルロータリーエンコーダ
インクリメンタルエンコーダは、回転軸が回るたびにパルス信号を生成します。これらのパルス信号は、回転数や回転方向を計測するための基本情報となります。エンコーダが一回転する時に生成するパルス数が多いほど、回転の角度を細かく測定することができます。
A相とB相の信号
多くのインクリメンタルロータリーエンコーダは、A相とB相の2つの信号を出力します。この2つの信号は、90度の位相差を持って出力されるため、回転方向(正転・逆転)を判定することができます。
A相:パルス信号の1つ目。
B相:A相に対して90度位相差のあるパルス信号。
下図のようにA相とB相の信号の立ち上がり・立ち下がりのタイミングを比較することで、モーターや回転軸がどちらの方向に回転しているかを検出します。
車輪の回転速度の計算
エンコーダを使って、車輪の回転速度(RPM:1分間に回転する回数)を計算する方法を説明します。車輪のRPMは次の式で求められます。
$$
\text{RPM} = \frac{\text{パルス数} \times 60}{\text{PPR} \times \text{ギア比} \times \text{計測時間(秒)}}
$$
PPR(Pulses Per Revolution):エンコーダが1回転する間に発生するパルスの数(分解能)を表します。例えば、PPRが20であれば、モーターが1回転する間に20回のパルスが出力されます。
パルス数:実際に計測時間内にカウントされたパルスの数です。
ギア比:モーターがギアを通して動いている場合、そのギア比を考慮する必要があります。たとえば、ギア比が1:15の場合、モーターが15回転すると車輪が1回転するので、RPMも1/15に調整されます。
計算例
1秒間に60パルスがカウントされ、エンコーダの分解能が20 PPR、ギア比が1:15の場合、車輪の回転速度(RPM)は次のように計算できます:
$$
\text{RPM} = \frac{60 \times 60}{20 \times 15 \times 1} = 12 \, \text{RPM}
$$
この計算によって、車輪の回転速度を正確に求めることができます。
エンコーダで車輪の回転速度を計測
パルス数とギア比
今回使用するエンコーダ付きDCモータのパルス数とギア比は以下の通りです。ギア比の記載がなかったため、実際にギアの歯を数えて導出しました。
パルス数 11パルス
ギア比 1:15.27
配線
下図のようにRaspberryPiとエンコーダ付きDCモータを繋げます。
プログラム1
プログラム1では手動で車輪を回して計測します。10秒ごとに計測し、計測結果を表示させます。従って、10秒で車輪を1回転させると、1分間で6回転となるので、RPMは6と計測されるはず!
#include <stdio.h>
#include <wiringPi.h>
#define ENCODER_A_PIN 17 // エンコーダのA相ピン
#define ENCODER_B_PIN 27 // エンコーダのB相ピン
#define PPR 11 // エンコーダの1回転あたりのパルス数
#define GEAR_RATIO 15.27 // ギア比
#define delay_time 10 // 待機時間
volatile int pulse_count = 0; // パルスをカウントするための変数
volatile int direction = 1; // 回転方向(1: 正転、-1: 逆転)
// パルスが検出されたときに呼ばれる関数
void pulseCounter() {
// A相の立ち上がりエッジでのB相の状態により方向を判定
if (digitalRead(ENCODER_B_PIN) == HIGH) {
direction = 1; // 正転
} else {
direction = -1; // 逆転
}
pulse_count += direction; // 回転方向に応じてカウントを増減
}
int main(void) {
// GPIOの初期化
if (wiringPiSetupGpio() == -1) {
printf("GPIOのセットアップに失敗しました\n");
return 1;
}
// エンコーダのA相とB相ピンを入力モードに設定
pinMode(ENCODER_A_PIN, INPUT);
pinMode(ENCODER_B_PIN, INPUT);
pullUpDnControl(ENCODER_A_PIN, PUD_UP);
pullUpDnControl(ENCODER_B_PIN, PUD_UP);
// 割り込み設定(A相の立ち上がりエッジでパルスをカウント)
if (wiringPiISR(ENCODER_A_PIN, INT_EDGE_RISING, &pulseCounter) < 0) {
printf("ISRの設定に失敗しました\n");
return 1;
}
while (1) {
// 10秒間のパルスカウントをリセット
int start_pulse_count = pulse_count;
delay(delay_time*1000); // 10秒間待機
int end_pulse_count = pulse_count;
// 10秒間にカウントされたパルス数を計算
int pulses = end_pulse_count - start_pulse_count;
// モーター軸の回転数を計算
double motor_rpm = (double)abs(pulses) * 60 / (PPR*delay_time);
// 出力軸(車輪など)の回転数
double output_rpm = motor_rpm / GEAR_RATIO;
// 回転方向の表示
if (pulses > 0) {
printf("回転方向: 正転\n");
} else if (pulses < 0) {
printf("回転方向: 逆転\n");
} else {
printf("回転方向: 停止\n");
}
printf("出力軸のRPM: %.2f\n", output_rpm);
}
return 0;
}
実行結果1
以下のように10秒間で車輪を一周させるとRPMが6付近の値になりました!また、反対に回すと、回転方向が逆転していることが読み取れました。
プログラム2
つぎに、実際にモータに電圧をかけてモータを回したときの車輪のRPMを計測したいと思います。
#include <stdio.h>
#include <wiringPi.h>
#define IN1 23 // GPIO 23(モータードライバのIN1ピン)
#define IN2 24 // GPIO 24(モータードライバのIN2ピン)
#define ENA 18 // GPIO 18(モータードライバのENAピン、PWM出力)
#define ENCODER_A_PIN 17 // エンコーダのA相ピン
#define ENCODER_B_PIN 27 // エンコーダのB相ピン
#define PPR 11 // エンコーダの1回転あたりのパルス数
#define GEAR_RATIO 15.27 // ギア比
#define delay_time 2 // 待機時間
volatile int pulse_count = 0; // パルスをカウントするための変数
volatile int direction = 1; // 回転方向(1: 正転, -1: 逆転)
// A相のパルスを検出したときに呼ばれる関数
void pulseCounter() {
// B相の状態で回転方向を判断
if (digitalRead(ENCODER_B_PIN) == HIGH) {
direction = 1; // 正転
} else {
direction = -1; // 逆転
}
pulse_count += direction; // 方向に応じてカウント増減
}
// モーターの正転
void forward(int speed) {
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
pwmWrite(ENA, speed); // PWM出力で速度を制御
}
// モーターの停止
void stopMotor() {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
pwmWrite(ENA, 0);
}
int main(void) {
// GPIOの初期化
if (wiringPiSetupGpio() == -1) {
printf("GPIOのセットアップに失敗しました\n");
return 1;
}
// ピンモードの設定
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENA, PWM_OUTPUT); // PWM出力に設定
pinMode(ENCODER_A_PIN, INPUT);
pinMode(ENCODER_B_PIN, INPUT);
pullUpDnControl(ENCODER_A_PIN, PUD_UP);
pullUpDnControl(ENCODER_B_PIN, PUD_UP);
// PWM設定
pwmSetMode(PWM_MODE_MS); // マークスペースモードに設定
pwmSetRange(1024); // PWM範囲を1024に設定
int frequency = 500; // PWM周波数(500Hz)
int clockDivisor = 54000000 / (frequency * 1024);
pwmSetClock(clockDivisor); // クロック分周を設定
// 割り込み設定(エンコーダのA相パルスカウント)
if (wiringPiISR(ENCODER_A_PIN, INT_EDGE_RISING, &pulseCounter) < 0) {
printf("ISRの設定に失敗しました\n");
return 1;
}
printf("モーターをPWM制御で回転させながら、2秒ごとにRPMを表示します。\n");
// メインループ
while (1) {
// PWM値(デューティサイクル)を設定してモーターを回転
forward(512); // デューティサイクル50%(最大1024の半分)
// 2秒間パルスをカウント
pulse_count = 0;
delay(delay_time*1000); // 2秒待機
// RPMの計算
double motor_rpm = (double)abs(pulse_count) * 60 / (PPR*delay_time); // モーター軸のRPM
double output_rpm = motor_rpm / GEAR_RATIO; // 出力軸(車輪)のRPM
// 結果の表示
printf("出力軸のRPM: %.2f\n", output_rpm);
printf("回転方向: %s\n", (direction == 1) ? "正転" : "逆転");
// モーターを停止して少し待機
stopMotor();
delay(2000); // 2秒待機
}
return 0;
}
実行結果2
デューティサイクル50%では車輪は約235RPMで回転していることが確認できました。
まとめ
今回は、Raspberry Piとロータリーエンコーダを使ってモーターの回転速度を計測しました。これにより、精密な速度制御や方向検出が可能になります。