← Back to Blog

[Control] Local Tangent Plane Coordinates: ENU and NED

engineering > control

2026-07-066 min read

#engineering #control #coordinates #drone #navigation #ned #enu

드론, 항공기, 로봇의 위치를 다룰 때는 좌표계 선택이 중요하다. GPS는 위도, 경도, 고도처럼 지구 기준 좌표를 주지만, 제어기는 보통 로컬 좌표계에서 위치 오차와 속도를 계산한다.

이때 자주 쓰는 좌표계가 Local tangent plane coordinate이다. 지구 표면의 한 기준점 주변을 평면으로 근사하고, 그 접평면 위에서 위치를 표현한다.

대표적인 local tangent plane 좌표계는 두 가지다.

좌표계축 방향주 사용 분야
ENUEast, North, Uprobotics, mapping
NEDNorth, East, Downaerospace, drone

왜 Local Frame이 필요한가

GPS 위치는 보통 geodetic coordinate로 표현된다.

(φ,λ,h)(\varphi, \lambda, h)

여기서

기호의미
φ\varphilatitude
λ\lambdalongitude
hhaltitude 또는 height

하지만 제어 문제에서는 다음과 같은 값이 더 직접적이다.

목표 지점이 현재 위치에서 북쪽으로 10m, 동쪽으로 3m 떨어져 있다.

즉 위도/경도 차이보다 meter 단위의 local displacement가 필요하다.

Δplocal=[ΔxΔyΔz]\Delta p_{\text{local}} = \begin{bmatrix} \Delta x \\ \Delta y \\ \Delta z \end{bmatrix}

Local tangent plane은 지구 곡면 위의 작은 영역을 평면으로 근사해 이런 meter 단위 계산을 쉽게 만든다.


ECEF 좌표계

Local frame을 정의하기 전에 지구 중심 좌표계인 ECEF를 보자. ECEF는 Earth-Centered, Earth-Fixed coordinate이다.

의미
XX위도 00^\circ, 경도 00^\circ 방향
YY위도 00^\circ, 경도 9090^\circE 방향
ZZ북극 방향

ECEF 좌표는 지구와 함께 회전하는 3차원 Cartesian coordinate이다. 어떤 지점의 ECEF position을 다음처럼 둔다.

pecef=[XYZ]p_{\text{ecef}} = \begin{bmatrix} X\\Y\\Z \end{bmatrix}

Local frame은 보통 기준점 p0p_0를 잡고, 주변 점과의 차이를 회전시켜 얻는다.

Δpecef=pecefp0,ecef\Delta p_{\text{ecef}} = p_{\text{ecef}}-p_{0,\text{ecef}}

그리고 이 displacement를 ENU 또는 NED frame으로 변환한다.


ENU 좌표계

ENU는 East-North-Up 좌표계이다. 기준점의 위도 φ\varphi, 경도 λ\lambda에서 세 단위축을 다음처럼 정의한다.

East

East 방향은 경도가 증가하는 방향이다.

e=[sinλcosλ0]e = \begin{bmatrix} -\sin\lambda\\ \cos\lambda\\ 0 \end{bmatrix}

North

North 방향은 위도가 증가하는 방향이다.

n=[sinφcosλsinφsinλcosφ]n = \begin{bmatrix} -\sin\varphi\cos\lambda\\ -\sin\varphi\sin\lambda\\ \cos\varphi \end{bmatrix}

Up

Up 방향은 지표면 바깥쪽 normal 방향이다.

u=[cosφcosλcosφsinλsinφ]u = \begin{bmatrix} \cos\varphi\cos\lambda\\ \cos\varphi\sin\lambda\\ \sin\varphi \end{bmatrix}

따라서 ECEF displacement를 ENU로 바꾸는 회전 행렬은 다음과 같다.

Recefenu=[sinλcosλ0sinφcosλsinφsinλcosφcosφcosλcosφsinλsinφ]R_{\text{ecef}\rightarrow\text{enu}} = \begin{bmatrix} -\sin\lambda & \cos\lambda & 0\\ -\sin\varphi\cos\lambda & -\sin\varphi\sin\lambda & \cos\varphi\\ \cos\varphi\cos\lambda & \cos\varphi\sin\lambda & \sin\varphi \end{bmatrix}

