NEO-6M GPS 定位模块
| 器材 | 数量 | 参考 |
|---|---|---|
| NEO-6M GPS 模块(带天线) | 1 | — |
价格随渠道波动,以购买页实时为准。
想做个能记录跑步轨迹的小东西,或者给一台设备装上"现在我在哪"的能力——把它走过的路线画在地图上、让丢了的设备能报出自己的坐标。这件事在室内传感器里找不到答案:温湿度、距离、光照都测的是"身边",而位置测的是"我在地球上的哪一点"。能直接吐出经纬度的,是 GPS 模块,NEO-6M 就是其中最经典、最便宜的入门款。
NEO-6M 是 u-blox 公司一颗 GPS 接收芯片做的模块,巴掌大、带一根方块陶瓷天线,UART 串口接出来。它不需要联网、不需要基站,只要头顶能看到天空,就能持续报出自己的经纬度、海拔、速度,还顺带给你一个精确到秒的世界时间。
工作原理
天上有几十颗 GPS 卫星绕着地球转,每一颗都在不停地往地面广播一条无线电信号,内容是"我是几号星、我现在的精确位置、我发出这条信号的精确时刻"。NEO-6M 做的事,就是同时收下好几颗卫星的广播,然后反推出自己在哪。
关键在"测时间差"。信号从卫星飞到地面要花一点时间(无线电波也是光速),模块拿自己收到信号的时刻,减去卫星发出信号的时刻,就得到信号飞了多久,乘以光速就是"这颗卫星离我多远"。
我到卫星的距离 = 光速 × (收到时刻 - 发出时刻)
测出到一颗卫星的距离,只能确定"我在以这颗星为球心的某个球面上";测到三颗,三个球面相交把范围缩到一个点附近;但模块自己的钟没卫星上的原子钟准,多出一个时间误差要消掉,所以实际需要至少 4 颗卫星才能同时解出经度、纬度、海拔和准确时间这四个未知数。搜到的星越多,解算越稳、越准。
算出来的结果,模块通过 UART 串口持续以文本形式吐出来,格式叫 NMEA——一行行以 $ 开头的语句,比如 $GPGGA(含定位、海拔、卫星数)、$GPRMC(含经纬度、速度、UTC 时间)。我们的单片机只要读这些文本、把里面的数字挑出来就行。UART 串口本身怎么工作,见 /principle/uart/。
接线
NEO-6M 是 UART 设备,核心就是 TX/RX 两根数据线,并且必须交叉接:模块的 TX(它说话)接单片机的 RX(单片机听),模块的 RX 接单片机的 TX。ESP32 用第二组硬件串口 Serial2 来读,刚好空出默认的 Serial 给电脑调试:
| NEO-6M | ESP32 | 说明 |
|---|---|---|
| VCC | 3.3V 或 5V | 模块多自带稳压,两者都行 |
| GND | GND | 公共地 |
| TX | GPIO16 (RX2) | 模块发,单片机收 |
| RX | GPIO17 (TX2) | 模块收,单片机发(只读数据可不接) |
模块的 TX 输出一般是 3.3V 电平,ESP32 直接收没问题;如果你用的是 5V 单片机、又要往模块的 RX 发指令,则模块 RX 这一侧需要降压,原理见 /principle/level-shift/。
最容易被忽视的不是接线,而是天线:那块方形陶瓷天线(或外接的蘑菇头天线)必须朝上对着天空、上方不能被金属或楼板挡住,否则一颗星都搜不到。
完整代码
用 TinyGPS++ 库(Arduino 库管理器搜 "TinyGPSPlus")解析 NMEA,省去自己抠字符串。NEO-6M 默认波特率 9600,从 Serial2 喂给库即可:
#include <TinyGPS++.h>
TinyGPSPlus gps; // GPS 解析器
HardwareSerial GPS(2); // 用 ESP32 的第二组硬件串口 Serial2
void setup() {
Serial.begin(115200); // 调试口,连电脑看结果
GPS.begin(9600, SERIAL_8N1, 16, 17); // 9600bps, RX2=16, TX2=17
Serial.println("等待 GPS 搜星,请到室外开阔处...");
}
void loop() {
// 把串口收到的每个字节喂给解析器
while (GPS.available() > 0) {
gps.encode(GPS.read());
}
// location.isValid() 为真,才表示真的定到位了
if (gps.location.isValid()) {
Serial.print("纬度: ");
Serial.print(gps.location.lat(), 6); // 6 位小数,约 0.1m 分辨
Serial.print(" 经度: ");
Serial.print(gps.location.lng(), 6);
Serial.print(" 海拔: ");
Serial.print(gps.altitude.meters());
Serial.print("m 卫星数: ");
Serial.println(gps.satellites.value());
// GPS 还能白嫖到精确 UTC 时间,无需联网
if (gps.time.isValid()) {
Serial.printf("UTC 时间: %02d:%02d:%02d\n",
gps.time.hour(), gps.time.minute(), gps.time.second());
}
} else {
// 还没定到位:报一下已经收到多少颗星的信号
Serial.print("搜星中... 已收到卫星: ");
Serial.println(gps.satellites.value());
}
delay(1000);
}
gps.encode() 逐字节把 NMEA 文本喂进去,库内部攒够一整句就解析好。读数时务必先判 gps.location.isValid()——没定到位时这些字段是空的,直接用会拿到 0 或上一次的旧值。
你应该看到什么
把板子带到室外开阔的地方(阳台、空地,头顶别有遮挡),上电后打开串口监视器(115200 波特率)。冷启动头一两分钟,你会看到模块在慢慢攒星:
等待 GPS 搜星,请到室外开阔处...
搜星中... 已收到卫星: 0
搜星中... 已收到卫星: 3 ← 收到信号了,但还不够 4 颗
搜星中... 已收到卫星: 4
纬度: 39.908600 经度: 116.397400 海拔: 52.30m 卫星数: 6
UTC 时间: 03:14:27 ← 顺手拿到的精确世界时间
纬度: 39.908612 经度: 116.397389 海拔: 52.10m 卫星数: 7
卫星数从 0 慢慢往上爬、爬过 4 之后开始稳定打印经纬度,这就对了。把它拿到室内几乎只会看到 卫星数: 0 一直刷——这不是模块坏了,是 GPS 信号穿不过楼板,室内本来就基本搜不到星。模块板上通常有颗 PPS 指示灯,没定位时不亮或常亮,定到位后会变成每秒闪一下,可以用它快速判断状态。
读数解读
- NMEA 语句:模块吐出的原始文本,
$GPGGA那句里有定位质量、卫星数、海拔,$GPRMC那句里有经纬度、速度、UTC 时间,TinyGPS++ 帮你把数字挑出来,平时不用自己看原文。 - 定位有效标志(
location.isValid()):最该看的一个值。为真才代表当前经纬度是真定位结果,为假说明还在搜星,字段不可信。 - 卫星数(
satellites.value()):当前参与定位的卫星颗数。少于 4 颗无法定位;6~10 颗算正常,越多越稳。 - 定位精度:消费级 GPS 民用定位精度大约 2~5m,也就是报出来的点和真实位置差几米,画轨迹够用,但别指望它分清你站在马路哪一条车道。经纬度小数留 6 位(约 0.1m 分辨)就足够承载这个精度。
选型 / 避坑
NEO-6M 解决的是"户外、全球范围、绝对经纬度"。如果你的需求是室内的相对定位(在房间里测到墙、到桌子的距离),GPS 完全不适用——室内收不到卫星信号,那是测距类传感器(如激光、超声波)的活。
必须到室外开阔处搜星,室内基本无效:GPS 信号很弱,穿不透楼板和厚墙。第一次跑代码请直接到阳台或空地,否则你会以为模块坏了,其实只是收不到星。
冷启动要等几分钟:模块第一次定位(或长时间断电后),要先从零搜索卫星、下载星历,常需等 30 秒到几分钟才出第一个坐标,期间卫星数一直为 0 是正常的,耐心等。之后短时间再开机会快很多(热启动)。
天线一定要朝天、别被金属挡:陶瓷天线的方向直接决定能不能收到星,把它平放朝上、上方留空。塞在金属外壳里、压在书底下都会让它搜不到。
故障排查
| 现象 | 可能原因 | 排查 |
|---|---|---|
| 卫星数一直 0、从不定位 | 在室内 / 天线被挡 / 冷启动还没到时间 | 拿到室外开阔处,天线朝天,耐心等几分钟 |
| 串口全是乱码 | 波特率不对 | NEO-6M 默认 9600,确认 GPS.begin(9600, ...) |
| 串口完全没数据 | TX/RX 接反 或 没共地 | 模块 TX 接 ESP32 RX2(16)、交叉接,确认 GND 相连 |
| PPS 灯不闪、读数全 0 | 没定到位 | 同"卫星数一直 0",先确认在室外 |
| 经纬度跳来跳去几十米 | 卫星数偏少 / 周围有高楼遮挡反射 | 换更开阔的位置,等卫星数升上去再读 |
进阶 / 变体
做成轨迹记录器:把每秒拿到的经纬度配上时间戳,连续写进 SD 卡的一个文本文件,跑完一趟把文件导出,用地图工具就能把整条路线画出来——这就是最朴素的运动轨迹记录器,不依赖手机和网络。
当精确授时源用:GPS 信号里自带卫星原子钟同步过的 UTC 时间,精度远超普通 RTC,而且不需要联网。设备装在没有 WiFi 的野外,又要精确时间时,可以直接拿 GPS 的时间来对表,作为联网 NTP 之外的另一条授时途径。
升级模块:NEO-6M 只收 GPS。若要更快定位、更高精度,可换支持多卫星系统(GPS + 北斗 + GLONASS)的新款模块,用法和接线基本一致,库不变。
想把"位置读数 → 自动决策/记录"做成完整项目并用 AI 辅助开发,参考 /guide/l4-sensor-ai/。
典型应用
- GPS 轨迹记录器(跑步、骑行、自驾路线,配 SD 卡存点)
- 宠物 / 车辆 / 设备定位追踪(报出自己的坐标,找回丢失物)
- 户外精确授时(无网环境拿 GPS 的 UTC 时间对表)
- 无人机 / 小车户外定位与返航
- 地理围栏(判断设备是否走出/进入指定区域并报警)
小结 · 相关
NEO-6M 的核心价值是"给项目装上卫星定位,直接输出经纬度、海拔、速度和时间"。它同时收下至少 4 颗 GPS 卫星的广播,用信号飞行时间差解算出自己的位置,再通过 UART 串口持续吐出 NMEA 文本。接线就是 TX/RX 交叉接到 ESP32 的 Serial2、默认 9600 波特率,天线朝天;代码用 TinyGPS++ 解析、先判 isValid() 再读经纬度十几行搞定。要做户外、全球范围的绝对定位与轨迹记录,它是经典首选;只要记住第一条铁律——到室外开阔处、耐心等冷启动搜星,室内搜不到星。
相关阅读:/principle/uart/(串口怎么读数据)、/principle/level-shift/(5V 模块的电平转换)、/guide/l2-oled/(把经纬度显示到小屏上)、/guide/l4-sensor-ai/(读数到决策的完整项目),更多器件见 /sensor/ 与 /principle/、/guide/。
参数以 datasheet 为准;本页公开资料整理,接线与代码请结合实物验证。