サーボモータを利用した場合、ある角度でモータを停止させるとプルプルと震える現象がよく起こります。これをハンチングと呼びます。ここでは、ArduinoのServeoクラスを利用したサーボモータのコントロールとハンチングの処理について実験してみます。
ハンチングとは
サーボモータのハンチングとは、モーターが目標位置に到達しようとした際に、その位置の周辺で小さく前後に振動する現象を指します。この現象は、サーボモーターが正確な位置制御を試みる過程でよく見られ、特に精密な位置決めが求められるアプリケーションでは問題となることがあります。
ハンチングは主に以下のような要因により引き起こされます:
- 制御ゲインの設定過多: サーボモータの制御システム(通常はPIDコントローラ)のゲインが適切でない場合、モーターが目標位置にオーバーシュートし、その後アンダーシュートを繰り返すことでハンチングが発生します。ゲインが高すぎると、モーターは目標位置を素早く正確に追従しようとしますが、それが過剰反応となってしまうのです。
- 機械的なバックラッシュ: ギアなどの機械的接続部分に遊びがある場合、この遊びが原因でモーターの動きが不安定になり、ハンチングを引き起こすことがあります。
- 外部からの干渉: サーボシステムに外部からの力が加わった場合、それに反応してモーターが微調整を繰り返し、ハンチングが発生することがあります。
ハンチングの抑制方法
ハンチングを抑制するためには、以下の方法が効果的です:
- PIDパラメータの調整: サーボモータのコントローラのPIDパラメータを調整し、適切なゲイン設定を見つけ出します。このプロセスは「チューニング」と呼ばれ、多くの場合、試行錯誤が必要です。
- ソフトスタートとソフトストップの実装: モーターの加速と減速を徐々に行うことで、モーターの急激な動きを抑え、ハンチングを減少させることができます。
- 機械的な改善: モーターと負荷との接続部分のバックラッシュを減らすために、ギアの精度を上げる、または適切な緩衝材を使用するなどの対策を講じます。
- フィードバックシステムの改善: エンコーダなどのフィードバックデバイスを利用し、より正確な位置情報を制御システムに提供します。これにより、モーターの動きをより正確に制御することが可能になります。
Arduinoのサーボクラスを使った基本的なプログラムに、以下のアプローチを組み込むことで、ハンチングを最小限に抑えることができる可能性があります。
プログラムの基本構造
まず、基本的なサーボ制御プログラムの構造を示します。
#include <Servo.h>
Servo myservo; // サーボオブジェクトを作成
void setup() {
myservo.attach(9); // サーボモーターをピン9に接続
}
void loop() {
myservo.write(90); // サーボモーターを90度に動かす
delay(1000); // 1秒間待つ
}
ハンチングを抑えるためのアプローチ
ソフトスタートとソフトストップの実装: サーボモーターに急激な動作をさせず、徐々に速度を上げる(または下げる)ことでハンチングを減らすことができます。
void softMove(int targetAngle, int stepDelay) {
int currentAngle = myservo.read(); // 現在の角度を取得
if (currentAngle < targetAngle) {
for (int angle = currentAngle; angle <= targetAngle; angle++) {
myservo.write(angle);
delay(stepDelay);
}
} else {
for (int angle = currentAngle; angle >= targetAngle; angle--) {
myservo.write(angle);
delay(stepDelay);
}
}
}
void loop() {
softMove(90, 15); // ソフトに90度に移動する
delay(1000);
}
- 位置フィードバックの利用: サーボモーターの実際の位置をフィードバックとして利用し、目標位置に近づくにつれて速度を下げるか、ピンポイントで調整することが可能です。これを実現するためには、サーボモーターから位置情報を取得する必要がありますが、一般的なサーボモーターでは外部センサーが必要になることもあります。
- 適切なディレイの調整: 上記の
softMove
関数で使われているstepDelay
パラメータを調整することで、サーボの動作速度を調整し、ハンチングを抑制します。ディレイが短すぎるとモーターが急激に動き、ハンチングが発生しやすくなります。
パルス制御で細かく調整してみる
Servoクラスに用意されているwriteMicroseconds()関数を使うと、writeのようにサーボモータを角度ではなくパルスで制御することができます。そのため、角度制御より細かい指定ができます。この方法を流用して、ハンチング処理ができないか試してみます。
#include <Servo.h>
Servo myservo; // サーボオブジェクトを作成
int pos = 1500; // 初期パルス幅をマイクロ秒で指定(通常の中心位置)
void setup() {
myservo.attach(9); // サーボモーターをピン9に接続
myservo.writeMicroseconds(pos); // 初期位置に移動
}
void loop() {
int target = 1700; // 目標パルス幅をマイクロ秒で設定(サーボの種類により調整)
// ターゲットにゆっくり移動
if (pos < target) {
for (int i = pos; i <= target; i += 10) {
myservo.writeMicroseconds(i);
delay(20); // 小さなステップでゆっくり移動
}
} else if (pos > target) {
for (int i = pos; i >= target; i -= 10) {
myservo.writeMicroseconds(i);
delay(20); // 小さなステップでゆっくり移動
}
}
pos = target; // 現在位置を更新
delay(1000); // 一定時間待機
target = (target == 1700) ? 1300 : 1700; // 目標パルス幅を変更
}
プログラムの解説
- サーボモーターを細かいパルス幅で制御することで、角度の精度を向上させ、ハンチングを抑制します。
- ループ内でサーボモーターの位置をゆっくりと変更し、急激な動きによる振動を避けます。
delay(20)
はサーボの移動速度を遅くすることで、より滑らかな動きとなり、ハンチングを抑える効果があります。
ハンチング対応 new_writeMicrosecondsの実験
ハンチングを抑えるためにnew_writeMicroseconds
関数を新しく作成してみます。サーボモーターのパルス制御をより細かく、スムーズに行うために利用できます。この関数は、指定されたパルス幅に徐々に遷移することで、急激な動きを避け、ハンチング現象を最小限に抑えます。以下に示すのは、Arduinoで使用可能なサンプルコードです。
#include <Servo.h>
Servo myservo; // サーボオブジェクトを作成
int currentPulse = 1500; // 初期パルス幅をマイクロ秒で指定(通常の中心位置)
void setup() {
myservo.attach(9); // サーボモーターをピン9に接続
myservo.writeMicroseconds(currentPulse); // 初期位置に移動
}
void loop() {
int targetPulse = 1700; // 目標パルス幅をマイクロ秒で設定(サーボの種類により調整)
new_writeMicroseconds(targetPulse);
delay(2000); // 2秒待ってから次の動作へ
targetPulse = 1300; // 目標パルス幅を変更
new_writeMicroseconds(targetPulse);
delay(2000); // 2秒待ってから次の動作へ
}
void new_writeMicroseconds(int targetPulse) {
int stepSize = 10; // パルス幅の変更幅(小さいほど滑らか)
int delayTime = 20; // パルス幅変更のディレイ時間(ミリ秒)
if (currentPulse < targetPulse) {
while (currentPulse < targetPulse) {
currentPulse += stepSize;
if (currentPulse > targetPulse) {
currentPulse = targetPulse; // オーバーシュート防止
}
myservo.writeMicroseconds(currentPulse);
delay(delayTime);
}
} else if (currentPulse > targetPulse) {
while (currentPulse > targetPulse) {
currentPulse -= stepSize;
if (currentPulse < targetPulse) {
currentPulse = targetPulse; // アンダーシュート防止
}
myservo.writeMicroseconds(currentPulse);
delay(delayTime);
}
}
}
関数の解説
new_writeMicroseconds
関数:- この関数は、現在のパルス幅
currentPulse
から目標のパルス幅targetPulse
まで、指定したstepSize
のステップで徐々に移動します。 - 移動間隔には
delayTime
ミリ秒の遅延を設けることで、サーボの動きをよりスムーズにし、ハンチングを抑えます。 - 関数内でパルス幅の増減を行い、オーバーシュートやアンダーシュートが起こらないように調整しています。
- この関数は、現在のパルス幅