PX4 MAVSDK – C++ Programming [7편] 특정 위치로 이동 (goto_location) 및 하버사인 공식의 이해
안녕하세요! 마케팅 팀의 에이든(Aiden)입니다. 지난 6편에서는 Action API를 활용하여 드론에 시동을 걸고 하늘로 이륙(Takeoff)시킨 뒤, 다시 지면으로 안전하게 착륙(Land)시키는 기본 제어 루프를 마스터했습니다.
하지만 제자리에서 위아래로만 움직이는 것은 진정한 의미의 자율 비행 로봇이라고 부르기 어렵습니다. 이번 7편에서는 드디어 드론을 지구상의 특정 GPS 좌표(위도, 경도)로 스스로 날아가게 하는 goto_location 명령에 대해 학습하겠습니다. 더불어 로보틱스 연구원이라면 반드시 알아야 할, 구면 좌표계 상에서 두 지점 간의 최단 거리를 구하는 하버사인(Haversine) 공식의 수학적 원리와 C++ 구현 방법까지 깊이 있게 파헤쳐 보겠습니다.

특정 위치로 이동하기: goto_location API 개요
MAVSDK의 Action 플러그인은 기체를 특정한 글로벌 좌표로 이동시키는 goto_location() 메서드를 제공합니다. 이 메서드는 드론이 현재 위치에서 목표 지점까지 직선으로 비행하도록 오토파일럿에 명령을 내립니다.
함수 호출 시 다음과 같은 4가지 파라미터를 전달해야 합니다.
- Latitude (위도): 목표 지점의 글로벌 위도 (단위: Degree)
- Longitude (경도): 목표 지점의 글로벌 경도 (단위: Degree)
- Absolute Altitude (절대 고도): 해발 고도(AMSL) 기준의 목표 고도 (단위: Meter)
- Yaw (요 각도): 이동하는 동안 기체의 전면부(기수)가 바라볼 나침반 방향 (단위: Degree, 북쪽이 0도)
[명령 수락과 실행의 비동기성 주의점] 이전 6편에서 강조했듯, goto_location()이 Success를 반환했다는 것은 기체가 목표 지점으로 날아갈 의도를 가지고 명령을 수락했다는 뜻이지, 이미 그곳에 도착했다는 뜻이 아닙니다. 따라서 명령을 내린 후에는 Telemetry 플러그인을 활용해 현재 위치와 목표 위치 사이의 거리를 계산하며 도착 여부를 모니터링해야 합니다.
그렇다면, 현재 위치와 목표 위치 사이의 거리는 어떻게 계산할까요?
여기서 수학적 지식이 필요해집니다.
드론이 날아갈 거리는 얼마일까?: 하버사인(Haversine) 공식
만약 드론이 완전히 평평한 2차원 평면 위를 날아간다면 피타고라스의 정리를 이용해 유클리드 거리(d=Δx2+Δy2)를 쉽게 구할 수 있습니다. 하지만 지구는 둥근 구면(Sphere)입니다.
따라서 위도와 경도라는 구면 좌표계를 사용하는 GPS 시스템에서 두 지점 사이의 실제 최단 거리(대권 거리, Great-circle distance)를 구하려면 구면 삼각법을 사용해야 합니다. 이때 사용되는 전통적이고 안정적인 공식이 바로 하버사인(Haversine) 공식입니다.