변환은 다음처럼 쓴다.

penu=Recefenu(pecefp0,ecef)p_{\text{enu}} = R_{\text{ecef}\rightarrow\text{enu}} \left( p_{\text{ecef}}-p_{0,\text{ecef}} \right)

NED 좌표계

NED는 North-East-Down 좌표계이다. 항공 분야에서는 고도가 아래 방향을 양수로 두는 convention을 자주 쓴다.

NED의 세 축은 다음과 같다.

방향
xxNorth
yyEast
zzDown

Down은 Up의 반대 방향이다.

d=ud=-u

따라서 ECEF에서 NED로 가는 회전 행렬은 다음과 같다.

Recefned=[sinφcosλsinφsinλcosφsinλcosλ0cosφcosλcosφsinλsinφ]R_{\text{ecef}\rightarrow\text{ned}} = \begin{bmatrix} -\sin\varphi\cos\lambda & -\sin\varphi\sin\lambda & \cos\varphi\\ -\sin\lambda & \cos\lambda & 0\\ -\cos\varphi\cos\lambda & -\cos\varphi\sin\lambda & -\sin\varphi \end{bmatrix}

변환은 다음과 같다.

pned=Recefned(pecefp0,ecef)p_{\text{ned}} = R_{\text{ecef}\rightarrow\text{ned}} \left( p_{\text{ecef}}-p_{0,\text{ecef}} \right)

이 행렬의 각 row는 NED의 basis vector이다.

Row의미
1st rowECEF에서 본 North 방향
2nd rowECEF에서 본 East 방향
3rd rowECEF에서 본 Down 방향

ENU와 NED의 관계

ENU와 NED는 같은 local tangent plane을 다른 축 순서와 부호로 표현한 것이다.

ENU vector를 다음처럼 두자.

penu=[enu]p_{\text{enu}} = \begin{bmatrix} e\\n\\u \end{bmatrix}

NED vector는 다음이다.

pned=[neu]p_{\text{ned}} = \begin{bmatrix} n\\e\\-u \end{bmatrix}

따라서 변환 행렬은 간단하다.

pned=[010100001]penup_{\text{ned}} = \begin{bmatrix} 0 & 1 & 0\\ 1 & 0 & 0\\ 0 & 0 & -1 \end{bmatrix} p_{\text{enu}}

반대로도 같은 행렬을 적용하면 된다.

penu=[010100001]pnedp_{\text{enu}} = \begin{bmatrix} 0 & 1 & 0\\ 1 & 0 & 0\\ 0 & 0 & -1 \end{bmatrix} p_{\text{ned}}

이 행렬은 자기 자신의 inverse이다.

T1=TT^{-1}=T

드론에서 NED를 많이 쓰는 이유

항공 분야에서는 NED가 자연스럽다. 항공기는 대부분 지표면 위를 날고, 관심 있는 대상은 기체 아래쪽에 있는 경우가 많다. 그래서 Down을 positive axis로 두면 고도, 하강, 지면과의 관계를 표현하기 쉽다.

드론 제어에서는 local position을 다음처럼 둘 수 있다.

pned=[NED]p_{\text{ned}} = \begin{bmatrix} N\\E\\D \end{bmatrix}

여기서 DD가 커진다는 것은 아래로 내려간다는 뜻이다. 즉 상승은 DD가 감소하는 방향이다.

climbD˙<0\text{climb} \quad\Rightarrow\quad \dot{D}<0

이 convention은 처음에는 어색하지만, PX4나 ArduPilot 같은 autopilot ecosystem에서는 자주 등장한다.


Body Frame과 Local Frame

Local frame은 지구 기준의 근사 평면 좌표계이다. 반면 body frame은 기체에 붙어 있는 좌표계이다.

Frame원점축이 움직이는가용도
Local NED기준 GPS 위치지구 기준으로 고정위치, 속도, waypoint
Body frame드론 중심드론 자세와 함께 회전thrust, torque, IMU

드론의 추력은 body frame에서 정의되는 것이 자연스럽다.

Fbody=[00T]F_{\text{body}} = \begin{bmatrix} 0\\0\\-T \end{bmatrix}

