PID-управление: является ли добавление задержки перед следующим циклом хорошей идеей?

#c #pid #robotics #control-theory #pid-controller

#c #pid #робототехника #теория управления #pid-контроллер

Вопрос:

Я реализую PID-управление на c , чтобы робот с дифференциальным приводом поворачивался на точное количество градусов, но у меня много проблем.

Досрочный выход из цикла управления из-за быстрого выполнения цикла

Если робот измеряет свою ошибку как меньшую, чем .5 градусов, он выходит из цикла управления и считает ход «завершенным» (.5 — это случайное значение, которое я могу изменить в какой-то момент). Похоже, что цикл управления работает так быстро, что робот может поворачиваться с очень высокой скоростью, переворачивать заданное значение и выходить из цикла / отключать мощность двигателя, потому что он находился на заданном значении в течение короткого мгновения. Я знаю, что в этом и заключается цель PID-управления — точно достичь заданного значения без превышения, но эта проблема очень затрудняет настройку PID-констант. Например, я пытаюсь найти значение kp таким образом, чтобы было устойчивое колебание, но колебаний никогда не бывает, потому что робот думает, что он «закончил», как только он проходит заданное значение. Чтобы исправить это, я внедрил систему, в которой робот должен находиться на заданном значении в течение определенного периода времени перед выходом, и это было эффективно, позволяя возникать колебаниям, но проблема раннего выхода из цикла кажется необычной проблемой, и мое решение может быть неправильным.

Общий термин не имеет эффекта из-за быстрого выполнения

Как только я заставил робота колебаться контролируемым образом, используя только P, я попытался добавить D, чтобы предотвратить перерегулирование. Однако большую часть времени это не имело никакого эффекта, поскольку цикл управления выполняется так быстро, что в 19 циклах из 20 скорость изменения ошибки равна 0: робот не двигался или двигался недостаточно, чтобы его можно было измерить за это время. Я напечатал изменение ошибки и производный член каждого цикла, чтобы подтвердить это, и я мог видеть, что оба они будут равны 0 примерно для 20 циклов цикла, прежде чем принимать разумное значение, а затем вернуться к 0 еще для 20 циклов. Как я уже сказал, я думаю, это связано с тем, что циклы цикла настолько быстры, что робот буквально не перемещается достаточно для какого-либо заметного изменения ошибки. Это было большой проблемой, потому что это означало, что общий член практически не влиял на движение робота, поскольку он почти всегда был равен 0. Чтобы устранить эту проблему, я попытался использовать последнее ненулевое значение производной вместо любых значений 0, но это не сработало, и робот будет колебаться случайным образом, если последняя производная не отражает текущую скорость изменения ошибки.

Примечание: я также использую небольшую прямую связь для статического коэффициента трения, и я называю эту прямую связь «f»

Должен ли я добавить задержку?

Я понял, что, по-моему, источником обеих этих проблем является цикл, выполняемый очень и очень быстро, поэтому я подумал о добавлении оператора ожидания в конце цикла. Однако, похоже, что в целом плохое решение намеренно замедлять цикл. Это хорошая идея?

 turnHeading(double finalAngle, double kp, double ki, double kd, double f){
    std::clock_t timer;
    timer = std::clock();

    double pastTime = 0;
    double currentTime = ((std::clock() - timer) / (double)CLOCKS_PER_SEC);

    const double initialHeading = getHeading();
    finalAngle = angleWrapDeg(finalAngle);

    const double initialAngleDiff = initialHeading - finalAngle;
    double error = angleDiff(getHeading(), finalAngle);
    double pastError = error;

    double firstTimeAtSetpoint = 0;
    double timeAtSetPoint = 0;
    bool atSetpoint = false;

    double integral = 0;
    double derivative = 0;
    double lastNonZeroD = 0;

    while (timeAtSetPoint < .05)
    {
        updatePos(encoderL.read(), encoderR.read());
        error = angleDiff(getHeading(), finalAngle);

        currentTime = ((std::clock() - timer) / (double)CLOCKS_PER_SEC);
        double dt = currentTime - pastTime;

        double proportional = error / fabs(initialAngleDiff);
        integral  = dt * ((error   pastError) / 2.0);
        double derivative = (error - pastError) / dt;
        
        //FAILED METHOD OF USING LAST NON-0 VALUE OF DERIVATIVE
        // if(epsilonEquals(derivative, 0))
        // {
        //     derivative = lastNonZeroD;
        // }
        // else
        // {
        //     lastNonZeroD = derivative;
        // }

        double power = kp * proportional   ki * integral   kd * derivative;

        if (power > 0)
        {
            setMotorPowers(-power - f, power   f);
        }
        else
        {
            setMotorPowers(-power   f, power - f);
        }

        if (fabs(error) < 2)
        {
            if (!atSetpoint)
            {
                atSetpoint = true;
                firstTimeAtSetpoint = currentTime;
            }
            else //at setpoint
            {
                timeAtSetPoint = currentTime - firstTimeAtSetpoint;
            }
        }
        else //no longer at setpoint
        {
            atSetpoint = false;
            timeAtSetPoint = 0;
        }
        pastTime = currentTime;
        pastError = error;
    }
    setMotorPowers(0, 0);
}

turnHeading(90, .37, 0, .00004, .12);
 

Комментарии:

1. Вы можете получить больше результатов, опубликовав в группе «Электротехника». Там много опытных специалистов по теории управления. Кроме того, если вы регистрируете положение, время и управление двигателем, вы можете получить много информации для настройки констант обратной связи.

2. Рассмотрите возможность сохранения меток времени с высокой точностью при каждом угловом переходе. Затем вы можете получить гораздо более точную скорость, которая необходима для бесперебойной работы.

3. @doug спасибо за предложение. Я отправлю сообщение в другие группы. Я регистрировал их, и это привело меня к этой позиции.