28.5.2. 플래그 조합에 따른 다단(Cascade) PID 제어 루프의 동적 스위칭 아키텍처
PX4-Autopilot의 내부 제어 로직은 단일 수학적 블록으로 이루어져 있지 않으며, 위치(Position), 속도(Velocity), 자세(Attitude), 각속도(Angular Rates)를 순차적으로 계산해 나가는 다단(Cascade) 구조를 취하고 있다. 이러한 계층적 제어 아키텍처가 유연하게 작동할 수 있는 근원적인 이유는 모듈들이 고정된 논리적 순서로 하드코딩된 것이 아니라, uORB 트래픽을 통해 전달되는 제어 플래그(vehicle_control_mode.msg)의 동적 조합(Dynamic Combination/Bitmap)에 의해 스위칭되기 때문이다.
본 절에서는 각 단계별 제어 계층이 어떤 연관된 플래그 비트와 상호 연동하여 활성화되거나 바이패스(Bypass)되는지 다단 제어 루프의 작동 원리를 심층적으로 분석한다.
1. 다단(Cascade) 제어 아키텍처 개요
다단 PID 제어 루프는 “로봇의 위치 오차를 해결하려면 목표 속도를 내야 하고, 목표 속도에 도달하려면 기동력이 필요한데 이를 얻으려면 기체를 기울여야 한다“는 역학적 역질의(Inverse Kinematics/Dynamics) 과정을 수학적으로 체인처럼 구성한 것이다.
1.1 계층별 역할과 연쇄성
- 1단계 위치 제어 (Outer Loop): 목적지와 현재 위치의 차이(오차)에 비례(Proportional)하여 “목표 속도 방향과 크기“를 출력한다 (P-Controller).
- 2단계 속도 제어: 목표 속도와 기체의 실제 속도 간의 차이를 가지고 가속도(또는 추력 벡터)를 산출한다 (PID-Controller).
- 3단계 자세 제어: 앞서 요구된 가속도 벡터를 만들어낼 수 있도록 기체의 롤(Roll), 피치(Pitch), 요(Yaw) 각도를 지정한다 (P-Controller).
- 4단계 각속도 제어 (Inner Loop): 지정된 각도로 신속하게 도달하기 위해 요구되는 회전 각속도를 산출하고, 믹서를 거쳐 모터 제어량(PWM)을 구한다 (PID-Controller).
이러한 수직 계열화된 구조에서는, 특정 비행 모드에서 어느 계층까지만 활성화하고 아래의 계층으로 데이터를 넘길지가 가장 큰 기술적 관건이다. PX4는 이 연결 스위핑을 제어 플래그(Control Flags)를 통해 우아하게(Elegantly) 제어한다.
2. 제어 플래그를 통한 동적 계층 트리거 메커니즘
vehicle_control_mode 토픽의 플래그는 기본적으로 상위 계층의 활성화가 하위 계층을 자동으로 필요로 하는 종속적 특성(Dependency) 을 갖는다.
2.1 상향식 종속 모델 (Downward Dependency)
만약 커맨더(Commander) 또는 비행 모드 관리자(Flight Mode Manager)가 기체를 “Position” 모드로 설정했다면, 다음과 같은 플래그 상태 머신이 작성된다.
flag_control_position_enabled = trueflag_control_velocity_enabled = true(위치 오차를 해결하기 위해 속도 제어 필요)flag_control_attitude_enabled = true(속도 제어를 위해 자세 제어 필요)flag_control_rates_enabled = true(자세 제어를 위해 각속도 제어 필요)
멀티로터의 위치 제어 전담 모듈인 mc_pos_control 은 updatePositionController() 내에서 위치 플래그를 먼저 확인한 후, 속도 플래그가 참이면 자신이 계산한 velocity_setpoint 를 다음 단계인 updateVelocityController() 의 인자로 넘겨 흐름을 지속한다.
2.2 플래그 조합에 따른 루프 절단기 (Loop Truncation)
반대로, “Stabilize” 모드와 같이 순수하게 기체의 자세(Attitude)만 자동으로 제어하고 조종사가 전후좌우의 이동 스피드를 직접 물리적 직감형태로 통제하는 경우, 상위 플래그들은 false로 닫히게 된다.
flag_control_position_enabled = falseflag_control_velocity_enabled = false- 조종사의 스틱 입력 \rightarrow 목표 자세(Target Attitude)로 직접 사영(Mapping)됨.
flag_control_attitude_enabled = true시작flag_control_rates_enabled = true끝
이렇게 되면 mc_pos_control은 자신에게 할당된 플래그가 전무하므로 P-Controller (위치/속도 연산) 엔진을 아예 유휴(Sleep) 상태로 두어 연산 리소스(CPU Time)를 아끼고, mc_att_control 만이 자세 오차를 보상하기 위한 루프를 열심히 가동한다.
3. 동적 스위칭(Dynamic Switching) 아키텍처의 C++ 로직 해부
이러한 동적 릴레이 방식은 코드를 어떻게 분기하고 있는지를 보면 더 명확해진다. 멀티로터의 메인 제어 블록은 루프마다 다음 세트로 플래그를 판별한다. (의사 코드)
// 1. Position -> Velocity 릴레이 (mc_pos_control)
void MulticopterPositionControl::Run() {
if (_vcontrol_mode.flag_control_position_enabled) {
// 위치 제어 수행 후 결과값 전달
_vel_sp = ControlPosition(_pos_sp, current_pos);
} else {
// 위치 루프가 스킵되었으므로, 다른 곳(RC나 Offboard)에서 들어온
// 속도 목표값이 있는지 확인한다.
_vel_sp = get_external_velocity_setpoint();
}
if (_vcontrol_mode.flag_control_velocity_enabled) {
// 입력받은 속도 목표(_vel_sp)로 속도 제어 수행
_att_sp = ControlVelocity(_vel_sp, current_vel);
publish_attitude_setpoint(_att_sp); // 하위 자세 루프로 목표 전달
}
}
// 2. Attitude -> Rates 릴레이 (mc_att_control)
void MulticopterAttitudeControl::Run() {
if (_vcontrol_mode.flag_control_attitude_enabled) {
// mc_pos_control에서 받은 목표 자세(Attitude)를 기반으로 P 제어
_rates_sp = ControlAttitude(_att_sp, current_att);
} else {
// 자세 제어가 꺼져있다면 (예: Acro 모드) 조종사 입력을 즉시 각속도로 맵핑
_rates_sp = get_manual_rates_setpoint();
}
if (_vcontrol_mode.flag_control_rates_enabled) {
ControlRates(_rates_sp, current_rates); // 실제 모터 PWM 신호 생성 단으로 이행
}
}
위의 로직에서 보듯, 제어 흐름(Control Flow)은 플래그의 상태에 따라 mc_pos_control의 연산이 특정 블록을 스킵(Skip)하고 바로 아랫단 블록으로 점프하거나, 아예 상위 프로세스 전체를 완전히 바이패스하고 RC 입력 등을 직접 rates_sp에 꽂아 넣는 플러거블 (Pluggable) 형상을 갖추고 있다.
4. ArduPilot과의 아키텍처 체계 차이 분석
이러한 메시지 퍼블리싱/서브스크라이빙 비트맵(Bitmap) 모델은 다른 오픈소스 컨트롤러와 가장 이질적인 부분이다.
| 특징 | PX4 (다단 모듈러 플래그 방식) | ArduPilot (상속형 Method Call 방식) |
|---|---|---|
| 제어 전개 방식 | 각 컨트롤러 쓰레드가 uORB 플래그를 관찰하며 자신이 계산할 타이밍을 개별 결정. 데이터는 토픽(Topic)으로 흐름. | mode_guided.cpp 등이 pos_control->update_xy_controller() 함수를 직접 연쇄 호출하여 C++ 객체 내부 변수를 바꿈. |
| Bypass의 용이성 | ROS2 노드 등 외부의 제3 시스템이 플래그만 조작하고 데이터를 중간에 찔러넣으면(Injection), 네이티브 소스코드를 고치지 않아도 특정 단계를 바이패스 가능함. | 제어 객체가 꽉 물려(Tightly Coupled) 있어, 소스 코드를 심하게 파고들어 분기문을 해킹하지 않으면 중간 루프 절단이나 외부 직접 개입이 까다로움. |
| 모드 개발 효율성 | 단일 제어 루프 아키텍처에 플래그만 변경하므로 중복 코드가 없음 (DRY 철학). | 새로운 비행 모드를 만들 때마다 상위 \rightarrow 하위 루프 체인을 mode_xx.cpp 안에서 처음부터 일일이 선언(Binding)해주어야 하는 경향이 있음. |
결론적으로 PX4의 플래그 조합 기반 다단 스위칭 아키텍처는, 시스템을 구성하는 가장 작은 수학적 단위(제어 블록)들을 레고 블록처럼 분해하고, vehicle_control_mode라는 조립 설명서를 통해 매 초 수십 번씩 자유롭게 재조합할 수 있게 해주는 결정적 객체지향 분산 런타임 시스템(Decoupled Runtime System) 이라고 평가할 수 있다.