PIDコントローラー積分期間が極端な不安定性
-
29-09-2019 - |
質問
いPIDコントローラー走行ロボットがありますが、ここでは、ロボットを操縦をコンパスの見出しを表示します。PID回修正再計算を行/応用率20Hz.
このPIDコントローラの作PDモード(IEの積分期間ゼロの思い出もに、微量の積分の力を出力不安定になるようにステアリングアクチュエータが押されたかを左右す。
コード:
private static void DoPID(object o)
{
// Bring the LED up to signify frame start
BoardLED.Write(true);
// Get IMU heading
float currentHeading = (float)RazorIMU.Yaw;
// We just got the IMU heading, so we need to calculate the time from the last correction to the heading read
// *immediately*. The units don't so much matter, but we are converting Ticks to milliseconds
int deltaTime = (int)((LastCorrectionTime - DateTime.Now.Ticks) / 10000);
// Calculate error
// (let's just assume CurrentHeading really is the current GPS heading, OK?)
float error = (TargetHeading - currentHeading);
LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2");
// We calculated the error, but we need to make sure the error is set so that we will be correcting in the
// direction of least work. For example, if we are flying a heading of 2 degrees and the error is a few degrees
// to the left of that ( IE, somewhere around 360) there will be a large error and the rover will try to turn all
// the way around to correct, when it could just turn to the right a few degrees.
// In short, we are adjusting for the fact that a compass heading wraps around in a circle instead of continuing
// infinity on a line
if (error < -180)
error = error + 360;
else if (error > 180)
error = error - 360;
// Add the error calculated in this frame to the running total
SteadyError = SteadyError + (error * deltaTime);
// We need to allow for a certain amount of tolerance.
// If the abs(error) is less than the set amount, we will
// set error to 0, effectively telling the equation that the
// rover is perfectly on course.
if (MyAbs(error) < AllowError)
error = 0;
LCD.Lines[2].Text = "Error: " + error.ToString("F2");
// Calculate proportional term
float proportional = Kp * error;
// Calculate integral term
float integral = Ki * (SteadyError * deltaTime);
// Calculate derivative term
float derivative = Kd * ((error - PrevError) / deltaTime);
// Add them all together to get the correction delta
// Set the steering servo to the correction
Steering.Degree = 90 + proportional + integral + derivative;
// We have applied the correction, so we need to *immediately* record the
// absolute time for generation of deltaTime in the next frame
LastCorrectionTime = DateTime.Now.Ticks;
// At this point, the current PID frame is finished
// ------------------------------------------------------------
// Now, we need to setup for the next PID frame and close out
// The "current" error is now the previous error
// (Remember, we are done with the current frame, so in
// relative terms, the previous frame IS the "current" frame)
PrevError = error;
// Done
BoardLED.Write(false);
}
いてうかが起きているのはどこを直せばいいの?
解決
このように見えますお時間ベースの一つです。エラーが既に累積エラーからのサンプルでよい掛けdeltaTime倍です。ようなので変更のコードは下記までお願いします。
SteadyError += error ;
SteadyErrorの積分または和のエラーになります。
を積分すべきですSteadyError*Ki
float integral = Ki * SteadyError;
編集:
思い通りにコードもあるその他の項目のように修正また、上記のほか、です。
1)によってお使いのデルタの時間をミリ秒です。通常の採取システムのデ期予想されているだけのような値が50の20Hzする効果Kiによるこの要因を減らしKd効果を明確にする必要がある50。てについて悩んでいるジッタを変換する必要があdelta時間が相対的にサンプル。私が使用する式です。
float deltaTime = (LastCorrectionTime - DateTime.Now.Ticks) / 500000.0
の500000.0は予定人数目盛りサンプルを用20Hzは、入光時50ms.
2)の積分期間以内の範囲です。
if ( SteadyError > MaxSteadyError ) SteadyError = MaxSteadyError;
if ( SteadyError < MinSteadyError ) SteadyError = MinSteadyError;
3)変更は、次のコードでエラーが発生した場合の周-180を得ることはありませんの第一歩誤差の少ない変化します。
if (error < -270) error += 360;
if (error > 270) error -= 360;
4)検証しました。学位を受け、正しい解決す。
5)最後に陽ができるとともに下落deltaTimeとを算出するの差動期には,以下のようにする
float derivative = Kd * (error - PrevError);
すべてのコードとなります。
private static void DoPID(object o)
{
// Bring the LED up to signify frame start
BoardLED.Write(true);
// Get IMU heading
float currentHeading = (float)RazorIMU.Yaw;
// Calculate error
// (let's just assume CurrentHeading really is the current GPS heading, OK?)
float error = (TargetHeading - currentHeading);
LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2");
// We calculated the error, but we need to make sure the error is set
// so that we will be correcting in the
// direction of least work. For example, if we are flying a heading
// of 2 degrees and the error is a few degrees
// to the left of that ( IE, somewhere around 360) there will be a
// large error and the rover will try to turn all
// the way around to correct, when it could just turn to the right
// a few degrees.
// In short, we are adjusting for the fact that a compass heading wraps
// around in a circle instead of continuing infinity on a line
if (error < -270) error += 360;
if (error > 270) error -= 360;
// Add the error calculated in this frame to the running total
SteadyError += error;
if ( SteadyError > MaxSteadyError ) SteadyError = MaxSteadyError;
if ( SteadyError < MinSteadyError ) SteadyError = MinSteadyError;
LCD.Lines[2].Text = "Error: " + error.ToString("F2");
// Calculate proportional term
float proportional = Kp * error;
// Calculate integral term
float integral = Ki * SteadyError ;
// Calculate derivative term
float derivative = Kd * (error - PrevError) ;
// Add them all together to get the correction delta
// Set the steering servo to the correction
Steering.Degree = 90 + proportional + integral + derivative;
// At this point, the current PID frame is finished
// ------------------------------------------------------------
// Now, we need to setup for the next PID frame and close out
// The "current" error is now the previous error
// (Remember, we are done with the current frame, so in
// relative terms, the previous frame IS the "current" frame)
PrevError = error;
// Done
BoardLED.Write(false);
}
他のヒント
初期化していますか SteadyError
(奇妙な名前...なぜ「インテグレーター」ではないのですか)?起動時にランダムな値が含まれている場合、ゼロ近くに戻ることはありません(1e100 + 1 == 1e100
).
あなたは苦しんでいるかもしれません インテグレーターのワインドアップ, 、これは通常消えるはずですが、車両が完全な回転を完了するよりも減少するのに時間がかかる場合はそうではありません(そして再びインテグレーターをワインドします)。些細な解決策は、インテグレーターに制限を課すことですが、 より高度なソリューション (PDF、879 KB)システムが必要な場合。
します Ki
正しいサインがありますか?
私は...するだろう 強く 任意の精度のため、PIDパラメーターにフロートを使用することを思いとどまらせます。整数を使用してください(たぶん 固定点)。制限チェックを課す必要がありますが、フロートを使用するよりもはるかに正気です。
積分用語はすでに時間の経過とともに蓄積されており、デルタイムを乗算すると、時間二乗速度で蓄積されます。実際、SteadyErrorは既にエラーをDeltatimeで乗算することによって誤って計算されているため、時間キューブです!
SteadyErrorでは、不妊のアップデートを補償しようとしている場合は、外傷性を修正する方が良いでしょう。ただし、いずれにせよ、計算には欠陥があります。エラーユニットのみが必要なのに対し、エラー/時間の単位で計算しました。本当に必要な場合は、タイミングジッターを補うための補助的な正しい方法は次のとおりです。
SteadyError += (error * 50.0f/deltaTime);
Deltatimeがミリ秒のままで、名目上の更新率が20Hzの場合。ただし、Deltatimeはフロートとしてより適切に計算されるか、検出しようとしているタイミングジッターである場合、ミリ秒に変換されません。あなたは不必要に精度を破棄しています。いずれにせよ、必要なのは、公称時間と実際の時間の比率でエラー値を変更することです。
良い読み物です 博士号なしのpid
なぜあなたのコードが機能していないのかはわかりませんが、その理由を見るためにテストすることもできないこともほとんどありません。タイマーサービスを挿入して、それを模倣して何が起こっているのかを確認できるようにすることができます。
public interace ITimer
{
long GetCurrentTicks()
}
public class Timer : ITimer
{
public long GetCurrentTicks()
{
return DateTime.Now.Ticks;
}
}
public class TestTimer : ITimer
{
private bool firstCall = true;
private long last;
private int counter = 1000000000;
public long GetCurrentTicks()
{
if (firstCall)
last = counter * 10000;
else
last += 3500; //ticks; not sure what a good value is here
//set up for next call;
firstCall = !firstCall;
counter++;
return last;
}
}
次に、両方の呼び出しを交換します DateTime.Now.Ticks
と GetCurrentTicks()
, 、そして、コードを踏み出して、値がどのように見えるかを確認できます。