Chapter4: 応用的な作例の制作:フィードバック制御への挑戦
これまでの章で、私たちはセンサーから情報を読み取り、アクチュエーターを動かす個別の技術を学んできました。この章では、それらの知識を統合し、より高度で実用的なシステム、「フィードバック制御」を実装する作例に挑戦します。
作例:物理的な水平器(セルフバランシング・プラットフォーム)
ここで制作するのは、傾きを検知して、自ら水平を保とうと動き続ける台(プラットフォーム)です。
- 目的:
MPU-6050で検出した傾斜角度に基づき、SG90サーボモーターをリアルタイムに制御して、サーボに取り付けた台が常に水平になるように保つ。 - 使用部品:
MPU-6050(6軸センサー)SG90(サーボモーター)- 小さな板や厚紙など(サーボホーンに取り付ける台)
システムの構造
- サーボホーンの上に、小さな板などで台座を作ります。
- その台座の上に、MPU-6050を設置します。
- 人間が手で台座全体を傾けると、MPU-6050がその傾きを検知します。
- プログラムは、検知した傾きと「目標の角度(水平=0度)」との差(誤差)を計算します。
- その誤差を打ち消す方向に、サーボモーターを動かします。
- この処理を、人間の目には止まって見えるほどの速さで繰り返します。
核となる考え方:「閉ループ・フィードバック制御」
このシステムは、電子制御の最も重要な概念の一つである「閉ループ・フィードバック制御」の簡単な実装例です。
センサーで現状を観測 → 目標との誤差を計算 → 誤差を減らすようにアクチュエーターを操作 → 操作の結果を再びセンサーで観測 → ...
この一連の流れ(ループ)が、システムを常に安定した状態(今回は水平)に収束させようと働きます。
実装のヒント
完全なコードは示しませんが、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の値を見つけ出し、台座がスムーズに水平を保つようになった時、あなたはフィードバック制御の第一歩をマスターしたと言えるでしょう。