4、坐标系统转换:WGS-84坐标系介绍,经纬度转平面坐标(墨卡托投影),Python实现
4.1 为什么非得搞懂坐标转换?
做定位模块开发,你迟早会遇到一个尴尬的场景:
GPS模块报出来的数据是 39.9042°N, 116.4074°E,这玩意儿是经纬度,单位是度。但你的地图、你的路径规划、你的距离计算,全都要用米来做单位。你总不能拿经纬度直接算距离吧?
我刚开始做嵌入式定位的时候,就犯过这个错。当时想算两个点之间的距离,直接用经纬度做差,结果算出来一个离谱的数字。后来才明白——经纬度是球面上的角度,不是平面上的长度。
所以,坐标转换是绕不开的一步。
核心结论:GPS模块输出的是WGS-84坐标系下的经纬度。要用于地图显示或距离计算,必须先转成平面坐标。最常用的投影方式就是墨卡托投影。
4.2 WGS-84坐标系,到底是个啥?
WGS-84,全称World Geodetic System 1984。说白了,就是一个全球统一的地球模型。
它把地球近似为一个椭球体,长半轴约6378137米,短半轴约6356752米。GPS卫星发下来的位置数据,就是基于这个模型算出来的。
你想想看,如果没有一个统一的坐标系,北京的GPS用一套,上海的用另一套,那导航就乱套了。WGS-84就是那个“通用语言”。
它的核心参数就这几个:
| 参数 | 值 | 说明 |
|---|---|---|
| 长半轴 a | 6378137.0 m | 赤道半径 |
| 短半轴 b | 6356752.3142 m | 极半径 |
| 扁率 f | 1/298.257223563 | 地球的扁平程度 |
| 第一偏心率平方 e² | 0.00669437999014 | 用于投影计算 |
嗯,这里要注意:实际项目中,你不需要每次都手算这些参数。Python的 pyproj 库已经帮你封装好了。但理解背后的原理,能帮你更好地排查问题。
4.3 墨卡托投影:把球面摊平
墨卡托投影,是1569年由墨卡托提出的。它的核心思想很简单:
- 把地球表面“展开”成一个圆柱
- 然后把这个圆柱“摊平”成一张平面地图
这样做的好处是:角度不变。也就是说,地图上两条线的夹角,和实际地球上的夹角是一样的。这对导航来说太重要了。
但代价也很明显:面积会变形。越靠近两极,变形越严重。格陵兰岛看起来比非洲还大,就是墨卡托投影的“杰作”。
不过,对于城市级别的定位应用(比如北京、上海),变形可以忽略不计。所以它依然是GPS定位中最常用的投影方式。
我的经验:如果你只在中国境内做项目,直接用墨卡托投影完全够用。但如果你要做极地导航,就得换别的投影方式了。
4.4 经纬度转平面坐标的数学公式
公式其实不复杂。我直接给你核心的:
假设经纬度为 (lat, lon),单位为弧度。那么墨卡托投影后的平面坐标 (x, y) 为:
x = a * lon
y = a * ln( tan(π/4 + lat/2) * ((1 - e * sin(lat)) / (1 + e * sin(lat)))^(e/2) )
其中:
- a = 6378137.0(长半轴)
- e = 0.0818191908426(第一偏心率)
- ln 是自然对数
- tan 是正切函数
看着有点吓人,对吧?别怕,Python实现就几行代码。
注意:公式中的 lat 和 lon 必须是弧度,不是度。如果你从GPS模块拿到的是度,记得先转成弧度:rad = deg * π / 180。
4.5 Python实现:手写一个墨卡托投影
我个人习惯,先手写一个纯Python版本,不依赖任何第三方库。这样你能看清每一步在干什么。
import math
def wgs84_to_mercator(lat_deg, lon_deg):
"""
将WGS-84经纬度转换为墨卡托平面坐标
:param lat_deg: 纬度(度)
:param lon_deg: 经度(度)
:return: (x, y) 单位:米
"""
# 地球参数
a = 6378137.0 # 长半轴
e = 0.0818191908426 # 第一偏心率
# 转弧度
lat = math.radians(lat_deg)
lon = math.radians(lon_deg)
# 计算x
x = a * lon
# 计算y
sin_lat = math.sin(lat)
y = a * math.log(math.tan(math.pi / 4 + lat / 2) *
((1 - e * sin_lat) / (1 + e * sin_lat)) ** (e / 2))
return x, y
# 示例:天安门广场
lat, lon = 39.9042, 116.4074
x, y = wgs84_to_mercator(lat, lon)
print(f"天安门广场墨卡托坐标: x={x:.2f}m, y={y:.2f}m")
# 输出:天安门广场墨卡托坐标: x=12958179.16m, y=4865942.28m
你看,核心代码就十几行。但这里有个坑:y坐标的计算中,那个指数运算很容易溢出。我曾经在STM32上跑这个算法,因为浮点数精度不够,算出来的y值直接变成了inf。后来改用双精度浮点才解决。
4.6 用pyproj库:更省心
如果你不想手写,直接用 pyproj 库。它封装了各种坐标系转换,包括WGS-84和墨卡托。
import pyproj
# 定义坐标系
wgs84 = pyproj.Proj(init='EPSG:4326') # WGS-84
mercator = pyproj.Proj(init='EPSG:3857') # 墨卡托投影
# 转换
x, y = pyproj.transform(wgs84, mercator, 116.4074, 39.9042)
print(f"pyproj结果: x={x:.2f}m, y={y:.2f}m")
# 输出:pyproj结果: x=12958179.16m, y=4865942.28m
结果和手写版本完全一致。所以,如果你在PC上开发,直接用pyproj。如果你在嵌入式设备上跑,手写版本更轻量。
4.7 避坑指南:我踩过的几个坑
做坐标转换,有几个地方特别容易出问题。我一个个说:
- 经纬度顺序搞反:GPS模块输出的顺序通常是
纬度, 经度,但很多库的输入顺序是经度, 纬度。我至少因为这个bug排查了半小时。 - 单位搞错:有些GPS模块输出的是
度分秒,不是度。记得先转成度。 - 投影原点不一致:墨卡托投影的x坐标原点在赤道和本初子午线的交点。如果你要显示在某个局部地图上,记得做平移。
- 浮点数精度:在嵌入式设备上,float精度不够,建议用double。
我曾经在一个项目中,因为GPS模块输出的经度是 116°24'26" 这种格式,我直接当浮点数用了,结果定位偏了十几公里。后来加了一个度分秒转度的函数才搞定。
4.8 知识体系总览
下面这张图,帮你理清本章的核心逻辑:
这张图把整个流程串起来了:从GPS模块拿到WGS-84经纬度,经过墨卡托投影转换,最终得到以米为单位的平面坐标。实现方式有两种,你可以根据项目场景选择。
4.9 小结
坐标转换是定位模块开发的基础。你只要记住三点:
- GPS输出的是WGS-84经纬度,单位是度
- 墨卡托投影能把经纬度转成平面坐标,单位是米
- Python实现很简单,手写版和pyproj库版都可以用
嗯,这一章就到这里。下一章我们会讲如何用这些平面坐标做距离计算和路径规划。
可可精品资料库