하버사인 공식의 수학적 이해 (연구원을 위한 Deep Dive)
하버사인 함수의 정의는 다음과 같습니다. hav(θ)=sin2(2θ)
구면 삼각법의 코사인 법칙에서 출발하여 이 하버사인 함수를 적용(반각 항등식 이용)하면 수치적으로 매우 안정적인 형태의 거리를 도출할 수 있습니다.
공식을 전개하면 두 지점 간의 중심각 c를 구하기 위한 매개변수 a는 다음과 같이 정의됩니다. a=sin2(2Δϕ)+cosϕ1cosϕ2sin2(2Δλ) (여기서 ϕ는 위도, λ는 경도, Δ는 두 지점의 차이를 의미합니다.)
최종적으로 중심각 c는 c=2⋅arcsin(a) 로 도출되며, 실제 곡면 거리 d는 지구의 반지름 R을 곱한 d=R⋅c 가 됩니다.
이 공식은 두 지점의 거리가 매우 가깝더라도 컴퓨터의 부동소수점 연산에서 발생할 수 있는 소수점 자리 소실(소수점 상실)을 방지해 주어 높은 정밀도를 보장합니다. 정밀한 자율 비행 알고리즘 개발을 위해서는 필수적으로 숙지해야 하는 공식입니다.
C++로 하버사인 공식 구현하기
앞서 설명한 수학 공식을 현대적인 C++ 코드로 구현해 보겠습니다. C++의 표준 수학 라이브러리인 <cmath>를 활용합니다.
특히 중심각 c를 계산할 때, 프로그래밍에서는 arcsin 대신 c=2⋅atan2(a,1−a
) 를 사용하는 것이 양끝점이나 반대편 점 등 극단적인 상황에서 훨씬 더 수치적으로 안정적입니다.
[하버사인 거리 계산 C++ 유틸리티 함수]
#include <cmath>
#include <numbers> // C++20 이상의 std::numbers::pi_v 사용
// 각도를 라디안으로 변환하는 헬퍼 함수
constexpr double deg_to_rad(double deg) {
// M_PI는 비표준이므로, <numbers>의 pi_v를 사용하거나 상수를 직접 정의하는 것을 권장합니다 [5].
return deg * (std::numbers::pi_v<double> / 180.0);
}
// 두 GPS 좌표 간의 거리를 미터(m) 단위로 반환하는 하버사인 함수 [6]
double calculate_distance(double lat1, double lon1, double lat2, double lon2) {
// 평균 지구 반지름 세팅 (단위: 미터) [6]
// 더 정밀한 계산을 원할 경우 WGS-84 적도반경(6378137.0)을 사용할 수도 있습니다 [6].
const double EARTH_RADIUS_M = 6371000.0;
// 1. 위도와 경도 차이를 라디안으로 변환 [5]
double d_lat = deg_to_rad(lat2 - lat1);
double d_lon = deg_to_rad(lon2 - lon1);
// 2. 입력받은 위도도 라디안으로 변환 [5]
double rad_lat1 = deg_to_rad(lat1);
double rad_lat2 = deg_to_rad(lat2);
// 3. 매개변수 a 계산 (sin^2 형태를 사용하여 소수점 상실 방지) [5]
double a = std::sin(d_lat / 2.0) * std::sin(d_lat / 2.0) +
std::cos(rad_lat1) * std::cos(rad_lat2) *
std::sin(d_lon / 2.0) * std::sin(d_lon / 2.0);
// 4. 중심각 c 계산 (atan2를 사용하여 안정성 확보) [5]
double c = 2.0 * std::atan2(std::sqrt(a), std::sqrt(1.0 - a));
// 5. 최종 거리 반환 (R * c) [6]
return EARTH_RADIUS_M * c;
}
전체 예제 통합: 이륙 후 목표 위치로 비행하기
이제 지금까지 배운 모든 지식(Action의 이륙/착륙, Telemetry의 비동기 구독, 하버사인 공식을 활용한 거리 계산)을 하나로 통합하여 완벽한 자율 이동 시퀀스를 만들어 보겠습니다.

이 예제에서는 드론이 이륙한 후 북쪽으로 약간 떨어진 임의의 지점으로 이동하도록 명령합니다. 목표 지점과의 거리가 1미터 이내가 되면 도착한 것으로 판단하고 착륙(Land)을 수행합니다.
[자율 위치 이동 및 모니터링 전체 코드]
#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <numbers>
#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;
// (앞서 정의한 deg_to_rad 및 calculate_distance 함수가 여기에 포함된다고 가정합니다)
int main() {
// 1. 초기화 및 SITL 연결
Mavsdk mavsdk{Mavsdk::Configuration{ComponentType::GroundStation}};
if (mavsdk.add_any_connection("udpin://0.0.0.0:14540") != ConnectionResult::Success) {
std::cerr << "연결 실패\n"; return 1;
}
auto system = mavsdk.first_autopilot(3.0);
if (!system) {
std::cerr << "시스템 감지 시간 초과\n"; return 1;
}
auto action = Action{system.value()};
auto telemetry = Telemetry{system.value()};
// 2. 비행 전 점검 및 이륙
while (!telemetry.health_all_ok()) {
std::cout << "안전 점검 및 GPS Lock 대기 중...\n";
sleep_for(seconds(1));
}
std::cout << "Arming 및 이륙 중...\n";
action.arm();
action.set_takeoff_altitude(5.0f);
action.takeoff();
// 이륙이 어느 정도 안정화될 때까지 대기
sleep_for(seconds(8));
// 3. 현재 위치를 기준으로 목표 지점 생성
auto current_pos = telemetry.position();
// 위도를 임의로 살짝 증가시켜 북쪽으로 이동 목표 설정 (약 11m 이동 효과)
double target_lat = current_pos.latitude_deg + 0.0001;
double target_lon = current_pos.longitude_deg;
// 고도는 현재 기체의 절대 고도(AMSL)를 그대로 유지
double target_alt = current_pos.absolute_altitude_m;
// 목표 지점을 향해 북쪽(0도)을 바라보게 요 각도 설정
float target_yaw = 0.0f;
// 4. goto_location 명령 전송
std::cout << "목표 지점으로 이동(goto_location) 명령 전송...\n";
Action::Result goto_result = action.goto_location(target_lat, target_lon, target_alt, target_yaw);
if (goto_result != Action::Result::Success) {
std::cerr << "이동 명령 실패: " << goto_result << '\n';
return 1;
}
// 5. 목표 지점 도달 모니터링 (하버사인 공식 활용)
bool reached_target = false;
while (!reached_target) {
// 최신 텔레메트리 위치 데이터 폴링
auto pos = telemetry.position();
// 현재 위치와 목표 위치 사이의 거리 계산
double distance_to_target = calculate_distance(
pos.latitude_deg, pos.longitude_deg, target_lat, target_lon);
std::cout << "목표까지 남은 거리: " << distance_to_target << " 미터\n";
// 남은 거리가 1미터 미만이면 도착한 것으로 간주
if (distance_to_target < 1.0) {
std::cout << "목표 지점에 도달했습니다!\n";
reached_target = true;
}
sleep_for(seconds(1)); // 1초 간격으로 모니터링
}
// 6. 착륙 및 종료
std::cout << "현재 위치에 착륙(Land)을 시도합니다.\n";
action.land();
while (telemetry.in_air()) {
std::cout << "하강 중...\n";
sleep_for(seconds(1));
}
std::cout << "착륙 완료! 프로그램을 종료합니다.\n";
return 0;
}
이번 7편에서는 드론을 특정 GPS 좌표로 조종하는 goto_location() 기능과, 비행 거리를 수학적으로 완벽하게 계산해 내는 구면 삼각법의 정수 하버사인(Haversine) 공식을 함께 학습했습니다.
특히 이 거리를 실시간으로 모니터링하며 자율 비행의 상태 전이(State Transition)를 제어하는 방식은, 실제 연구 논문이나 상용 로보틱스 프로젝트에서 가장 뼈대가 되는 필수 논리 구조입니다.
단일 지점으로의 이동을 완벽하게 구현하셨다면, 다음 단계는 당연히 여러 개의 지점을 순차적으로 비행하는 복합 임무겠죠? 다음 [8편: 임무 비행 (Missions) 자동화]에서는 웨이포인트(Waypoint)를 수십 개씩 업로드하고 카메라 촬영 명령까지 섞어서 완전한 형태의 자율 미션을 구성하는 방법에 대해 다루겠습니다.
YouTube 강좌

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