مصطلح PID وحدة التحكم المتكاملة مما يسبب عدم الاستقرار الشديد

StackOverflow https://stackoverflow.com/questions/3902713

  •  29-09-2019
  •  | 
  •  

سؤال

لديّ وحدة تحكم PID تعمل على روبوت مصمم لجعل الروبوت توجه إلى عنوان بوصلة. يتم إعادة حساب تصحيح PID/تطبيقه بمعدل 20 هرتز.

على الرغم من أن وحدة تحكم PID تعمل بشكل جيد في وضع PD (أي ، مع المصطلح المتكامل صفر) ، حتى أدنى قدر من التكامل سوف يجبر الإخراج غير المستقر بطريقة بحيث يتم دفع مشغل التوجيه إلى أقصى اليسار أو اليمين.

شفرة:

        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 ;

ثابت هو جزء لا يتجزأ أو مجموع الخطأ.

لذلك يجب أن يكون التكامل ثابتًا * KI

float integral = Ki * SteadyError;

يحرر:

لقد مررت من خلال الكود الخاص بك مرة أخرى وهناك العديد من العناصر الأخرى التي أود إصلاحها بالإضافة إلى الإصلاح أعلاه.

1) لا تريد وقت دلتا بالميلي ثانية. في نظام أخذ العينات العادي ، سيكون مصطلح دلتا واحد ، لكنك تضع قيمة مثل 50 بالنسبة لمعدل 20 هرتز ، وهذا له تأثير زيادة KI بهذا العامل وتقليل KD بمقدار 50 أيضًا. إذا كنت قلقًا بشأن الارتعاش ، فأنت بحاجة إلى تحويل وقت دلتا إلى وقت عينة نسبي. أود استخدام الصيغة بدلا من ذلك.

float deltaTime = (LastCorrectionTime - DateTime.Now.Ticks) / 500000.0

500000.0 هو عدد القراد المتوقع لكل عينة وهو 20 هرتز هو 50 مللي ثانية.

2) الحفاظ على المصطلح المتكامل ضمن نطاق.

if ( SteadyError > MaxSteadyError ) SteadyError = MaxSteadyError;
if ( SteadyError < MinSteadyError ) SteadyError = MinSteadyError;

3) قم بتغيير الكود التالي بحيث عندما يكون الخطأ حول -180 ، فلن تحصل على خطوة خطأ مع تغيير بسيط.

if (error < -270) error += 360;
if (error >  270) error -= 360;

4) تحقق من التوجيه. degree يتلقى القرار الصحيح والتوقيع.

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

قد تعاني من المتكامل Windup, ، والتي يجب أن تختفي عادةً ، ولكن ليس إذا استغرق الأمر وقتًا أطول لتناقص أكثر مما هو عليه في سيارتك لإكمال دوران كامل (وترطيأ المتكامل مرة أخرى). الحل التافلي هو فرض حدود على التكامل ، على الرغم من وجود حلول أكثر تقدما (PDF ، 879 كيلو بايت) إذا كان نظامك يتطلب ذلك.

يفعل Ki هل لديك العلامة الصحيحة؟

أود بقوة تثبيط استخدام العوامات لمعلمات PID بسبب دقتها التعسفية. استخدم الأعداد الصحيحة (ربما نقطة ثابتة). سيتعين عليك فرض فحص الحد ، ولكن سيكون عاقلًا أكثر بكثير من استخدام العوامات.

تم تراكم المصطلح المتكامل بالفعل بمرور الوقت ، مما يتكاثر بواسطة Deltatime سيجعله يتراكم بمعدل مربع من الوقت. في الواقع نظرًا لأن SeathereRror يتم حسابها بالفعل عن طريق الخطأ عن طريق ضرب الخطأ بواسطة Deltatime ، فهذا أمر مملوء بالوقت!

في ثابتة ، إذا كنت تحاول التعويض عن تحديث فاتح ، فسيكون من الأفضل إصلاح الفتيل. ومع ذلك ، فإن الحساب معيب في أي حال. لقد قمت بحساب وحدات الخطأ/الوقت بينما تريد وحدات الخطأ فقط. الطريقة الصحيحة الحسابية للتعويض عن توقيت ارتعاش إذا كان ذلك ضروريًا حقًا:

SteadyError += (error * 50.0f/deltaTime);

إذا ظل Deltatime في مللي ثانية ومعدل التحديث الاسمي هو 20 هرتز. ومع ذلك ، سيتم احتساب 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(), ، ويمكنك أن تتخطى الكود ومعرفة شكل القيم.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top