30.3.3.3. 대기(Loiter): 시간 이산화(Time Discretization)에 따른 타이머 인터럽트 및 궤도 이탈 방지 로직
비행 임무(Mission) 수행 중 특정 지점에 도달해 주어진 조건(시간, 회전 수, 또는 무기한) 동안 체공(Hovering)하거나 선회 비행(Orbiting)하는 것을 대기(Loiter) 기동이라 한다. PX4-Autopilot의 내비게이터(Navigator)는 GCS(예: QGroundControl)로부터 수신된 MAV_CMD_NAV_LOITER_TIME, MAV_CMD_NAV_LOITER_TURNS, MAV_CMD_NAV_LOITER_UNL 등의 명령어를 처리할 때, 단순히 하위 위치 제어기(Position Controller)에 좌표를 넘겨주는 선에서 끝내지 않는다. 이 장에서는 mission.cpp 내부에서 대기 시간을 어떻게 이산화(Discretization)하여 추적하는지, 그리고 측풍(Crosswind) 등의 외란으로 인한 궤도 이탈(Orbit Deviation)을 어떻게 수학적으로 억제하는지를 분석한다.
1. 고분해능 타이머(HRT)를 활용한 시간 이산화(Time Discretization)
지상 관제 시스템에서 “해당 웨이포인트에서 15초간 대기하라“는 명령(MAV_CMD_NAV_LOITER_TIME)을 내렸을 때, navigator 모듈은 이 15초를 물리적인 시계열로 변환하여 추적해야 한다.
NuttX RTOS 상에서 동작하는 PX4는 sleep(15)와 같은 스레드 블로킹(Thread-blocking) 함수를 절대 사용하지 않는다. 메인 루프가 차단되면 기체의 자세 제어 연산이 함께 멈추기 때문이다.
대신 mission.cpp는 시스템 부팅 후 경과한 마이크로초 단위의 절대 시간, 즉 고분해능 타이머(High Resolution Timer, HRT)를 바탕으로 비동기적 타이머 폴링(Timer Polling) 기법을 사용한다.
// src/modules/navigator/mission.cpp 내부 시간 측정 로직의 추상화
void Mission::on_active()
{
// ... 기하학적 목표점 도달(Acceptance) 확인 ...
if (is_mission_item_reached()) {
// 도달한 최초 시점에만 타이머 시작점을 기록
if (_time_first_inside_orbit == 0) {
_time_first_inside_orbit = hrt_absolute_time();
}
// 지정된 대기 시간(Time_inside, 초 단위) 경과 검사
hrt_abstime time_elapsed = hrt_absolute_time() - _time_first_inside_orbit;
bool loiter_completed = (time_elapsed >= (hrt_abstime)_mission_item.time_inside * 1_s);
if (loiter_completed) {
advance_mission(); // 대기 완료 시 다음 웨이포인트 실행
}
}
}
이러한 **이산화된 시계열 검사 로직(Discretized Time-series Checking)**은 50Hz 루프(약 20ms 간격)마다 한 번씩 실행된다. 따라서 타임아웃 이벤트는 하드웨어 인터럽트(Hardware Interrupt) 수준의 즉답성을 가지지는 않으나, 항법 제어에 있어 20ms의 오차는 기체 동역학적으로 완전히 무시할 수 있는 수준의 정밀도를 보장한다.
2. 궤도 이탈(Orbit Deviation) 방지 및 복원 로직
고정익(FW) 항공기나 특정 선회 기동이 요구되는 VTOL의 경우, 제자리에서 호버링할 수 없으므로 목표점 반경(R) 내부를 순환(Orbit)하는 궤적 벡터장을 펌웨어 계층에서 형성해야 한다.
-
외란 진입 시 타이머 강제 초기화 (Timer Reset):
기체가 바람에 밀려 정의된 통과 반경(Acceptance Radius, 통상 R) 바깥으로 이탈하게 되면,navigator시스템 내부의is_mission_item_reached()조건문 블록이false로 전환된다.
PX4는 이 때 철저한 보수적 검증 철학을 적용하여, 누적되던 대기 타이머(_time_first_inside_orbit) 수치를 0으로 초기화(Reset)해버리거나, 파라미터 옵션에 따라 “궤도 내부로 다시 진입할 때까지만 일시 정지(Pause)“시킨다. -
구심 벡터 제어 (Centripetal Vector Control):
고정익 위치 제어기(fw_pos_control_l1)는 타겟 반경 R을 유지하기 위하여 L1 유도 법칙(L1 Guidance Law)의 비선형(Non-linear) 피드백 루프를 적용한다. 목표 웨이포인트 \mathbf{P}_{loiter} 를 원점으로 하는 반경 방향의 에러 e_r = || \mathbf{P}_{uav} - \mathbf{P}_{loiter} || - R 에 비례하여 뱅크각(Bank Angle) 명령을 추가하거나 줄임으로써 나선형(Spiral) 이탈을 막고 안정적인 리미트 사이클(Limit Cycle)로 진입시킨다.
3. Loiter 상태 머신 흐름도 (Mermaid Diagram)
다음은 Loiter 시간 명령어에 대한 mission.cpp 계층의 상태 머신 전이를 도식화한 다이어그램이다.
graph TD
A[Fly towards Loiter Target] --> B{Distance < Acceptance Radius?}
B -- No --> A
B -- Yes --> C[Mark Reached, Store hrt_abs_time]
C --> D[Begin Loiter Orbit / Hover]
D --> E{Wind blows UAV out of Radius?}
E -- Yes --> F[Pause/Reset Timer, Correct Trajectory]
F --> B
E -- No --> G{Current Time - Stored Time >= Loiter Time?}
G -- No --> D
G -- Yes --> H[advance_mission: Proceed to Next Waypoint]
4. Ardupilot 대비 구조적 해석 차이
Ardupilot은 전통적으로 회전수 중심(LOITER_TURNS)의 루틴이나 시간 중심의 루틴을 mode_loiter.cpp 또는 mode_auto.cpp 안에서 개별적으로 비대하게 다루는 경향이 짙다.
반면 PX4 모듈은 타이머 인터럽트 로직을 베이스 클래스인 MissionBlock 수준으로 완전히 끌어내려 추상화하였다. 이로 인해 임무 항목(Mission Item)의 열거형 값(Enum)이 턴(Turns)이든 시간(Time)이든 형태만 다를 뿐, 궤도 달성 조건(Acceptance criteria) 판독 모듈 안에서 다형성(Polymorphism)을 활용해 깨끗하게 처리된다.
즉, “수평 내비게이션 상태량“과 “대기 조건 판별 상태량“이 완전히 격리된(Decoupled) 스레드 안전성(Thread-Safe) 코드를 가지며, 이는 학계나 산업계에서 PX4 기반으로 독자적인 Loiter 제어(예: 비전 인식 기반 객체 주변 맴돌기) 모듈을 커스텀하기에 매우 수월한 진입점이 된다.
5. 시사점: 기하학과 시간의 이중적 구속 모델
결론적으로 Loiter 기동은 3차원 공간상의 XYZ 좌표 오차를 제로(0)로 수렴시키는 “공간적 제약(Spatial Constraint)“과, 목표 반경 내부에서 카운터를 누적하는 “시간적 제약(Temporal Constraint)“이 교차로 검증되는 이중 구속(Dual-constraint) 모델이다. QGroundControl 사용자나 자율 비행 알고리즘 개발자는, 기체가 단순히 그 자리에 멈춰 서 있는 것이 아니라 매 20ms 마다 궤도 이탈 여부와 마이크로초 단위의 시계열 오차를 지루하게 심문받으며 궤적으로 되돌아오려는 치열한 PID/L1 피드백 루프의 산물임을 명확하게 인지하여야 한다.