[英]Why does this LinqPad program produce different results on the second run?
我正在 LinqPad 中開發一個簡單的 PID 控制器:
class PidController
{
public float Proportional { get; set; }
public float Integral { get; set; }
public float Derivative { get; set; }
public float SetPoint { get; set; }
public float Kp { get; set; }
public float Ki { get; set; }
public float Kd { get; set; }
float _lastError;
DateTime _lastTime = DateTime.Now;
public PidController(float kp, float ki, float kd)
{
Kp = kp; Ki = ki; Kd = kd;
}
public float GetControlValue(float actual)
{
var currentTime = DateTime.Now;
var deltaTime = (float)(currentTime - _lastTime).TotalSeconds;
var error = SetPoint - actual;
Proportional = error;
Integral = Integral + error * deltaTime;
Derivative = (error - _lastError) / deltaTime;
_lastError = error;
return Kp * Proportional + Ki * Integral + Kd * Derivative;
}
}
為了測試和調整,控制器將控制這個簡單的過程:
class SimpleProcess
{
private DateTime _lastTime = DateTime.Now;
private float _output;
public float Output { get { UpdateOutput(); return _output; }}
public float Input { get; set; }
private void UpdateOutput()
{
var deltaTime = (float)(DateTime.Now - _lastTime).TotalSeconds;
_output += Input * deltaTime;
}
}
...使用這個主循環:
void Main()
{
var pid = new PidController(1f, 0f, 0f) { SetPoint = 100f };
var proc = new SimpleProcess();
// pid.Dump();
// proc.Dump();
var values = new List<ProcessValue>();
for (int i = 0; i < 50; i++)
{
var actual = proc.Output;
var controlValue = pid.GetControlValue(actual);
proc.Input = controlValue;
var value = new ProcessValue
{
index = i,
timestamp = DateTime.Now.ToString("ss.fff"),
p = pid.Proportional,
i = pid.Integral,
d = pid.Derivative,
input = controlValue,
output = actual
};
values.Add(value);
Thread.Sleep(100);
}
values.Dump();
}
public class ProcessValue
{
public int index;
public string timestamp;
public float p, i, d, input, output;
}
第一次運行時一切都按預期工作:
index timestamp p i d input output
0 53.309 100 0.46 21490.59 100 0
1 53.411 89.69 10.06 -96.27 89.69 10.30
etc...
但是,在我注釋掉proc.Dump()
行后,我開始在第二次和后續運行中得到意想不到的結果:
index timestamp p i d input output
0 10.199 100 0 ∞ NaN 0
1 10.299 NaN NaN NaN NaN NaN
2 10.399 NaN NaN NaN NaN NaN
etc...
為什么第二次運行(和后續運行)在我的情況下返回不同的結果?
以下任何操作都將導致下一次運行成功:
以下使代碼每次都能正確運行:
proc.Dump()
這個答案提到靜態變量將在運行之間緩存,但我沒有靜態變量。 我懷疑問題是關系到LinqPad應用程序域緩存功能,但我想知道為什么我受此影響。
StriplingWarrior 的回答是正確的,我的一階導數計算在系統運行良好時(即在 LinqPad 緩存第一次運行之后)導致 Infinity,導致所有后續計算失敗。 以任何方式修改我的程序都會使這個緩存失效,並導致 deltaTime 足夠大,以避免在下次運行時再次出現錯誤。
由於導數項在第一個區間沒有意義,我決定通過簡單地忽略它來處理這個問題:
var p = Kp * Proportional;
var i = Ki * Integral;
var d = float.IsInfinity(Derivative) ? 0 : Kd * Derivative;
return p + i + d;
您可以通過更改 main 方法的第一部分來測試 Andrew 在上面的評論中的理論化:
var sw = new Stopwatch();
sw.Start();
var pid = new PidController(1f, 0f, 0f) { SetPoint = 100f };
var proc = new SimpleProcess();
// pid.Dump();
// proc.Dump();
var values = new List<ProcessValue>();
for (int i = 0; i < 50; i++)
{
var actual = proc.Output;
var controlValue = pid.GetControlValue(actual);
if(sw.IsRunning){
sw.Stop();
sw.ElapsedTicks.Dump();
}
在我的機器上運行,我可以看到第一次運行需要 10,000+ 滴答,而第二次運行只需要 20 滴答。 我猜這會使您根據 DateTime.Now 的差異進行計算。現在的增量值非常小,並產生您所看到的差異。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.