PID 제어 : 다음 루프 전에 지연을 추가하는 것이 좋은 생각입니까?

Jan 11 2021

차동 드라이브 로봇이 정확한 각도로 회전하도록 C ++에서 PID 제어를 구현하고 있지만 많은 문제가 있습니다.

빠른 루프 런타임으로 인해 제어 루프를 일찍 종료

로봇이 .5도 미만의 오차를 측정하면 제어 루프를 종료하고 턴을 "완료"한 것으로 간주합니다 (.5는 어떤 시점에서 변경할 수있는 임의의 값입니다). 제어 루프가 너무 빨리 실행되어 로봇이 매우 빠른 속도로 회전하고, 설정 값을 넘어서고, 루프 / 절단 모터 전원을 종료 할 수있는 것 같습니다. 왜냐하면 잠시 동안 설정 값에 있었기 때문입니다. 이것이 오버 슈트없이 정확하게 설정 값에 도달하는 PID 제어의 전체 목적이라는 것을 알고 있지만이 문제는 PID 상수를 조정하는 것을 매우 어렵게 만듭니다. 예를 들어, 일정한 진동이있는 kp 값을 찾으려고하지만 로봇이 설정 값을 통과하면 "완료"되었다고 생각하기 때문에 진동이 전혀 없습니다. 이 문제를 해결하려면나가기 전에 일정 시간 동안 로봇이 설정 값에 있어야하는 시스템을 구현했는데 이것이 효과적이어서 진동이 발생할 수 있지만 루프를 일찍 나가는 문제는 비정상적인 문제로 보이며 내 해결책 정확하지 않을 수 있습니다.

D term은 빠른 런타임으로 인해 영향을 미치지 않습니다.

P 만 사용하여 제어 된 방식으로 로봇을 진동하게 한 후 오버 슈트를 방지하기 위해 D를 추가하려고했습니다. 그러나 이것은 대부분의 시간 동안 아무런 영향을 미치지 않았습니다. 제어 루프가 너무 빨리 실행되어 20 개 중 19 개 루프가 발생했기 때문에 오류 변화율은 0입니다. 로봇이 움직이지 않았거나 충분히 움직이지 않았습니다. 그 시간에 측정됩니다. 이를 확인하기 위해 오류의 변화와 각 루프의 미분 용어를 인쇄했으며 합리적인 값을 취하기 전에 약 20 루프 사이클 동안 0이되고 또 다른 20 사이클 동안 0으로 돌아 간다는 것을 알 수있었습니다. 제가 말했듯이, 이것은 루프 사이클이 너무 빨라서 로봇이 말 그대로 어떤 종류의 눈에 띄는 오류 변화에도 충분히 움직이지 않았기 때문이라고 생각합니다.이것은 D 항이 거의 항상 0이기 때문에 로봇 움직임에 본질적으로 영향을 미치지 않았기 때문에 큰 문제였습니다.이 문제를 해결하기 위해 0 값 대신에 0이 아닌 마지막 파생 값을 사용해 보았습니다. 그러나 이것은 잘 작동하지 않았고 마지막 미분이 현재 오류 변화율을 나타내지 않으면 로봇이 무작위로 진동 할 것입니다.

참고 : 또한 정적 마찰 계수에 대해 작은 피드 포워드를 사용하고 있으며이를 피드 포워드 "f"라고합니다.

지연을 추가해야합니까?

저는이 두 가지 문제의 원인이 매우 빠르게 실행되는 루프라고 생각했기 때문에 루프 끝에 wait 문을 추가하는 것이라고 생각했습니다. 그러나 의도적으로 루프 속도를 늦추는 것은 전반적으로 나쁜 해결책처럼 보입니다. 이것이 좋은 생각입니까?

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

답변

3 UgoPattacini Jan 12 2021 at 05:14

컨트롤러를 분리하지 마십시오.

컨트롤러의 목적은 미리 결정된 동적 응답에 따라 시스템을 원하는 설정 값으로 조정하는 것뿐만 아니라이 작업을 방해 할 수있는 잠재적 인 외부 요인에 대응하는 것입니다. 일단 도달하면 시스템을 설정 값에서 멀리 떨어 뜨릴 방해를 생각해보십시오. 따라서 컨트롤러는 항상 작동해야합니다 (작업 자체를 변경하고 결과적으로 컨트롤러도 변경해야하는 경우 제외).

이를 위해서는 모델링되지 않은 수량과 외부 장애가있을 때 null 정상 상태 오류를 발생시키는 통합 부분을 추가해야합니다.

이것은 상당히 관련있는 리소스입니다. https://robotics.stackexchange.com/a/19198/6941.

미분은 제외하십시오.

산업에서 PID 컨트롤러의 95 %는 PI 컨트롤러입니다 ( "피드백 시스템"Astrom, Murray 참조). D 부분은 느린 프로세스에서만 중요한 역할을 할 수 있습니다 (예 : 온도 및 탱크 레벨 조절 관련). 이것은 확실히 당신의 경우가 아닙니다. 파생 용어의 어려움에 대한 관련 리소스는 다음과 같습니다.

  • https://robotics.stackexchange.com/a/21556/6941.
  • https://robotics.stackexchange.com/a/21555/6941.

P 만 사용하여 제어 된 방식으로 로봇을 진동하게 한 후 오버 슈트를 방지하기 위해 D를 추가하려고했습니다.

음, 컨트롤러를 조정하기 위해 Ziegler-Nichols의 처방을 따르는 것 같습니다. 이익에 대한 추정치를 제공하기 위해 고수해야 할 표가 있습니다. 그러나 이들은 휴리스틱이며 귀하의 경우에는 작동하지 않을 가능성이 있습니다.

D 부분을 떨어 뜨리고 PI 컨트롤러에 집중하기 만하면됩니다. 의도적으로 생성 된 진동에 의존하지 않는 다른 ZN 변형도 있습니다.https://robotics.stackexchange.com/a/21616/6941.

시스템에 지연을 추가하지 마십시오.

지연은 제어 루프에서 처리 하기에는 나쁜 사람이며, 최악의 엔지니어는 전체 시스템을 불안정하게 만드는 위상 마진을 크게 줄 이므로 싸워야하는 최악의 짐승과 싸워야 합니다.

루프가 너무 빠르다고 생각되면 입력 모양을 설정 점 (예 : 최소 저크 궤적 사용 )에 적용하여 날카로운 단계 시작을 부드럽게합니다. 폐쇄 루프 시스템은 더 우아하게 반응합니다. 또 다른 가능성은 P 게인이 너무 높다는 것입니다. 컨트롤러를 디 튜닝하면됩니다.

이 점에서 적분 항 I을 삽입하면 샘플 시간에 대해 추론해야합니다. $T_s$ 게다가.

1 TimWescott Jan 12 2021 at 08:36

컨트롤러에 대한 고정 샘플 속도를 결정하는 것이 훨씬 낫습니다. 정말 대략적인 경험 법칙은 일단 선형 영역에서 작동 하면 루프에서 필요한 정착 시간이 무엇이든 샘플링 간격은 정착 시간보다 10 배에서 100 배 작아야한다는 것입니다. 다시 말해, 샘플 속도는 원하는 루프 대역폭보다 10 ~ 100 배 더 빨라야합니다.

약간의 추가 보너스로 루프 외부에서 dt를 계산할 수 있습니다.

선호에 따라 샘플 속도는 하드웨어 (예 : 하드웨어 타이머)에 의해 결정되어야하며 위치 샘플링은 타이머에서 트리거되는 하드웨어에 의해 수행되어야합니다. 이것은 소프트웨어에 대한 실시간 제약을 상당히 완화시킵니다.

합리적인 샘플 속도를 사용하고 있고 대부분 인코더에 변경 사항을 등록하지 않는 경우 인코더에 충분한 단계가없는 것입니다.

루프에 모터가 포함되어 있고 컨트롤러가 충분히 깨끗한 경우 일부 차동 동작이 도움이 될 수 있다는 점에서 @Ugo_Pattachini에 동의하지 않을 것입니다. 그러나 대역 제한 (예 : 리드 지연 컨트롤러가 필요함)이 필요할 가능성이 있으며 바지 PID 튜닝을 수행하는 경우 도구가 없을 가능성이 큽니다. 대역 제한을 올바르게 설정하십시오.

그러나 인코더 샘플링에 매우 엄격한 타이밍을 적용 할 수 없다면 미분 제어를 사용하지 마십시오. 불규칙한 샘플링 시간과 모터의 약간의 속도는 추가 소음을 발생시키고 미분 컨트롤러는 소음을 증폭시키는 경향이 있습니다. 깨끗한 신호를 제어 할 수 없으면 미분 제어가 적합하지 않습니다.