Skip to content

Chapter4: 応用的な作例の制作:フィードバック制御への挑戦

これまでの章で、私たちはセンサーから情報を読み取り、アクチュエーターを動かす個別の技術を学んできました。この章では、それらの知識を統合し、より高度で実用的なシステム、「フィードバック制御」を実装する作例に挑戦します。

作例:物理的な水平器(セルフバランシング・プラットフォーム)

ここで制作するのは、傾きを検知して、自ら水平を保とうと動き続ける台(プラットフォーム)です。

  • 目的: MPU-6050で検出した傾斜角度に基づき、SG90サーボモーターをリアルタイムに制御して、サーボに取り付けた台が常に水平になるように保つ。
  • 使用部品:
    • MPU-6050 (6軸センサー)
    • SG90 (サーボモーター)
    • 小さな板や厚紙など(サーボホーンに取り付ける台)

システムの構造

  1. サーボホーンの上に、小さな板などで台座を作ります。
  2. その台座の上に、MPU-6050を設置します。
  3. 人間が手で台座全体を傾けると、MPU-6050がその傾きを検知します。
  4. プログラムは、検知した傾きと「目標の角度(水平=0度)」との差(誤差)を計算します。
  5. その誤差を打ち消す方向に、サーボモーターを動かします。
  6. この処理を、人間の目には止まって見えるほどの速さで繰り返します。

核となる考え方:「閉ループ・フィードバック制御」

このシステムは、電子制御の最も重要な概念の一つである「閉ループ・フィードバック制御」の簡単な実装例です。

センサーで現状を観測目標との誤差を計算誤差を減らすようにアクチュエーターを操作操作の結果を再びセンサーで観測 → ...

この一連の流れ(ループ)が、システムを常に安定した状態(今回は水平)に収束させようと働きます。

実装のヒント

完全なコードは示しませんが、loop()関数の中核は以下のようになります。Chapter3で使ったMPU6050_tocknライブラリの知識が役立ちます。

void loop() {
  mpu6050.update(); // センサー値を更新

  // 1. MPU-6050から現在の傾斜角度を取得する (Y軸回りの傾きを取得)
  float currentAngle = mpu6050.getAngleY();

  // 2. 目標角度(0度)との誤差を計算する
  float error = 0 - currentAngle;

  // 3. 誤差に基づいてサーボの目標角度を決定する
  float servoAngle = 90 + error * gain; // 'gain'は調整用の係数

  // 4. サーボの角度を制限する (0〜180度の範囲に収める)
  servoAngle = constrain(servoAngle, 0, 180);

  // 5. サーボに角度を指示する
  myservo.write(servoAngle);

  delay(10);
}

この gain という係数(Pゲインと呼ばれます)を調整することが、この作例のキモとなります。 - gainが小さすぎると、傾きに対する反応が鈍く、水平に戻りきりません。 - gainが大きすぎると、反応が過敏になり、サーボが激しく振動(ハンチング)してしまいます。

ちょうど良いgainの値を見つけ出し、台座がスムーズに水平を保つようになった時、あなたはフィードバック制御の第一歩をマスターしたと言えるでしょう。


作例2:抵抗とピンだけで作る静電容量タッチセンサー

機械的なスイッチの次は、動く部品が一切ない、魔法のようなタッチセンサーを作ってみましょう。これは、高価な専用ICを使わずに、マイコンのピン2本、高抵抗1本、そして少しの電線だけで実現できる、非常に奥が深い作例です。

  • 目的: RC回路の充放電時間の変化を測定することで、指の接近・接触を検出する。
  • 使用部品:
    • 高抵抗(1MΩなど、100kΩ〜10MΩ程度) x 1
    • ジャンパワイヤ(タッチ部分になる)

原理:RC時定数と身体の静電容量

この技術の核心はRC回路の充放電時間です。 1. コンデンサ(Capacitor)は、抵抗(Resistor)を通して充放電するとき、電圧が変化するのに一定の時間がかかります。この時間の目安を時定数(τ = R × C)と呼びます。 2. 人間の身体は、電気を蓄えることができる一種のコンデンサです。 3. タッチセンサーとして使う電線に指を近づけたり触れたりすると、身体の静電容量(C)が回路に加わります。 4. Cが増加すると、時定数(τ)も増加し、コンデンサの充電にかかる時間が長くなります。

この「充電にかかる時間の変化」を測定することで、タッチを検出します。

回路図

非常にシンプルです。SEND_PINからRECEIVE_PINへ、非常に大きな抵抗(1MΩなど)を接続し、RECEIVE_PINからタッチするための電線を伸ばします。

           +-- SEND_PIN (例: GP14)
           |
           R (1MΩ)
           |
RECEIVE_PIN ---+-- 電線やアルミホイル(タッチ部分)
(例: GP15)

プログラム

以下のプログラムは、SEND_PINからRECEIVE_PINへ電流を流し、RECEIVE_PINの電圧がHIGHになるまでの時間をマイクロ秒単位で測定します。

#define SEND_PIN 14
#define RECEIVE_PIN 15
#define THRESHOLD 100 // この閾値は環境によって要調整

void setup() {
    Serial.begin(115200);
}

void loop() {
    long duration = readCapacitivePin();

    Serial.print("Duration: ");
    Serial.print(duration);

    if (duration > THRESHOLD) {
        Serial.println(" - Touched!");
    } else {
        Serial.println("");
    }
    delay(100);
}

long readCapacitivePin() {
    // 1. RECEIVEピンを出力LOWにして、コンデンサを放電させる
    pinMode(RECEIVE_PIN, OUTPUT);
    digitalWrite(RECEIVE_PIN, LOW);
    delay(1); // 放電を確実にする

    // 2. SENDピンを出力HIGHにして、充電を開始する準備
    pinMode(SEND_PIN, OUTPUT);
    digitalWrite(SEND_PIN, HIGH);

    // 3. RECEIVEピンを入力モードに戻す(ここから充電時間測定開始)
    pinMode(RECEIVE_PIN, INPUT);

    // 4. 時間測定開始
    long startTime = micros();

    // 5. RECEIVEピンがHIGHになるまで待つ
    while(digitalRead(RECEIVE_PIN) == LOW) {
        // タイムアウト処理を入れるのが望ましいが、ここでは省略
    }

    long endTime = micros();

    // 6. SENDピンを低インピーダンス状態にして、次の測定に影響が出ないようにする
    pinMode(SEND_PIN, INPUT);

    return endTime - startTime;
}

まずはTHRESHOLDを大きな値にして実行し、タッチしていない時とタッチした時のDurationの値を観察してください。そして、その2つの値の間にTHRESHOLDを設定すれば、あなただけのタッチボタンの完成です。

このように、マイコンの機能を深く理解すれば、最小限の部品で高度な機能を実現できるのです。

この gain という係数(Pゲインと呼ばれます)を調整することが、この作例のキモとなります。 - gainが小さすぎると、傾きに対する反応が鈍く、水平に戻りきりません。 - gainが大きすぎると、反応が過敏になり、サーボが激しく振動(ハンチング)してしまいます。

ちょうど良いgainの値を見つけ出し、台座がスムーズに水平を保つようになった時、あなたはフィードバック制御の第一歩をマスターしたと言えるでしょう。