PX4 MAVSDK – C++ Programming [5편] 시스템 정보 쿼리 및 원격 측정(Telemetry) 활용

안녕하세요! 마케팅팀의 Aiden(에이든)입니다. 지난 4편에서는 빈 프로젝트에서 CMake를 설정하고 드론과 통신 채널을 연결하여 시스템을 발견(Discovery)하는 과정에 대해 설명드렸습니다.

그렇다면 다음 단계는 무엇일까요? 바로 드론이 현재 어떤 상태인지, 배터리는 얼마나 남았는지, 현재 위치(GPS)는 어디인지 파악하는 것입니다. 자율 비행 알고리즘을 설계하는 연구원들에게 드론의 실시간 센서 데이터를 빠르고 정확하게 수신하는 것은 제어 로직의 핵심 기반이 됩니다.

이번 5편에서는 MAVSDK의 Info 플러그인을 통해 시스템 기본 정보를 쿼리하는 방법과, Telemetry 플러그인을 활용해 수많은 비행 데이터를 비동기(Asynchronous)로 수신하는 방법을 상세히 알아보겠습니다.


드론의 신분증 확인: Info 플러그인 활용

기체에 본격적인 명령을 내리기 전에, 우리가 연결한 드론이 어떤 하드웨어와 펌웨어를 사용하고 있는지 식별해야 할 때가 있습니다. MAVSDK의 Info 플러그인은 기체의 고유 식별자(UUID), PX4 펌웨어 버전, 운영체제(OS) 버전 등의 정적(Static)인 정보를 가져오는 데 사용됩니다.

이러한 정보는 비행 중에 변하지 않기 때문에, 프로그램 시작 시 한 번만 쿼리(Query)해 두면 됩니다.

시스템 정보 쿼리 예제 코드

C++
#include <iostream>
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/info/info.h>

using namespace mavsdk;

// 시스템(드론)이 이미 연결되어 'system' 객체가 있다고 가정합니다.
void print_system_info(std::shared_ptr<System> system) {
    // 1. Info 플러그인 인스턴스 생성
    auto info = Info{system};

    // 2. 정보가 모두 수집되었는지 확인
    while (!info.is_complete()) {
        std::cout << "시스템 정보를 가져오는 중...\n";
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    // 3. 펌웨어 버전 및 하드웨어 UID 출력
    auto version = info.get_version();
    std::cout << "PX4 펌웨어 버전: " 
              << version.flight_sw_major << "." 
              << version.flight_sw_minor << "." 
              << version.flight_sw_patch << '\n';

    auto product = info.get_product();
    std::cout << "하드웨어 제품명: " << product.vendor_name << " " << product.product_name << '\n';
    
    // MAVLink 상속 변경으로 char[4] 타입의 hardware_uid 대신 uid2 사용을 권장합니다.
    auto identification = info.get_identification();
    std::cout << "기체 고유 UID: " << identification.hardware_uid << '\n';
}

💡 팁: 정보 쿼리 전에 info.is_complete()를 사용하여 드론으로부터 모든 버전 정보가 성공적으로 수신되었는지 확인하는 것이 안전합니다.


드론의 신경망: Telemetry 플러그인 개요

Info가 변하지 않는 정보를 제공한다면, Telemetry(원격 측정) 플러그인은 시시각각 변하는 동적인 비행 데이터를 제공합니다. 드론의 신경망이라고 부를 수 있을 만큼 방대하고 중요한 데이터를 다루죠.

연구원 여러분이 논문 구현이나 프로젝트에서 자주 사용하게 될 주요 Telemetry 데이터는 다음과 같습니다.

  • Position: 현재 위도, 경도, 그리고 고도(상대 고도 및 해발 고도 AMSL)
  • VelocityNed / VelocityBody: 기체의 현재 속도 (NED 절대 좌표계 및 기체 기준 Body 좌표계)
  • EulerAngle / Quaternion: 롤(Roll), 피치(Pitch), 요(Yaw) 등 기체의 기울기(자세) 데이터
  • Battery: 남은 배터리 전압 및 잔량(%)
  • Flight Mode: 현재 비행 모드 (오프보드, 임무, 수동 등)
  • Health: GPS 수신 상태, 자이로/가속도계 센서 캘리브레이션 등 비행 적합성 상태


실시간 데이터 처리를 위한 ‘비동기(Asynchronous) 구독’ 모델

MAVSDK가 파이썬의 단순 스크립트 기반 라이브러리들보다 압도적으로 우수한 이유 중 하나가 바로 이 비동기 콜백(Callback) 기반의 구독(Subscribe) 아키텍처입니다.

과거에는 while(true) 루프 안에서 센서 값을 계속 요청(Polling)하느라 CPU 자원이 낭비되거나 프로그램이 멈추는(Blocking) 문제가 잦았습니다. 하지만 MAVSDK에서는 **”새로운 데이터가 들어오면 이 함수(콜백)를 실행해줘!”**라고 한 번만 등록(subscribe_xxx)해두면, 백그라운드 스레드가 알아서 최신 데이터를 인자로 담아 콜백 함수를 실행해 줍니다.

비동기 콜백을 이용한 위치 정보 구독 예제

C++
#include <mavsdk/plugins/telemetry/telemetry.h>

void subscribe_drone_position(std::shared_ptr<System> system) {
    auto telemetry = Telemetry{system};

    // 위치 업데이트 주기 설정 (예: 1초에 1번, 1.0Hz)
    telemetry.set_rate_position(1.0);

    // 비동기 구독 및 람다 함수 콜백 등록
    telemetry.subscribe_position([](Telemetry::Position position) {
        std::cout << "[Telemetry] 고도: " << position.relative_altitude_m << "m | "
                  << "위도: " << position.latitude_deg << " | "
                  << "경도: " << position.longitude_deg << '\n';
    });
}

💡 C++ 문법 팁: 람다(Lambda) 함수 [](){}

위 코드에서 subscribe_position 함수 안에 들어간 [](Telemetry::Position position) { ... } 블록이 바로 2편에서 배운 람다 함수입니다. “위치 정보가 갱신될 때마다 즉석에서 실행할 이름 없는 함수”를 정의한 것이죠. 외부 변수 접근이 필요 없으므로 캡처 블록 []은 비워두었습니다. 이 패턴은 MAVSDK 전체에서 아주 숨 쉬듯이 사용됩니다!

⚠️ 연구원을 위한 핵심 주의사항 (콜백 스레드 관리)

MAVSDK 내부의 모든 사용자 콜백은 기본적으로 단일 스레드에서 순차적으로 호출됩니다. 따라서 위 람다 콜백 함수 내부에서 영상 처리(OpenCV), 복잡한 경로 탐색(A* 알고리즘), 파일 디스크 I/O 등 시간이 오래 걸리는 무거운 작업을 수행해서는 절대 안 됩니다. 콜백에서 시간이 지연되면 다음 Telemetry 데이터 처리가 밀리게 되어 전체 시스템의 반응성(Latency)이 심각하게 저하됩니다. 콜백에서는 가볍게 전역 변수나 클래스 멤버 변수에 최신 값만 덮어쓰고(Update), 무거운 제어 연산은 별도의 메인 작업 스레드에서 수행하는 아키텍처를 권장합니다.


변경 사항 감지 및 이륙 전 안전 검사 (Health Check)

자율 비행 로직을 짜다 보면 “데이터가 무조건 들어올 때마다” 실행하는 것이 아니라, “특정 상태로 변했을 때만” 또는 “특정 상태가 될 때까지 기다려야” 하는 경우가 생깁니다.

1) 비행 모드 변경 감지하기

