サーボモータを利用した場合、ある角度でモータを停止させると、モータを停止させているのにプルプルと震える現象をハンチングと呼びます。これは、様々な原因がありますが、プログラムによってもある程度回避できることがあります。今回はソフト的に回避する方法を、ArduinoのServeoクラスを利用していろいろトライしてみます。
ハンチングの原因とその対策
ハンチングが起こる原因としては、ギアーやベルトやエンコーダなどハード的な原因もありますが、ソフト的な原因も考えられるため、まずはその原因と、ソフト的な面からこのハンチングを止める対策を施してみます。
ソフト的な原因とは….【制御ゲインの設定過多】
制御システム(通常はPIDコントローラ)のゲインが適切でない場合があります。
その場合、モーターが目標位置にオーバーシュートし、その後アンダーシュートを繰り返す、つまり目的角度の前後でいったり来たりを繰り返すことでことでハンチングが発生します。
この場合、サーボモータのコントローラのPIDパラメータを調整し、適切なゲイン設定を見つけ出すという「チューニング」作業を行います。
また別の方法では、 モーターの加速と減速を徐々に行うことで、モーターの急激な動きを抑えて、パルスの行ったり来たりを抑えハンチングを減少させることができます。
さらには、高性能なサーボモータを使用できる場合には エンコーダなどのフィードバックデバイスを利用し、より正確な位置情報を制御システムに提供します。これにより、モーターの動きをより正確に制御することが可能になります。
プログラミング
0度と90度の回転を繰り返すプログラムを書いてみます。0度、90度では3秒間の停止を行います。
1.Servoクラスでwriteコマンドを使用
ArduinoのServoクラスを使い単純にwriteコマンドを使って単純に書いてみます。
include <Servo.h>
Servo myservo; // サーボオブジェクトを作成
void setup() {
myservo.attach(9); // サーボモーターをピン9に接続
}
void loop() {
myservo.write(0); // サーボモーターを90度に動かす
delay(3000); // 1秒間待つ
myservo.write(90); // サーボモーターを90度に動かす
delay(3000); // 1秒間待つ
}
2.加速と原則を緩やかにしてみます
サーボモーターに急激な動作をさせず、徐々に速度を上げる(または下げる)ことでハンチングを減らすプログラムで制御してみます。
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);
}
3.writeMicrosecondsでパルスで細かく制御してみる
writeコマンドよりもっと細かい制御ができるwriteMicroseconds()関数によって、モータを角度ではなくパルスで制御してみましょう。writeコマンドでは角度が例えば0~180度等の角度指定だったのに対して、writeMicrosecondsでは例えば0~180度を500~2500などのパルス値で制御できますから、より細かく移動量を調整できます。
#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; // 目標パルス幅を変更
}
関数化 new_writeMicrosecondsの実験
パルスで指定した加速減速対策を加えた動きを、関数化して使用してみましょう。
#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);
}
}
}
※改善点として、関数内で使用している delayコマンドを delayMicrosecondsに変えると、より細かい時間制御ができ、さらにチューニングができるようになります。
※ただし、この関数は関数内で delayを使うため、その間他の制御ができなくなります。実用的な制御をするためには、もう一度しっかり設計をして作り直す必要があります。
次はこのあたりを考慮した多軸制御にも挑戦していきたいと思います。