PX4 MAVSDK – C++ Programming [6편] Action API를 이용한 기본 비행 제어
안녕하세요! 마케팅 팀의 에이든(Aiden)입니다. 지난 5편에서는 드론의 신경망이라고 할 수 있는 Telemetry 플러그인을 통해 기체의 현재 상태와 GPS 위치를 비동기 콜백으로 수신하는 방법을 알아보았습니다.
센서 데이터를 성공적으로 읽어왔다면, 이제 드디어 기체에 물리적인 움직임을 명령할 차례입니다! 자율 비행 로보틱스 연구에 있어서 드론을 원하는 시점에 정확히 이륙(Takeoff)시키고 안전하게 착륙(Land)시키는 것은 모든 알고리즘의 시작과 끝을 담당하는 가장 핵심적인 제어입니다.
이번 6편에서는 MAVSDK의 Action 플러그인을 활용하여 비행 전 안전 점검부터 시동, 이륙, 착륙, 그리고 귀환(RTL)까지 이어지는 기본 비행 사이클을 C++ 코드로 완벽하게 제어하는 방법을 친절하게 설명해 드리겠습니다.

Action 플러그인 개요 및 중요 설계 철학
Action 클래스는 드론(차량)에 시동(Arm), 무장 해제(Disarm), 이륙(Takeoff), 착륙(Land), 복귀(RTL) 및 VTOL 모드 전환 등의 기본적이고 필수적인 동작을 명령하는 데 사용됩니다.
명령을 내리기 전에 MAVSDK Action API가 가진 매우 중요한 응답 설계 철학을 이해해야 합니다.

⚠️ “명령 수락(Accepted)”과 “동작 완료(Completed)”의 차이
Action 플러그인의 대부분의 메서드는 동기식(Synchronous)과 비동기식(Asynchronous) 버전을 모두 제공합니다. 이 메서드들을 호출하여 반환받는 결과(Action::Result::Success)는 기체가 해당 명령을 수락하고 행동할 의도가 있음을 의미하는 것이지, 그 동작이 끝났다는 것을 의미하지 않습니다.
예를 들어, action.takeoff()를 호출하여 Success를 반환받았다고 해서 드론이 이미 목표 고도에 도달해 호버링하고 있는 것이 아닙니다. 드론은 이제 막 바닥에서 떠오르기 시작했을 뿐입니다. 따라서 연구원 여러분은 명령 전송 후, 기체가 물리적인 동작을 완료했는지 별도의 코드(예: 텔레메트리 고도 확인)를 통해 모니터링해야 합니다.
비행의 첫 단추: 안전 점검과 시동 (Arming)
드론의 프로펠러가 고속으로 회전하는 것은 매우 위험하므로, PX4 오토파일럿은 기체가 ‘정상 상태’일 때만 시동을 허용합니다. 여기서 정상 상태란 센서 캘리브레이션이 완료되고, 홈 위치가 설정되었으며, GPS 신호 품질이 충분히 확보된 상태를 말합니다.
1) 기체 상태 확인 (Health Check)
시동을 걸기 전에 Telemetry 플러그인의 health_all_ok() 메서드를 사용하여 시스템이 비행 준비가 되었는지 동기적으로 폴링(Polling)하며 대기하는 것이 일반적인 워크플로우입니다.
// 텔레메트리를 통해 기체가 시동 가능한 상태인지 확인
while (!telemetry.health_all_ok()) {
std::cout << "시스템 준비 대기 중... (GPS Lock 및 센서 캘리브레이션 확인)\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "시스템 비행 준비 완료!\n";
2) 시동 (Arming)
기체 상태가 양호해졌다면, action.arm() 메서드를 호출하여 모터에 시동을 겁니다. 반환값이 Action::Result::Success라면 모터가 회전하기 시작하며 이륙할 준비가 끝난 것입니다.
std::cout << "모터 시동 중...\n";
const Action::Result arm_result = action.arm();
if (arm_result != Action::Result::Success) {
std::cerr << "시동(Arming) 실패: " << arm_result << '\n';
return 1; // 실패 시 프로그램 안전 종료
}
std::cout << "시동 성공! 프로펠러 회전 중.\n";
하늘로 날아오르기: 이륙 (Takeoff)
기체에 시동이 걸렸다면 하늘로 날아오를 차례입니다. MAVSDK(및 PX4)를 사용하여 이륙하는 가장 권장되는 방법은 takeoff() 또는 비동기 버전인 takeoff_async()를 사용하는 것입니다.
이륙 명령이 수락되면 드론은 지정된 이륙 고도까지 수직으로 상승한 뒤, 다음 명령이 수신될 때까지 그 자리에서 안정적으로 호버링(Hovering)합니다.