예를 들어 사용자가 조종기를 통해 ‘수동 모드’에서 ‘오프보드 모드’로 스위치를 넘겼을 때만 알림을 띄우고 싶다면, 이전 상태를 기억해두었다가 비교하는 로직을 람다 함수의 캡처 블록을 활용해 구현할 수 있습니다.

C++
// 비행 모드가 변경될 때만 알림을 주는 콜백
telemetry.subscribe_flight_mode([](Telemetry::FlightMode current_mode) {
    // static 변수를 사용하여 이전 모드를 기억
    static Telemetry::FlightMode last_mode = Telemetry::FlightMode::Unknown;
    
    if (current_mode != last_mode) {
        std::cout << "비행 모드 변경됨: " << current_mode << '\n';
        last_mode = current_mode;
    }
});

2) 이륙 전 필수 검사: health_all_ok()

드론 제어에서 가장 중요한 것은 안전입니다. 드론을 하늘로 띄우기(Takeoff) 전에는 반드시 기체의 센서가 캘리브레이션 되었는지, GPS 위성 신호를 충분히 잡아 홈 위치(Home Position)가 설정되었는지 확인해야 합니다.

이때는 비동기 콜백보다는 메인 코드의 흐름을 잠시 멈추고 기다리는 동기식(Synchronous) 폴링(Polling) 방식이 직관적입니다.

C++
std::cout << "기체 안전 점검 및 GPS Lock 대기 중...\n";

// health_all_ok()가 true를 반환할 때까지 무한 대기 (1초 간격 폴링)
while (!telemetry.health_all_ok()) {
    std::cout << "아직 준비되지 않았습니다. 대기 중...\n";
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

std::cout << "모든 센서 및 GPS 준비 완료! 이륙 가능 상태입니다.\n";

이 단계를 통과해야만 다음 편에서 배울 Action::arm() (모터 시동) 명령이 거부당하지 않고 정상적으로 수행됩니다.


이번 5편에서는 MAVSDK의 InfoTelemetry 플러그인을 활용하여 드론의 시스템 정보를 파악하고, 실시간 위치와 상태 데이터를 비동기 방식으로 효율적으로 수신하는 방법을 배웠습니다.

특히 람다(Lambda) 함수를 이용한 비동기 콜백 모델은 처음에는 낯설 수 있지만, 익숙해지면 드론의 수많은 센서 데이터를 시스템 부하 없이 병렬적으로 처리할 수 있게 해주는 마법 같은 도구입니다.

이제 드론의 상태를 속속들이 읽어낼 수 있게 되었으니, 다음 단계는 마침내 드론을 하늘로 띄우는 것입니다! 다음 [6편: Action API를 이용한 기본 비행 제어]에서는 모터에 시동을 걸고(Arm), 이륙(Takeoff) 후 목표 고도에서 호버링하다가 다시 안전하게 착륙(Land)하는 핵심 비행 시퀀스를 코드로 구현해 보겠습니다.

YouTube 강좌

재생

작성자: 에이든(Aiden), 쿼드(QUAD) 드론연구소 마케팅팀

기고일: 2026.03.15

Similar Posts

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다