前回まで
前回までは、サーボイージングによってサーボモータを滑らかに動かすため、その前提となる回路とプログラムを作成しました。今回はこれを使って実際にサーボイージングを実装していきます。
Step 3:サーボイージングの導入
それでは前編Step2までのプログラムに、サーボイージングを実装していきます。
Step2までで用いた回路構成もArduino Uno R4 MinimaとサーボモータSG90を使ったものそのままです。
Step2までのプログラムを次のように拡張します。変更部分にのみコメントを付与しています。
#include <Servo.h>
unsigned int interval_ms = 10;
unsigned int elapsed_ms, previous_ms, current_ms;
static const int sg90_minAngle = 0;
static const int sg90_maxAngle = 180;
static const int sg90_minPulse = 500;
static const int sg90_maxPulse = 2400;
static const int NUM_PATTERN = 2;
int angle[NUM_PATTERN] = {10, 170};
int currentPattern = 0, previousPattern = 0;
int operationTime[NUM_PATTERN] = {1000, 2000};
int waitTime = 2000;
int motionStartTime = 0, motionEndTime = 0;
static const int MOTION_READY = 0;
static const int MOTION_OPERATING = 1;
static const int MOTION_WAITING = 2;
int currentMode = MOTION_READY;
Servo servo1;
////イージング関数
double easeInOutCubic(double x)
{
return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2, 3) / 2;
}
////イージング関数
double easeOutBounce(double x)
{
double n1 = 7.5625;
double d1 = 2.75;
if (x < 1 / d1)
{
return n1 * x * x;
}
else if (x < 2 / d1)
{
return n1 * (x -= 1.5 / d1) * x + 0.75;
}
else if (x < 2.5 / d1)
{
return n1 * (x -= 2.25 / d1) * x + 0.9375;
}
else
{
return n1 * (x -= 2.625 / d1) * x + 0.984375;
}
}
int angle2Pulse(int angle, int minAngle, int maxAngle, int minPulse, int maxPulse)
{
double angleRate = double(angle) / 180.0;
int pulse = angleRate * (sg90_maxPulse - sg90_minPulse) + sg90_minPulse;
return pulse;
}
void setup()
{
previous_ms = 0;
servo1.attach(9);
}
void loop()
{
current_ms = millis();
elapsed_ms = current_ms - previous_ms;
if (elapsed_ms >= interval_ms)
{
if (currentMode == MOTION_READY)
{
previousPattern = currentPattern;
currentPattern = (currentPattern + 1) % NUM_PATTERN;
currentMode = MOTION_OPERATING;
motionStartTime = current_ms;
}
else if (currentMode == MOTION_OPERATING)
{
double currentMotionRate = double(current_ms - motionStartTime) / operationTime[currentPattern];
if (currentMotionRate >= 1.0)
{
currentMode = MOTION_WAITING;
motionEndTime = current_ms;
}
else
{
////経過時間から算出したcurrentMotionRate(0~1)に対しイージング関数で計算
currentMotionRate = easeInOutCubic(currentMotionRate);
//currentMotionRate = easeOutBounce(currentMotionRate);
int currentAngle = double(angle[currentPattern] - angle[previousPattern]) * currentMotionRate + angle[previousPattern];
servo1.writeMicroseconds(angle2Pulse(currentAngle, sg90_minAngle, sg90_maxAngle, sg90_minPulse, sg90_maxPulse));
}
}
else if (currentMode == MOTION_WAITING)
{
if (current_ms - motionEndTime >= waitTime)
{
currentMode = MOTION_READY;
}
}
previous_ms = current_ms;
}
}
実際に動作させたものがこちらです。
主な変更部分は下記のみです。
////経過時間から算出したcurrentMotionRate(0~1)に対しイージング関数で計算
currentMotionRate = easeInOutCubic(currentMotionRate);
//currentMotionRate = easeOutBounce(currentMotionRate);
このまま用いるとeaseInOutCubicの動作をします。
easeInOutCubicの行をコメントアウトし、easeOutBounceの行のコメントアウトを外すとeaseOutBounceの動作になります。
対応するeaseInOutCubicとeaseOutBounceの関数も新たに実装されています。引数として0から1の値を取る浮動小数値を受け取り、値を返します。
Step2では、動作開始時を0、動作終了時を1として、現在の動作がどの時点のものであるのかを表した数値を計算していました。
////経過時間から現在の動作が0から1のどこにあたるかを計算
double currentMotionRate = double(current_ms - motionStartTime) / operationTime[currentPattern];
この値をここで用います。この値をeaseInOutCubicまたはeaseOutBounceに引数として入れて再計算し、以降はStep2までと同様に角度を求めてやることでサーボイージングが実現できます。
easeInOutCubicやeaseOutBounceのような関数をイージング関数と呼びます。Step2との違いは、これらの関数を実装したことと、関数を利用する一行を加えただけです。
サーボイージングとイージング関数
ではイージング関数とはどのようなものなのでしょうか?
イージング関数はユーザーインターフェースのアニメーションの動きなどに用いられている関数で、滑らかな動きや様々な効果を持った動きを生み出すために使われています。
こちらのサイトで様々な例が紹介されています。
このサイトでは多くのイージング関数が掲載されています。実際に各関数の詳細を見てみると、イージング関数のコードが掲載されています。これらのコードはTypeScriptで書かれているためArduinoで用いる場合は多少変更する必要がありますが、簡単に利用することができます。また全てのイージング関数が動作の開始を0、動作の終了を1とする値を基準に作られています。
そのためこうしたイージング関数の中から好きなものを選び、0~1で現在の動作時点を表す値をこのイージング関数に渡してやれば望んだ動きが簡単に作れるというわけです。Step2で0~1で表される動作時点の値をわざわざ計算した理由がこれです。
今回はeaseInOutCubicとeaseOutBounceを例にして作りましたが、他のイージング関数を用いる場合もそのイージング関数だけ作って、その関数に置き換えるだけで簡単に動作を変更できます。
まとめ
以上、まとめると次のようになります。
- プログラム内でサーボの動作時点を0~1の値で表す
- イージング関数を実装する
- イージング関数に動作時点の数値を渡す
- イージング関数が返した値を元にサーボの角度を計算する
このような流れで、簡単にサーボイージングを実現することができます!