28.7.3. 3단계: Commander 및 Flight Mode Manager에 신규 컴포넌트 등록 및 스위칭

28.7.3. 3단계: Commander 및 Flight Mode Manager에 신규 컴포넌트 등록 및 스위칭

PX4 시스템 트리의 가장 깊은 곳, FlightTaskCustom 클래스라는 독립적인 ’뇌(Brain)’를 완성하고 CMake 빌드까지 마쳤다면 절대로 기체가 알아서 그 뇌를 구동해주지 않는다.

마치 완성된 엔진을 자동차(기체)에 얹기 위해서는 ECU(Commander)에 엔진의 존재를 등록하고, 변속기(Flight Mode Manager)가 이 엔진으로 동력을 스위칭(Switching)할 수 있도록 물리적인 배선을 연결해 주어야 하는 것과 같다. 본 절에서는 3단계 과정인 코어 모듈 편입 및 상태 전환(State Transition) 배선 작업의 핵심을 다룬다.


1. Flight Mode Manager 배전반(Switchboard) 등록

FlightModeManager 모듈 시스템은 수많은 비행 모드(FlightTask) 객체들을 관리하는 공장장이다. 기체가 부팅될 때 이 공장장에게 우리의 신규 클래스를 메모리에 인스턴스화(Instantiation)할 수 있도록 설계도를 쥐여주어야 한다.

1.1 FlightTaskIndex 상수 등록

우선 시스템이 배열을 통해 Task 객체들을 순회할 수 있도록 인덱스 번호를 뚫어준다. src/modules/flight_mode_manager/tasks/FlightTaskIndex.hpp 파일을 열어 Custom 모드를 추가한다.

// FlightTaskIndex.hpp 내부
enum class FlightTaskIndex : int {
	None = -1,
	ManualPosition,
	AutoLineSmoothWaypoints,
	AutoFollowTarget,
	// ... (기존 모드 생략) ...
	Custom,           // [NEW] 커스텀 모드 추가
	Count            // Count는 항상 맨 밑에 있어야 함
};

1.2 FlightModeManager 팩토리(Factory) 패턴 스위칭

가장 결정적인 부분이다. FlightModeManager.cpp 내부에는 switch-case 문배열을 통해 uORB로 들어온 nav_state를 바라보고 어떤 FlightTask 객체로 컨텍스트 스위칭(Context Switching)할지 결정하는 공장 라인(switchTask())이 존재한다.

여기에 수동으로 분기점을 하나 판다.

#include "tasks/Custom/FlightTaskCustom.hpp"  // 1. 헤더 포함

// ... 중략 ... FlightModeManager::Run() 등 내부 스위치 문

	case vehicle_status_s::NAVIGATION_STATE_CUSTOM_FOO: // 1단계에서 만든 상수
		// _flight_tasks 배열의 알맞은 인덱스에 새로운 뇌를 가동시킨다.
		_flight_tasks.switchTask(FlightTaskIndex::Custom);
		break;

이 코드가 삽입됨으로써, 외부에서 NAVIGATION_STATE_CUSTOM_FOO 신호가 들어오면 FlightModeManager는 기존에 돌던 위치 제어 로직을 empty() 함수로 정리해 버리고, 우리가 짠 FlightTaskCustom::initialize() 함수를 호출하여 제어권을 넘겨버리게 된다.


2. Commander: 허가(Permission)와 보안관 설정

엔진(FlightModeManager)을 갈아 끼울 준비가 모두 끝났다면, 운전자가 그 버튼을 눌러도 되는지 감시하는 최종 보안관 역할을 commander 모듈이 담당한다. 드론이 공중에 떠 있는데 GPS가 끊긴 상태에서 자율 비행 모드를 켜면 치명적인 추락 사고로 이어지기 때문이다.

src/modules/commander/Commander.cpp (혹은 state_machine_helper.cpp 등 버전별 메인 상태 천이 로직 파일) 내부의 main_state_transition() 함수를 수정해야 한다.

2.1 진입 조건(Prerequisites) 분기 추가

커스텀 모드의 수학적 모델이 GPS 데이터를 필수적으로 요구하는 로직인지, 아니면 IMU와 고도계만 가지고도 작동하는 로직인지에 따라 commander의 엄격한 가드(Guard) 분기를 설정한다.

// commander 내부 main_state_transition() 함수 예시

bool transition_allowed = false;

switch (main_state_req) {
	// ... 기존 모드 판별 문 ...

	case commander_state_s::MAIN_STATE_CUSTOM_FOO: // Commander용 상태 머신 상수 존재 가정
		// [조건 검사 로직]
		// 1. 전역 위치(Global Position) 즉 GPS가 필요한 로직인가?
		// 2. 비행기가 Arming 되어 허공에 떠 있는가?
		if (status.is_rotary_wing && status_flags.condition_global_position_valid) {
			transition_allowed = true; // 통과! 커스텀 모드 이행 승인.
		} else {
			// GPS가 들어오지 않거나, 콥터 모드가 아니라면 거부(Reject)하여 기체를 보호한다.
			transition_allowed = false;
			mavlink_log_critical(&mavlink_log_pub, "Custom Mode denied! No global position.");
		}
		break;
}

이 분기를 거칠게 뚫어놓지 않으면, 조종사가 QGroundControl에서 ‘Custom Mode’ 버튼을 마우스로 아무리 광클해도 Px4 시스템은 스피커로 “부~” 하는 경고음만 내면서 모드를 전환해 주지 않는다.


3. 요약: 견고하게 설계된 삼중 안전망

PX4-Autopilot에 새로운 비행 객체를 등록하고 연결하는 3단계 과정을 아키텍처 관점에서 돌이켜보면 이 시스템이 왜 군수용 및 상업용 UAM(Urban Air Mobility) 제어의 표준기반으로 꼽히는지 그 진면목이 드러난다.

단순히 if (mode == CUSTOM) { run_this(); } 식의 해킹(Hacking)을 허용하지 않고, 객체 지향 패턴 \rightarrow 공장(Factory) \rightarrow 보안관(Commander Guard)으로 이어지는 삼중 안전망 구조를 통해, 비숙련 개발자가 짠 다소 엉성한 커스텀 모드라 할지라도 기체의 근본적인 안전(Arming, Sensor validation) 체계를 무너뜨리지 않도록 보호막을 두껍게 치고 있는 것이다.