2、坐标系统与转换:常用坐标系、转换矩阵与WGS-84
坐标系统,说白了就是给目标一个「地址」。雷达测到目标,报的是距离、方位、俯仰。但你要把它画在地图上,或者跟别的传感器数据融合,就得做坐标转换。这个环节要是搞错了,后面所有误差补偿都是白搭。
我个人习惯,在项目一开始就把坐标系的定义文档写清楚。别嫌麻烦,我见过太多因为坐标系没对齐,导致整个系统联调时数据全乱的案例。
2.1 常用坐标系:地心、地理、雷达站心
雷达数据处理里,最常用的坐标系就三个。咱们一个一个说。
2.1.1 地心地固坐标系(ECEF)
ECEF 的原点在地球质心。Z 轴指向北极,X 轴指向本初子午线与赤道的交点,Y 轴按右手定则确定。这个坐标系跟地球一起转,所以叫「地固」。
为什么需要它?因为它是全球统一的。你在北京测一个目标,我在上海测同一个目标,最后都得转到 ECEF 下才能做数据融合。我在做多雷达组网项目时,就吃过这个亏——两台雷达各自报的坐标,直接拿来用,结果差了上百米。后来才发现,一台用的是 WGS-84 的 ECEF,另一台用的却是自己定义的局部地心系。
2.1.2 地理坐标系(LLA)
就是咱们常说的经纬度+海拔高度。纬度(Latitude)、经度(Longitude)、高度(Altitude)。这个坐标系最直观,GPS 给的就是这个。
但要注意,纬度分大地纬度和地心纬度。大地纬度是参考椭球法线方向与赤道面的夹角,地心纬度是地球质心到目标连线与赤道面的夹角。两者差多少?在中纬度地区大概差 0.2 度左右,换算成距离就是十几公里。嗯,这里要注意,雷达数据处理中,我们通常用大地纬度。
2.1.3 雷达站心坐标系(ENU)
这个坐标系的原点在雷达天线中心。X 轴指向东(East),Y 轴指向北(North),Z 轴指向天顶(Up)。所以也叫东北天坐标系。
雷达直接测量的极坐标(距离 R、方位角 Az、俯仰角 El),第一步就是转到 ENU 下:
# Python 示例:极坐标转 ENU
import numpy as np
def polar_to_enu(R, az_deg, el_deg):
"""
R: 斜距 (米)
az_deg: 方位角 (度),正北为0,顺时针
el_deg: 俯仰角 (度),水平为0
返回: (E, N, U) 单位米
"""
az = np.deg2rad(az_deg)
el = np.deg2rad(el_deg)
E = R * np.cos(el) * np.sin(az)
N = R * np.cos(el) * np.cos(az)
U = R * np.sin(el)
return E, N, U
这个转换很简单,但有个坑:方位角的定义。有的雷达以正北为0度顺时针,有的以正东为0度逆时针。我当年调试某型雷达时,就因为方位角定义没对齐,折腾了两天。后来我养成了习惯——拿到雷达数据第一件事,先确认方位角定义。
2.2 坐标转换矩阵
坐标转换的核心就是旋转矩阵。说白了,就是把一个坐标系下的向量,投影到另一个坐标系下。
2.2.1 旋转矩阵的基本形式
绕 X 轴旋转角度 θ:
R_x(θ) = [[1, 0, 0],
[0, cosθ, -sinθ],
[0, sinθ, cosθ]]
绕 Y 轴旋转角度 θ:
R_y(θ) = [[ cosθ, 0, sinθ],
[ 0, 1, 0],
[-sinθ, 0, cosθ]]
绕 Z 轴旋转角度 θ:
R_z(θ) = [[cosθ, -sinθ, 0],
[sinθ, cosθ, 0],
[ 0, 0, 1]]
你想想看,从 ENU 转到 ECEF,其实就是先绕 Z 轴旋转经度,再绕 Y 轴旋转负纬度,最后平移原点。为什么是负纬度?因为 ENU 的 Z 轴指向天顶,而 ECEF 的 Z 轴指向北极,中间差了个纬度。
2.2.2 ENU 到 ECEF 的完整转换
假设雷达站位于 (lat0, lon0, h0),目标在 ENU 下的坐标为 (E, N, U)。转到 ECEF 的步骤:
- 先把雷达站位置 (lat0, lon0, h0) 转到 ECEF,得到 (x0, y0, z0)
- 构造旋转矩阵:R = R_z(-lon0) * R_y(-lat0) (注意角度符号)
- 目标 ECEF = R * [E, N, U]^T + [x0, y0, z0]^T
def enu_to_ecef(E, N, U, lat0_deg, lon0_deg, h0):
"""
ENU 转 ECEF
lat0_deg, lon0_deg: 雷达站经纬度 (度)
h0: 雷达站海拔 (米)
"""
from scipy.spatial.transform import Rotation as R
# 1. 雷达站位置转 ECEF
x0, y0, z0 = lla_to_ecef(lat0_deg, lon0_deg, h0)
# 2. 构造旋转矩阵
lat0 = np.deg2rad(lat0_deg)
lon0 = np.deg2rad(lon0_deg)
# 先绕 Y 轴旋转 -lat0,再绕 Z 轴旋转 -lon0
r = R.from_euler('yz', [-lat0, -lon0], degrees=False)
# 3. 转换
enu_vec = np.array([E, N, U])
ecef_vec = r.apply(enu_vec) + np.array([x0, y0, z0])
return ecef_vec
这里有个细节:旋转顺序。先绕 Y 轴还是先绕 Z 轴?顺序不同,结果不同。我建议用欧拉角时,明确指定顺序,别依赖默认值。
2.3 WGS-84 与局部坐标系
WGS-84 是目前最常用的全球大地坐标系。GPS 用的就是它。它的椭球参数:
| 参数 | 值 | 说明 |
|---|---|---|
| 长半轴 a | 6378137.0 m | 赤道半径 |
| 扁率 f | 1/298.257223563 | 描述椭球扁平程度 |
| 短半轴 b | 6356752.3142 m | 极半径 |
为什么用椭球不用球体?因为地球自转导致赤道鼓起,两极略扁。如果你用球体近似,在赤道附近误差可达 21 公里。嗯,这个误差在雷达数据处理中是不能接受的。
2.3.1 LLA 转 ECEF(WGS-84)
这个公式我建议直接背下来,或者存成工具函数:
def lla_to_ecef(lat_deg, lon_deg, alt):
"""
WGS-84 经纬高转 ECEF
"""
a = 6378137.0
f = 1 / 298.257223563
e2 = 2*f - f**2 # 第一偏心率平方
lat = np.deg2rad(lat_deg)
lon = np.deg2rad(lon_deg)
N = a / np.sqrt(1 - e2 * np.sin(lat)**2)
x = (N + alt) * np.cos(lat) * np.cos(lon)
y = (N + alt) * np.cos(lat) * np.sin(lon)
z = (N * (1 - e2) + alt) * np.sin(lat)
return x, y, z
注意那个 N 值,它叫卯酉圈曲率半径。不是常数,随纬度变化。我在项目中见过有人直接用 a 代替 N,结果在高纬度地区误差很大。
2.3.2 局部坐标系的选择
除了 ENU,还有 NED(北东地)坐标系。NED 的 Z 轴指向地心,常用于飞行器导航。ENU 的 Z 轴指向天顶,更适合地面雷达。
我个人建议:地面雷达系统统一用 ENU。为什么?因为高度是正的,看着舒服。而且大多数雷达数据处理软件默认也是 ENU。
2.4 坐标转换的误差传递
坐标转换本身不引入误差,但原始测量误差会通过转换矩阵传递。比如雷达测距误差 5 米,测角误差 0.1 度,转到 ECEF 后,误差会变成多少?
这需要做误差协方差矩阵的线性变换:
P_ecef = J * P_enu * J^T
其中 J 是雅可比矩阵。这个在后续章节「误差补偿」里会详细讲。这里先提一句:坐标转换后的误差,不是简单的加减关系,而是跟目标位置、雷达站位置都有关。
好了,坐标系统这部分就这些。记住三点:
- 搞清楚每个坐标系的定义和原点
- 旋转矩阵的符号和顺序别搞反
- WGS-84 椭球参数用标准值,别自己改
下一章咱们聊雷达测量误差的来源和建模。到时候会用到今天讲的坐标转换知识。