28.7.4. 4단계: MAVLink 인터페이스 연동 및 관제 시스템(GCS) UI 반영

28.7.4. 4단계: MAVLink 인터페이스 연동 및 관제 시스템(GCS) UI 반영

PX4 펌웨어 내부에서 FlightTaskCustom 코드가 완벽하게 빌드되고 Commander의 스위칭 허가까지 얻어냈다 하더라도, 이는 기체 내부의 이야기일 뿐이다. 조종사가 들고 있는 지상 관제소(GCS, 예: QGroundControl) 화면에 이 모드를 띄우고, 마우스 클릭 한 번으로 기체에 모드 전환 명령을 내리기 위해서는 통신 프로토콜의 표준인 MAVLink(Micro Air Vehicle Link) 영역을 반드시 관통해야 한다.

본 절에서는 커스텀 모드 추가 파이프라인의 마지막 단계인 MAVLink 통신 메시지 파싱(Parsing) 구조 수정과, QGroundControl(QGC) 프론트엔드/백엔드 코드를 수정하여 최종 UI에 버튼을 노출시키는 지침을 다룬다.


1. MAVLink 프로토콜과 Custom Mode 매핑의 개념

PX4는 GCS와 통신할 때 내부 uORB 상수(NAVIGATION_STATE)를 날것 그대로 보내지 않는다. 기종(PX4, ArduPilot 등)에 상관없이 산업 표준으로 제정된 MAVLink 프로토콜의 HEARTBEAT 메시지와 COMMAND_LONG 메시지로 래핑(Wrapping)하여 통신한다.

특히 비행 모드는 MAVLink 스펙 내의 enum MAV_MODE 에서는 CUSTOM_MODE_ENABLED 라는 큰 껍데기 플래그 하나만 켜두고, 실제 세부 비행 모드는 32비트짜리 custom_mode 필드에 시스템별로 독자적인 숫자를 구겨 넣어 정의한다.

따라서 우리는 이 custom_mode 필드의 특정 비트배열(번호)을 하나 점유하여, 앞서 만든 NAVIGATION_STATE_CUSTOM_FOO 와 1:1로 매핑(Mapping) 시켜주는 양방향 번역기를 튜닝해야 한다.


2. PX4 펌웨어 단 수정: MAVLink Receiver 및 Heartbeat

조종사가 QGC에서 “모드 변경” 버튼을 누르면, 기체의 안테나를 타고 MAV_CMD_DO_SET_MODE 커맨드가 날아온다. 기체 내의 mavlink_receiver.cpp 가 이를 가장 먼저 받아 해독한다.

2.1 수신 파트: mavlink_receiver.cpp 매핑

src/modules/mavlink/mavlink_receiver.cpp 파일 내부의 handle_message_command_long() 함수나 별도의 모드 변경 파서를 찾아, MAVLink로 들어온 커스텀 번호를 uORB 커맨더 상태로 치환하는 분기를 추가한다.

// 예시: MAVLink 수신 처리부 내 매핑 로직
case MAV_CMD_DO_SET_MODE:
	// ... (중략)
	// 커스텀 모드 플래그가 켜져 있을 때 (base_mode 검사 후)
	uint32_t custom_mode = cmd_mavlink.param2; // MAVLink payload에서 번호 추출
	
	switch (custom_mode) {
		// 기존 모드들 매핑
		case PX4_CUSTOM_MAIN_MODE_AUTO:
		// ...
		
		// [NEW] 우리가 선점할 고유 번호 (예: 25번)
		case 25: 
			// 추출한 번호를 내부 uORB 통신용 상수로 치환하여 Commander에 쏜다.
			cmd.param1 = (float)vehicle_status_s::NAVIGATION_STATE_CUSTOM_FOO;
			break;
	}

2.2 송신 파트: vehicle_status \rightarrow HEARTBEAT 인코딩

반대로, 기체가 현재 “내부적으로 커스텀 모드를 돌고 있음“을 1초에 1번씩 QGC로 알려주기 위해서 src/modules/mavlink/mavlink_messages.cpp 내의 Heartbeat 전송 로직도 수정해야 한다.

// mavlink_messages.cpp 내 Heartbeat 스트리머 예시
switch (status.nav_state) {
	// ...
	case vehicle_status_s::NAVIGATION_STATE_CUSTOM_FOO:
		// 내부 상수를 MAVLink payload 용 25번으로 치환하여 방송
		custom_mode = 25; 
		break;
}

3. QGroundControl (GCS) 단 수정: UI 플러그인 연동

기체(PX4) 쪽의 번역기 세팅이 끝났다면, 이제 관제소 프로그램(QGC) 자체의 소스 코드를 열어 C++ 백엔드를 고치고 재빌드(Re-build)해야 한다.

QGroundControl 소스 트리의 src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc 파일은 PX4 기체가 연결되었을 때 이 기체가 어떤 모드들을 가지고 있는지 딕셔너리(Dictionary) 형태로 관리하여 프론트엔드(QML UI)에 메뉴를 띄워주는 핵심 파일이다.

3.1 flightModes() 리스트에 신규 메뉴 등록

PX4FirmwarePlugin.cc 파일을 열고, PX4 기체가 가질 수 있는 모든 커스텀 모드의 리스트가 하드코딩된 부분(FlightModeList 또는 custom_mode 매핑 배열)을 찾는다.

QList<FlightModeInfo_t> PX4FirmwarePlugin::flightModeInfoList()
{
    // QGC 백엔드 코드 내부 예시
    static QList<FlightModeInfo_t> customFlightModeInfoList;
    
    if (customFlightModeInfoList.isEmpty()) {
        // 기존의 Position, Mission 등의 모드가 이곳에서 등록된다.
        customFlightModeInfoList << FlightModeInfo_t{ PX4_CUSTOM_MAIN_MODE_POSCTL, 0, tr("Position"),   true,  false };
        
        // [NEW] 방금 우리가 펌웨어에서 매핑한 통신 번호(25번)와, 
        // 화면에 노출시킬 예쁜 텍스트("Custom Wall Follow")를 연결한다.
        customFlightModeInfoList << FlightModeInfo_t{ 25, 0, tr("Custom Wall Follow"), true, false };
    }
    return customFlightModeInfoList;
}

위와 같이 QGC 백엔드에 텍스트와 번호(25)를 매핑해 두면, 조종사는 더 이상 복잡한 파라미터나 개발자 콘솔을 열 필요가 없다.
QGroundControl 화면 왼쪽 상단의 비행 모드 텍스트를 클릭했을 때 드롭다운 메뉴(Drop-down Menu) 리스트에 “Custom Wall Follow” 라는 버튼이 매끄럽게 노출되며, 이를 클릭하는 순간 파이프라인의 역순(QGC \rightarrow MAVLink \rightarrow Commander \rightarrow FlightModeManager \rightarrow FlightTaskCustom)으로 명령이 흘러가 완벽한 자율제어가 심리스(Seamless)하게 가동된다.