ECEF与ENU坐标系定义及C语言实现

发布于:2025-03-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、ECEF与ENU坐标系定义

  1. ECEF坐标系(地心地固坐标系)
  • 原点:地球质心
  • X轴:指向本初子午线与赤道交点
  • Y轴:在赤道平面内与X轴垂直
  • Z轴:指向北极
  • 数学表示: P e c e f = ( x , y , z ) P_{ecef} = (x, y, z) Pecef=(x,y,z)
  1. ENU坐标系(东北天坐标系)
  • 原点:本地参考点
  • E轴:指向东方
  • N轴:指向北方
  • U轴:垂直地面向上
  • 数学表示: P e n u = ( e , n , u ) P_{enu} = (e, n, u) Penu=(e,n,u)

二、坐标转换数学原理

  1. 地理坐标转ECEF:
    { x = ( N + h ) cos ⁡ ϕ cos ⁡ λ y = ( N + h ) cos ⁡ ϕ sin ⁡ λ z = ( N ( 1 − e 2 ) + h ) sin ⁡ ϕ \begin{cases} x = (N + h)\cos\phi\cos\lambda \\ y = (N + h)\cos\phi\sin\lambda \\ z = (N(1-e^2)+h)\sin\phi \end{cases} x=(N+h)cosϕcosλy=(N+h)cosϕsinλz=(N(1e2)+h)sinϕ
    其中:
  • N = a 1 − e 2 sin ⁡ 2 ϕ N = \frac{a}{\sqrt{1-e^2\sin^2\phi}} N=1e2sin2ϕ a(卯酉圈曲率半径)
  • e 2 = 2 f − f 2 e^2 = 2f - f^2 e2=2ff2(椭球偏心率平方)
  • a = 6378137 m a=6378137m a=6378137m(WGS84长半轴)
  • f = 1 / 298.257223563 f=1/298.257223563 f=1/298.257223563(WGS84扁率)
  1. ECEF转ENU:
    [ e n u ] = [ − sin ⁡ λ cos ⁡ λ 0 − sin ⁡ ϕ cos ⁡ λ − sin ⁡ ϕ sin ⁡ λ cos ⁡ ϕ cos ⁡ ϕ cos ⁡ λ cos ⁡ ϕ sin ⁡ λ sin ⁡ ϕ ] [ x − x 0 y − y 0 z − z 0 ] \begin{bmatrix} e \\ n \\ u \end{bmatrix} = \begin{bmatrix} -\sin\lambda & \cos\lambda & 0 \\ -\sin\phi\cos\lambda & -\sin\phi\sin\lambda & \cos\phi \\ \cos\phi\cos\lambda & \cos\phi\sin\lambda & \sin\phi \end{bmatrix} \begin{bmatrix} x-x_0 \\ y-y_0 \\ z-z_0 \end{bmatrix} enu = sinλsinϕcosλcosϕcosλcosλsinϕsinλcosϕsinλ0cosϕsinϕ xx0yy0zz0

三、C语言实现(保存为ecef2enu.c)

#include <stdio.h>
#include <math.h>

#define WGS84_A 6378137.0
#define WGS84_F 1/298.257223563

typedef struct { double x, y, z; } ECEF;
typedef struct { double lat, lon, alt; } Geodetic;
typedef struct { double e, n, u; } ENU;

ECEF geodetic_to_ecef(Geodetic geo) {
    double a = WGS84_A;
    double f = WGS84_F;
    double e2 = 2*f - f*f;
    double sinphi = sin(geo.lat);
    double cosphi = cos(geo.lat);
    double N = a / sqrt(1 - e2*sinphi*sinphi);
    
    ECEF ecef;
    ecef.x = (N + geo.alt) * cosphi * cos(geo.lon);
    ecef.y = (N + geo.alt) * cosphi * sin(geo.lon);
    ecef.z = (N*(1-e2) + geo.alt) * sinphi;
    return ecef;
}

ENU ecef_to_enu(ECEF target, Geodetic ref_geo) {
    ECEF ref_ecef = geodetic_to_ecef(ref_geo);
    double dx = target.x - ref_ecef.x;
    double dy = target.y - ref_ecef.y;
    double dz = target.z - ref_ecef.z;
    
    double sinphi = sin(ref_geo.lat);
    double cosphi = cos(ref_geo.lat);
    double sinlam = sin(ref_geo.lon);
    double coslam = cos(ref_geo.lon);
    
    ENU enu;
    enu.e = -sinlam*dx + coslam*dy;
    enu.n = -sinphi*coslam*dx - sinphi*sinlam*dy + cosphi*dz;
    enu.u = cosphi*coslam*dx + cosphi*sinlam*dy + sinphi*dz;
    return enu;
}

int main() {
    // 北京参考点(39.9042°N, 116.4074°E,海拔43m)
    Geodetic ref = {
        .lat = 39.9042 * M_PI/180,
        .lon = 116.4074 * M_PI/180,
        .alt = 43
    };
    
    // 生成测试数据(实际应用时可从文件读取)
    for(int i=0; i<5; i++) {
        Geodetic target_geo = {
            .lat = ref.lat + 0.01*i,
            .lon = ref.lon + 0.01*i,
            .alt = ref.alt + 10*i
        };
        ECEF target_ecef = geodetic_to_ecef(target_geo);
        ENU enu = ecef_to_enu(target_ecef, ref);
        
        printf("ENU: E=%.3fm, N=%.3fm, U=%.3fm\n", enu.e, enu.n, enu.u);
    }
    return 0;
}

编译执行:

gcc ecef2enu.c -lm -o ecef2enu && ./ecef2enu

四、Python验证代码

import numpy as np
import matplotlib.pyplot as plt
from pyproj import Transformer
from subprocess import check_output

# 生成100个测试点
np.random.seed(42)
ref_lat, ref_lon = 39.9042, 116.4074
d_lats = np.random.uniform(-0.1, 0.1, 100)
d_lons = np.random.uniform(-0.1, 0.1, 100)
alts = np.random.uniform(0, 1000, 100)

# 运行C程序获取结果
c_output = check_output(["./ecef2enu"]).decode().strip().split('\n')
c_enu = [list(map(float, line.split()[2::2])) for line in c_output]

# 使用pyproj计算
ecef_trans = Transformer.from_crs(4326, 4978)
enu_trans = Transformer.from_crs(4326, 4467, 
                        authority="EPSG",
                        lon_0=ref_lon, lat_0=ref_lat, h_0=0)

errors = []
for i in range(100):
    # Python计算结果
    lat = ref_lat + d_lats[i]
    lon = ref_lon + d_lons[i]
    alt = alts[i]
    
    # pyproj计算ENU
    ecef = ecef_trans.transform(lat, lon, alt)
    enu_py = enu_trans.transform(lat, lon, alt)
    
    # 计算误差
    enu_c = c_enu[i] if i <5 else [0,0,0] # 仅示例前5个
    errors.append(np.array(enu_py) - np.array(enu_c))

# 可视化
errors = np.array(errors)
plt.figure(figsize=(12,4))

plt.subplot(131)
plt.hist(errors[:,0], bins=20)
plt.title('East Error Distribution')
plt.xlabel('Meters')

plt.subplot(132)
plt.hist(errors[:,1], bins=20)
plt.title('North Error Distribution')

plt.subplot(133)
plt.scatter(errors[:,0], errors[:,1], alpha=0.6)
plt.xlabel('East Error')
plt.ylabel('North Error')
plt.tight_layout()
plt.savefig('enu_errors.png')
plt.show()

五、验证结果分析

  1. 误差直方图显示各方向误差分布
  2. 散点图展示平面误差的相关性
  3. 典型误差应小于1e-4米(数值计算误差)

该实现完整展示了从理论到实践的全流程,可通过调整测试点数量和分布进行更严格的验证。实际工程应用中需考虑坐标系的旋转顺序、大地水准面模型等更多细节。

(验证代码还有BUG,不能直接运行,但是我肝不了了,该天再调了,晚安,吗喀巴卡)


网站公告

今日签到

点亮在社区的每一天
去签到