이륙 고도 설정하기
이륙 고도는 기본값이 설정되어 있지만, set_takeoff_altitude() 함수를 사용하여 이륙 전 언제든지 연구 목적에 맞게 변경할 수 있습니다.
// 이륙 고도를 5미터로 설정
action.set_takeoff_altitude(5.0f);
std::cout << "이륙(Takeoff) 명령 전송 중...\n";
const Action::Result takeoff_result = action.takeoff();
if (takeoff_result != Action::Result::Success) {
std::cerr << "이륙 실패: " << takeoff_result << '\n';
return 1;
}
std::cout << "이륙 명령 수락됨! 목표 고도로 상승합니다.\n";
앞서 언급했듯 takeoff()가 성공을 반환해도 이륙이 완전히 끝난 것은 아닙니다. 이륙이 완료되었는지 확인하려면 목표 고도에 도달할 때까지 현재 고도를 폴링하거나, 충분한 시간(예: sleep_for(seconds(10)))을 대기하는 방식이 쓰입니다.
안전한 귀환과 착륙 (Land, RTL, Disarm)
비행 테스트나 임무가 끝났다면 기체를 지면으로 안전하게 내려야 합니다. MAVSDK는 이를 위해 여러 가지 방법을 제공합니다.
1) 현재 위치에 착륙 (Land)
드론을 현재 떠 있는 위치 바로 아래로 착륙시키는 가장 좋은 방법은 land() 메서드를 사용하는 것입니다. 이 명령이 수락되면 기체는 Land(착륙) 모드로 전환되어 서서히 하강합니다. 착륙 임무 항목(Mission)을 사용하거나 오프보드 모드를 이용해 수동으로 착륙시키는 것은 안전상 권장되지 않습니다.
std::cout << "현재 위치에 착륙합니다...\n";
const Action::Result land_result = action.land();
if (land_result != Action::Result::Success) {
std::cerr << "착륙 실패: " << land_result << '\n';
}
2) 출발지로 복귀 (Return To Launch, RTL)
만약 드론이 멀리 날아간 상태라면, 처음 이륙했던 홈 위치(Home Position)로 돌아와서 착륙하도록 하는 return_to_launch() 메서드를 사용할 수 있습니다. 기체 설정에 따라 복귀 후 착륙하거나 홈 위치 상공에서 호버링할 수 있습니다.
3) 무장 해제 (Disarm)와 강제 정지 (Kill)
- Disarm (disarm()): 드론이 지면에 안전하게 착륙했다고 판단할 때 모터를 끄는 정상적인 절차입니다. PX4는 착륙을 완료하면 자동으로 무장을 해제(Auto-disarm)하므로 반드시 명시적으로 호출할 필요는 없으나, 코드 흐름상 확실한 제어를 위해 사용할 수 있습니다.
- Kill (kill()): 비행 중인 기체의 모터를 즉시 강제로 정지시킵니다. 이 명령은 비상 상황에만 사용해야 하며, 공중에서 호출 시 기체는 그대로 추락합니다.
C++ 이착륙(Takeoff and Land) 전체 예제 코드
지금까지 배운 내용을 하나로 통합하여, 드론을 연결하고 안전 점검 후 이륙하여 10초간 호버링한 뒤 착륙하는 전체 C++ 소스 코드입니다. 이 코드는 SITL 시뮬레이터 환경에서 바로 빌드하여 테스트해 볼 수 있습니다.
#include <iostream>
#include <thread>
#include <chrono>
#include <mavsdk/mavsdk.h>
#include <mavsdk/plugins/action/action.h>
#include <mavsdk/plugins/telemetry/telemetry.h>
using namespace mavsdk;
using std::chrono::seconds;
using std::this_thread::sleep_for;
int main() {
// 1. MAVSDK 초기화 및 SITL 시뮬레이터 연결
Mavsdk mavsdk{Mavsdk::Configuration{ComponentType::GroundStation}};
ConnectionResult connection_result = mavsdk.add_any_connection("udpin://0.0.0.0:14540");
if (connection_result != ConnectionResult::Success) {
std::cerr << "연결 실패!\n";
return 1;
}
// 2. 드론 시스템 발견 대기
auto system = mavsdk.first_autopilot(3.0);
if (!system) {
std::cerr << "시스템을 찾는 데 시간 초과 발생\n";
return 1;
}
// 3. Action 및 Telemetry 플러그인 인스턴스화
auto action = Action{system.value()};
auto telemetry = Telemetry{system.value()};
// 4. 비행 전 안전 점검 (GPS 및 캘리브레이션)
while (!telemetry.health_all_ok()) {
std::cout << "시스템 비행 준비 대기 중...\n";
sleep_for(seconds(1));
}
std::cout << "시스템 준비 완료!\n";
// 5. 모터 시동 (Arming)
std::cout << "Arming...\n";
if (action.arm() != Action::Result::Success) {
std::cerr << "Arming 명령 실패\n";
return 1;
}
// 6. 이륙 고도 설정 (5미터) 및 이륙
action.set_takeoff_altitude(5.0f);
std::cout << "이륙합니다 (Takeoff)!\n";
if (action.takeoff() != Action::Result::Success) {
std::cerr << "이륙 명령 실패\n";
return 1;
}
// 7. 이륙 후 호버링 상태 유지를 위해 10초 대기
// (실제 연구 환경에서는 고도 텔레메트리 값을 지속적으로 확인하는 로직 권장)
sleep_for(seconds(10));
// 8. 현재 위치에 착륙 (Land)
std::cout << "착륙합니다 (Landing)...\n";
if (action.land() != Action::Result::Success) {
std::cerr << "착륙 명령 실패\n";
return 1;
}
// 9. 착륙이 완전히 끝날 때까지 대기 (공중에 있는 동안 대기)
while (telemetry.in_air()) {
std::cout << "기체가 하강 중입니다...\n";
sleep_for(seconds(1));
}
std::cout << "착륙 성공 및 모터 정지 완료!\n";
return 0;
}
이번 6편을 통해 C++ 코드로 드론의 모터를 돌리고 하늘로 띄워 올린 뒤, 다시 지면으로 안전하게 착륙시키는 기본 비행 제어 루프에 대해 설명드렸습니다.
Action 플러그인의 직관적인 API 덕분에 복잡한 통신 패킷을 몰라도 단 몇 줄의 코드만으로 강력한 제어가 가능하다는 것을 실감하셨을 것입니다. 참고로, 헬리콥터와 고정익 비행이 모두 가능한 수직 이착륙(VTOL) 기체를 연구하시는 분들은 이 Action API 안에 포함된 transition_to_fixedwing() 등의 메서드를 활용하여 비행 모드 전환까지 손쉽게 구현할 수 있습니다.
하지만 자율 비행 로봇이 제자리에서만 오르내린다면 진정한 자율 주행이라 부를 수 없겠죠? 다음 [7편: 특정 위치로 이동 (goto_location)]에서는 지구상의 특정 글로벌 좌표(위도, 경도)를 지정하여 드론을 목적지로 스스로 날아가게 하는 방법에 대해 다루겠습니다. 하버사인(Haversine) 공식을 활용한 거리 계산 등 연구에 피가 되고 살이 되는 내용이 준비되어 있습니다.
YouTube 강좌

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