見出し画像

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とロータリーエンコーダを使ってモーターの回転速度を計測しました。これにより、精密な速度制御や方向検出が可能になります。

いいなと思ったら応援しよう!