简体   繁体   English

为什么这个 LinqPad 程序在第二次运行时会产生不同的结果?

[英]Why does this LinqPad program produce different results on the second run?

I am developing a simple PID controller in LinqPad:我正在 LinqPad 中开发一个简单的 PID 控制器:

The PID Class 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;
    }
}

For testing and tuning, the controller will control this simple process:为了测试和调整,控制器将控制这个简单的过程:

The Controlled Process受控过程

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;
    }
}

...using this main loop: ...使用这个主循环:

The Main Program Loop主程序循环

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;
}

Everything works as expected on the first run:第一次运行时一切都按预期工作:

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...

However, I started getting unexpected results on the second and subsequent runs after I commented out the line proc.Dump() :但是,在我注释掉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...

Why is the second run (and subsequent runs) returning different results in my case?为什么第二次运行(和后续运行)在我的情况下返回不同的结果?

Any of the following actions will cause the next run to succeed:以下任何操作都将导致下一次运行成功:

  • modify the code (even just adding/removing a single whitespace)修改代码(甚至只是添加/删除一个空格)
  • press [CTRL]+[SHIFT]+[F5]按 [CTRL]+[SHIFT]+[F5]

The following makes the code run correctly every time:以下使代码每次都能正确运行:

  • uncomment the line proc.Dump()取消注释行proc.Dump()

This answer mentions that static variables will be cached between runs, but I have no static variables.这个答案提到静态变量将在运行之间缓存,但我没有静态变量。 I suspect the problem is related to the Application Domain Caching feature in LinqPad, but I'm trying to understand why I'm affected by this.我怀疑问题关系到LinqPad应用程序域缓存功能,但我想知道为什么我受此影响。

Update更新

StriplingWarrior's answer is correct, my first derivative calculation resulted in Infinity when they system was performing well (ie after LinqPad had cached the first run), causing all subsequent calculations to fail. StriplingWarrior 的回答是正确的,我的一阶导数计算在系统运行良好时(即在 LinqPad 缓存第一次运行之后)导致 Infinity,导致所有后续计算失败。 Modifying my program in any way was invalidating this cache and caused the deltaTime to be large enough to avoid the error again on the next run.以任何方式修改我的程序都会使这个缓存失效,并导致 deltaTime 足够大,以避免在下次运行时再次出现错误。

Since a derivative term makes no sense on the first interval, I decided to handle this by simply ignoring it:由于导数项在第一个区间没有意义,我决定通过简单地忽略它来处理这个问题:

var p = Kp * Proportional;
var i = Ki * Integral;
var d = float.IsInfinity(Derivative) ? 0 : Kd * Derivative;

return p + i + d;

You can test what Andrew theorizes in your comments above, by changing the first part of your main method thusly:您可以通过更改 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();
    }

Running on my machine, I can see that the first run takes 10,000+ ticks, whereas the second run takes only 20 ticks.在我的机器上运行,我可以看到第一次运行需要 10,000+ 滴答,而第二次运行只需要 20 滴答。 I'm guessing this makes your calculations based on differences in DateTime.Now have very small delta values, and yield the differences you're seeing.我猜这会使您根据 DateTime.Now 的差异进行计算。现在的增量值非常小,并产生您所看到的差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么这个表达式在 C# 和 C++ 中产生不同的结果? - Why does this expression produce different results in C# and C++? 为什么完全相同的代码产生不同的结果? - Why does the exact same code produce different results? 没有语法错误,但程序不产生任何结果 - No syntax errors, but program does not produce any results 为什么LinqPad在转储某些类型时会运行ToString()? - Why does LinqPad run ToString() on some types when they are dumped? 为什么这个程序只产生三个警告? - Why does this program only produce three warnings? 为什么我的Vertex Animation着色器在“场景”和“游戏”视图中产生不同的结果? - Why does my Vertex Animation shader produce different results in Scene and Game view? 为什么Physics2D会为AddForce()与速度产生不同的结果? - Why does Physics2D produce different results for AddForce() versus velocity? 为什么 Double.ToString("#") 和 BigInteger.ToString() 对相同的值产生不同的结果? - Why does Double.ToString("#") and BigInteger.ToString() produce different results for the same value? 为什么使用volatile产生不同的结果? - Why does using volatile produce a different result? 为什么这三个LINQ代码产生不同(或错误)的结果? - Why do these three pieces of LINQ code produce different (or erroneous) results?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM