29.5.3.3. 적분기 누적 포화 방지(Integrator Wind-up Protection): 포화 상태 감지 시 적분 누적을 즉각 정지시키는 안티 와인드업(Anti-windup) 알고리즘

29.5.3.3. 적분기 누적 포화 방지(Integrator Wind-up Protection): 포화 상태 감지 시 적분 누적을 즉각 정지시키는 안티 와인드업(Anti-windup) 알고리즘

비행 제어 시스템에서 적분(Integral, I) 제어는 잔류 오차를 완벽히 제거하기 위해 필수 불가결한 요소이다. 그러나 모터가 낼 수 있는 최대 추력의 한계치에 이미 도달한 상황(포화 상태, Saturation)에서도 제어 오차가 계속 존재한다면, 적분기는 그 오차를 무한정 누적시키게 된다. 이후 조종사가 반대 방향으로 스틱을 쳐서 제어를 회복하려 할 때, 이 거대하게 누적된 ‘적분 빚(Integral Debt)’ 때문에 기체는 한참 동안 반대 방향으로 반응하지 못하고 조종 불능 상태에 빠지게 된다. 이를 “적분기 와인드업(Integrator Wind-up)” 현상이라 부른다.

본 절에서는 PX4-Autopilot의 내부 각속도 제어 루프(mc_rate_control)가 이러한 와인드업 현상을 하드웨어 포화 상태와 연동하여 방지하는 메커니즘을 소스 코드 다이내믹스를 통해 상세히 분석한다.

1. 액추에이터 포화 상태 피드백 (Saturation Status Subscription)

안티 와인드업 로직의 가장 중요한 전제는 “기체의 모터가 지금 한계에 달했는가?“를 제어기가 인지하는 것이다. PX4는 최하단 모터 믹서(Control Allocator) 모듈로부터 모터가 포화되었다는 피드백 플래그를 uORB 메시지로 받아들인다.

// src/modules/mc_rate_control/RateControlMain.cpp 의 믹서 상태 구독

void RateControl::Run()
{
    // ... 자이로 데이터 폴링 및 초기화 로직 생략 ...

    // Control Allocator (믹서) 모듈에서 발행한 제어 표면 상태 토픽 확인
    if (_control_allocator_status_sub.updated()) {
        control_allocator_status_s control_allocator_status;
        _control_allocator_status_sub.copy(&control_allocator_status);

        // 믹서가 하나 이상의 모터 구동 밴드(0~100%)에서 포화되었는지(Clip) 여부 확인
        _mixer_saturated = control_allocator_status.torque_sp_clip_roll ||
                           control_allocator_status.torque_sp_clip_pitch ||
                           control_allocator_status.torque_sp_clip_yaw ||
                           control_allocator_status.thrust_sp_clip;
    }
    // ...
}

위의 로직을 통해 _mixer_saturated 불리언(Boolean) 변수는 기체의 어느 한 축(Roll, Pitch, Yaw, Thrust)이라도 명령된 토크를 하드웨어적으로 낼 수 없어 강제 클리핑(Clipping)이 발생했을 때 true가 된다.

2. 안티 와인드업 정지 메커니즘 로직 (RateControl::update)

포화 상태 피드백 변수는 실질적인 PID 연산을 관장하는 단위 제어 객체 내부에서 다음과 같이 적분 누적을 차단하는 방어막으로 작용한다.

// src/modules/mc_rate_control/RateControl.cpp 적분기 와인드업 가드 클래스

void RateControl::update(const vehicle_rates_setpoint_s &rates_sp, const vehicle_angular_velocity_s &rates)
{
    // 오차 계산 로직 생략 (rate_error 도출)

    // [안티 와인드업 핵심 조건문]
    // 믹서가 포화되지 않은 정상 비행 상태에서만 오차 누적(Integral)을 허용한다.
    if (!_mixer_saturated) {
        // I-term의 핵심: 오차 * 시간(dt)을 적분 저장소에 더함
        _integral_error += rate_error * _dt;
    }
    
    // 이 시점에서 _mixer_saturated가 true라면, _integral_error는
    // 이전 프레임의 값을 그대로 유지(Freeze)하게 되어 더 이상 빚(Wind-up)을 지지 않는다.

    // 누적된 I값(정지되었든 아니든)에 I_GAIN을 곱하여 출력에 합성
    matrix::Vector3f integral_term = _integral_error.emult(_gain_i);
    torque += integral_term;

    // ... P, D 연산 및 최종 토크 배포 로직 생략 ...
}

이 간단한 if (!_mixer_saturated) 조건문은 비행 역학에 치명적인 영향을 미친다. 기체가 강풍에 밀려 모터 4개가 모두 100%로 회전하고 있을 때, 조종사가 바람을 거슬러가기 위해 피치(Pitch)를 더 밀어도 오차가 줄어들지 않는다. 이때 만약 적분기가 돌아가고 있었다면 나중에 숲이나 건물 뒤로 들어가 바람이 멈출 때 기체가 통제 불능으로 고꾸라지거나 급발진하게 되지만, 이 방어 로직 덕분에 바람이 멈추는 즉시 제어 이력이 오염되지 않고 정상 궤도로 안전하게 복귀할 수 있다.

3. 동적 I-Term 한계(Clamp) 설정 및 매개변수 유의점

위의 믹서 포화 검증 외에도, PX4는 내부적으로 파라미터로 세팅된 MC_ROLLRATE_I_MAX 등의 값을 통해 무조건적인 절대 한계치(Clamp) 제한(Limit)을 한 번 더 시행한다.

  • _integral_error의 결과값이 이 I_MAX 값을 초과할 경우, 상한선이나 하한선으로 즉시 자르는(Clip) 이중 방어 로직이 수행된다.
  • Ardupilot과의 차별성: Ardupilot의 비행 제어기(AC_PID)에서는 ‘Stop Windup’ 플래그를 생성할 때, 조종사의 스틱 타겟 방향과 현재 오차의 부호가 반대일 경우(조종사가 오차를 줄이려 반대로 칠 때) 적분 누적을 즉시 허용하는 이완(Relaxation) 메커니즘을 보다 복합적으로 병행 사용한다. 반면 PX4는 철저하게 “하위 컴포넌트인 액추에이터가 멈추면 상층부 누적도 즉시 멈춘다“는 하향식 커플링(Bottom-up Status Feedback) 기반의 직관적인 아키텍처를 우선시한다. 이를 통해 복잡한 상황 판단 논리 없이 수학 모델의 강건성을 확보하려는 설계 철학을 엿볼 수 있다.