하지만 위치와 속도 update는 local frame에서 하는 것이 자연스럽다. 따라서 자세 회전 행렬 RbodynedR_{\text{body}\rightarrow\text{ned}}가 필요하다.

Fned=RbodynedFbodyF_{\text{ned}} = R_{\text{body}\rightarrow\text{ned}} F_{\text{body}}

이 글의 핵심은 ECEF에서 local frame으로 가는 좌표 변환이고, 자세 동역학 자체는 별도의 문제이다. 즉 local tangent plane은 "내가 지구 어디 근처에 있는가"를 다루고, body-to-local rotation은 "기체가 어떤 자세인가"를 다룬다.


Python 예시

아래 코드는 위도/경도 기준점에서 ECEF displacement를 NED로 변환하는 기본 구조이다.

import numpy as np


def ecef_to_ned_rotation(lat_rad, lon_rad):
    s_lat = np.sin(lat_rad)
    c_lat = np.cos(lat_rad)
    s_lon = np.sin(lon_rad)
    c_lon = np.cos(lon_rad)

    return np.array([
        [-s_lat * c_lon, -s_lat * s_lon, c_lat],
        [-s_lon, c_lon, 0.0],
        [-c_lat * c_lon, -c_lat * s_lon, -s_lat],
    ])


def ecef_delta_to_ned(point_ecef, origin_ecef, origin_lat_rad, origin_lon_rad):
    rotation = ecef_to_ned_rotation(origin_lat_rad, origin_lon_rad)
    delta = point_ecef - origin_ecef
    return rotation @ delta

실제 시스템에서는 geodetic coordinate를 ECEF로 바꾸는 WGS84 변환이 먼저 필요하다. 하지만 한 번 ECEF 좌표가 준비되면, local NED 변환은 위 회전 행렬로 처리된다.


작은 거리에서의 근사

아주 작은 영역에서는 위도/경도 차이를 meter로 근사할 수도 있다. 지구 반지름을 RR이라 하면,

ΔNRΔφ\Delta N \approx R\Delta\varphi ΔERcosφΔλ\Delta E \approx R\cos\varphi\Delta\lambda

여기서 Δφ\Delta\varphi, Δλ\Delta\lambda는 radian 단위이다. 고도 차이를 Δh\Delta h라 하면 NED의 Down은 다음처럼 볼 수 있다.

ΔDΔh\Delta D \approx -\Delta h

이 근사는 짧은 거리에서는 편리하지만, 넓은 영역이나 정밀한 항법에서는 ECEF를 거친 변환을 쓰는 것이 더 안전하다.


흔한 실수

좌표계 버그는 드론 시뮬레이션과 항법 코드에서 매우 자주 발생한다. 대표적인 실수는 다음과 같다.

실수결과
degree를 radian으로 바꾸지 않음회전 행렬이 완전히 틀어진다.
ENU와 NED를 섞음east/north가 바뀌거나 altitude sign이 뒤집힌다.
altitude와 down sign 혼동상승 명령이 하강처럼 보일 수 있다.
origin을 빼지 않고 회전local displacement가 아니라 지구 중심 위치를 회전하게 된다.
body frame과 local frame 혼동추력/속도 방향이 잘못 적용된다.

특히 NED에서는 "위로 올라가는 것"이 zz 증가가 아니라 DD 감소라는 점을 계속 확인해야 한다.


정리

Local tangent plane coordinate는 지구 곡면 위의 작은 영역을 평면으로 근사해 제어와 항법을 쉽게 만드는 좌표계이다.

핵심 변환은 다음이다.

plocal=Receflocal(pecefp0,ecef)\boxed{ p_{\text{local}} = R_{\text{ecef}\rightarrow\text{local}} \left( p_{\text{ecef}}-p_{0,\text{ecef}} \right) }

ENU와 NED는 같은 local frame을 다른 convention으로 표현한다.

pned=[010100001]penu\boxed{ p_{\text{ned}} = \begin{bmatrix} 0 & 1 & 0\\ 1 & 0 & 0\\ 0 & 0 & -1 \end{bmatrix} p_{\text{enu}} }

드론에서는 local NED와 body frame을 구분하는 것이 중요하다. NED는 위치와 속도를 표현하는 지구 기준 local frame이고, body frame은 thrust와 torque처럼 기체에 붙은 물리량을 표현하는 frame이다.