# 1. 가상 함수(Virtual Function)를 활용한 비행 모드별(Mission, Loiter, RTL) 공통 제어 인터페이스 설계
C++와 같은 객체 지향 프로그래밍(OOP) 언어에서 **다형성(Polymorphism)**과 **가상 함수(Virtual Function)**는 거대하고 파편화되기 쉬운 시스템 소프트웨어 아키텍처를 하나로 통일시키는 강력한 매개체이다. PX4-Autopilot의 내비게이션 비행 제어기(navigator) 역시 자율 비행 모드(Mission, Loiter, RTL 등)의 다양성과 유지 보수성을 동시에 잡기 위해 이 가상 함수 기반의 공통 제어 인터페이스(Common Control Interface) 설계를 적극적으로 차용하고 있다.
본 절에서는 추상 베이스 클래스인 NavigatorMode와 이를 1차적으로 구체화하는 MissionBlock 계층, 그리고 최종 종단 파생 클래스(상세 비행 모드) 간에 어떻게 C++ 가상 함수(virtual) 키워드가 동작하여 코드 중복을 제거하고 제어망을 통일하는지 소스 코드 패턴 구성을 중점적으로 분석한다.
1.1 자율 비행 상태 머신(FSM) 모드의 인터페이스 파편화 딜레마
임무 프로파일을 따라 이동하는 Mission, 제자리에 멈춰 서는 Loiter(Hold), 이륙 지점으로 비상 회귀하는 RTL 모드는 모두 3차원 공간을 제어한다는 공통된 목표를 지니지만 그 내부 산술 메커니즘은 매우 이질적이다.
Mission모드는 SD 카드로부터 웨이포인트 스택을 지속적으로 인출(Fetch)하고, 수용 반경(Acceptance Radius) 통과 여부를 검사해야 한다.Loiter모드는 외부 데이터 인출 없이, 파라미터로 지정된 체공 고도(Loiter Altitude)와 현재 위치 좌표를 락킹(Locking)하여 지속적으로 바람 외란을 상쇄하는 제자리 궤적만을 고집한다.RTL모드는 고도 상승(Climb) \rightarrow 회귀 비행(Return) \rightarrow 하강(Descend) \rightarrow 착륙(Land)이라는 복합적인 4단계 자체 서브 상태 머신(Sub-FSM) 코드를 내장해야 한다.
과거 레거시 펌웨어 아키텍처는 이를 제어하기 위해 메인 루프 안에 거대한 switch-case 분기문을 두어 각 상태마다 서로 다른 이름의 커스텀 함수(run_mission(), do_loiter() 등)를 하드코딩으로 무한 호출했다. 이는 특정 비행 모드에 치명적 버그가 발생했을 때 전체 메인 루프가 교착 상태(Deadlock)에 빠질 위험성을 동반하는 안티 패턴이었다.
1.2 NavigatorMode 베이스 클래스의 순수 가상 함수(Pure Virtual Function) 규약
PX4는 이 구조적 파편화를 타파하기 위해 최상위 유닛인 NavigatorMode라는 추상 클래스(Abstract Class)를 선언하고, 모든 비행 모드 객체가 시스템 인터페이스에 결합되기 위해 반드시 구현해야만 하는 순수 가상 함수(Pure Virtual Function) 규약을 마련했다.
// src/modules/navigator/navigator_mode.h
class NavigatorMode
{
public:
NavigatorMode(Navigator *navigator);
virtual ~NavigatorMode() = default;
// 생명주기 및 궤적 제어 인터페이스 (순수 가상 함수)
virtual void on_inactive() {} // 비활성화 상태 대기 연산 (Optional)
virtual void on_activation() = 0; // 모드 최초 진입 초기화 (Mandatory)
virtual void on_active() = 0; // 50Hz 폴링 주기의 핵심 궤적 산출부 (Mandatory)
virtual void on_inactivation() = 0; // 모드 이탈 및 메모리 정리 (Mandatory)
};
C++ 문법의 virtual void ... = 0; 선언은 파생 클래스로 하여금 해당 함수의 몸체를 강제로 구현(Override)하도록 컴파일 타임에 압박을 가한다. 이로써 메인 마스터 인스턴스인 Navigator 컨트롤러 루프는 자신이 현재 어떤 구체적인 비행 모드를 소유하고 있는지 전혀 의식(Awareness)할 필요가 없다.
마스터는 그저 현재 할당된 NavigatorMode* 포인트를 향해 맹목적으로 ->on_active() 가상 함수만을 초당 수십 회씩 트리거할 뿐이다. V-Table(Virtual Table)에 의한 동적 바인딩(Dynamic Binding) 메커니즘을 통해, 포인터가 가리키는 실제 메모리 인스턴스가 Mission 객체인지 RTL 객체인지에 따라 독립된 타겟 산술 코드가 정확하고 안전하게 실행된다.
1.3 MissionBlock을 통한 데이터 접근 코드의 구체화(Instantiation)
하지만 모든 모드를 100% 분리 독립시키면, “좌표를 받아 설정점(Setpoint) 객체로 번역하는” 반복적인 보일러플레이트(Boilerplate) 코드가 각 파일에 복사+붙여넣기 되는 또 다른 낭비가 초래된다. 이를 중재하는 것이 중간 베이스 계층(Intermediate Base Class)인 **MissionBlock**의 가상 멤버 설계 기법이다.
graph TD
A[NavigatorMode <br/> 추상 베이스] -->|on_active() 등 <br/> FSM 껍데기 인터페이스| B[MissionBlock <br/> 중간 관리자]
B -->|mission_item_to_position_setpoint() <br/> 구체화된 공용 투영 함수| C(Mission)
B -->|set_loiter_item() <br/> 공용 좌표 제동 함수| D(Loiter)
B -->|set_land_item() <br/> 공용 착륙 벡터 함수| E(RTL)
MissionBlock은 NavigatorMode의 자식이자 Mission, RTL의 부모로서 설계되었다. 이 계층에서는 on_active()와 같은 모드 특화형 순수 가상 함수는 오버라이드하지 않고 그대로 하위로 패스(Pass-through)시킨다. 대신, 앞서 언급한 WGS84 좌표계에서 NED 좌표계로의 변환 투영 함수, 웨이포인트 수용 반경(Acceptance Radius) 검사 함수 같이 모든 자율 비행 모드가 밥 먹듯이 호출해야 하는 유틸리티성 함수들의 몸체(Body)를 구체적으로 선언해 둔다.
결국 종단 클래스인 Mission 파생 객체의 on_active() 내부 코드에서는 궤적을 쏘아 보낼 때 억지로 좌표 변환 삼각함수를 코딩할 필요 없이, 투명하게 상속받은 부모의 this->mission_item_to_position_setpoint()를 단절 없이 호출하여 메모리 이득과 객체 응집도(Cohesion)를 최고조로 끌어올리게 된다.
1.4 다형성(Polymorphism)을 통한 신규 비행 모드 확장성(Scalability) 확보
이렇듯 잘 설계된 가상 함수형 공통 제어 인터페이스 생태계는 PX4-Autopilot이 왜 전 세계 드론 연구기관(University Labs) 및 민간 기업에서 가장 선호되는 “확장 가능한(Scalable)” 오픈소스 백본인지 증명하는 대목이다.
만일 특정 대학 연구소에서 카메라 짐벌이 목표물을 항상 지향하게끔 나선형(Spiral)으로 하강 탐색하는 SpiralSearch라는 특수한 임무 모드를 만들어 추가하고 싶다고 가정해 보자.
Ardupilot 환경에서는 시스템 열거형(Enum)에 새 모드 ID를 추가하고, 거대 폴링 루프 곳곳에 스위치 케이스를 심절하며, 모드 배열 크기를 수동으로 넓히는 고된 레거시 스파게티 작업이 요구될 수 있다.
하지만 PX4 아키텍처 하에서는 다음과 같은 단 두 가지의 엘리건트(Elegant)한 단계로 종결된다.
class SpiralSearch : public MissionBlock의 C++ 헤더를 생성하고, 강제된 가상 함수on_activation(),on_active()내부 산술만 개별 설계한다.Navigator메인 초기화 루프 배열 포인터에 런타임 다형성 업캐스팅(Up-casting)을 이용해new SpiralSearch(this)로직 한 줄만 적재 장착한다.
이러한 V-Table 기반 커스텀 펌웨어 개발론은 베이스 코어 시스템(NuttX RTOS 메인 루프, 통신 채널)의 강건함은 한 치의 타협 없이 방어하면서, 사용자 목적 함수(User Logic)만을 극치로 고립시켜 마음껏 커스터마이징하게 허용하는, 우아한 항공 로보틱스 소프트웨어 패턴의 모범 답안이라 할 수 있다.